MIDI and Lyrics Data Preprocess
将netease music中含有时间轴的歌词全部转移到统一的文件夹下
由于并不是所有的歌词信息都含有时间轴,因此我们需要将含有时间轴的歌词先抽取出来。
基于如下观察,我们可以发现,含有时间轴信息的歌词文件的开头如下:
1 | |
而不含有时间轴的歌词文件为如下格式:
1 | |
当然,还有一些歌词文件为空:
1 | |
所以我们可以将"[0作为条件判断该歌词文件是否含有时间轴信息。
过滤的python script如下:
get_lyrcis_by_id.py
1 | |
之后一共得到了19000多条带有时间轴的数据(一共40000多条数据,netease music上有歌词的就24000条,再滤掉没有时间轴的就只剩19000条了,太难啦,之后还得试试spotify,把spotify的脚本完善一下了跑起来)
将MIDI文件与lyrics文件做对齐
接下来使用pretty_midi库尝试将MIDI文件按照lyrics中的时间轴进行切片,从而得到对齐的MIDI音符序列与歌词文本序列对。
从lyrics的json文件中抽取歌词与对应的时间戳
此处我是将原本的json文件修改为如下的格式:
1 | |
由于tuple以及list是unhashable的,无法作为python中dict的key,因此将其改为"start_time--end_time"的字符串格式
1 | |
以下是代码实现:
1 | |
根据lyrics中的时间信息对midi文件进行分片
设计数据结构如下图所示:
1 | |
每一个片段为一个dict,包含两组kv对,第一组是歌词,key为字符串“lyrics”, value即是歌词的字符串。第二组是notes,他是一个list,每一个元素也是一个lIst,其中第一个元素是乐器名,第二个元素是乐器名对应的编号(该编号由pretty_midi提取得到,样例中的数字是随便写的,不代表真实编号),从第三个元素开始都是一个list,对应一个note,其中四元组分别是音高,响度,开始时间,持续时间。
之所以这样设计数据结构,是因为每一个片段的midi文件中,可能有不止一个乐器,而每个乐器对应一组音符,这里的想法是将这些信息全部抽取出来,之后每一个乐器作为输入数据中的一个channel输入给模型(具体设计暂定)
在代码实现中,将切好的midi片段也一并保存下来,即既保存片段的midi文件,亦保存其json文件。
对一段时间内的midi进行切片
1 | |
此处没有使用pretty_midi中的adjust直接对原midi文件进行时间调整从而切片,而是以音符为单位,搜索所有在这个时间段内部的音符,并按照不同的乐器分别存储起来,至于为什么要用str将音符的音高,响度,起止时间进行转换,这是为了最后的数据能够用json文件存储。
对整个midi文件进行切片
由于我们之前已经爬取好了带时间轴的歌词,并按照上述所说的json文件的格式进行组织,所以直接根据歌词中的时序信息,对相应的整个midi文件进行依次调取cut_one_piece即可(当然,此处的实现方式可以优化,只需要遍历一遍整个midi文件即可完成对所有时间序列的切片,但是懒得重写代码了,暂时就这样吧)
1 | |
之后还剩余一些文件遍历的代码,此处就不再贴出,与主题无关。
至此,完成了对midi与lyrics的对齐处理。以下是处理后得到的json文件的效果:
1 | |