【python脚本系列】利用mido库解析midi文件
Hallo大家好~~我是Lampard猿奋
昨天写了关于Midi数字化乐器接口的文章,介绍了midi是一组代表音乐参数(如音高,动态,节奏等等 )的标准化代码,是音乐与计算机之间交互的语言。那么这个语言究竟是怎么组成,是什么数据结构呢,下面就借助python的mido库和大家一起打开这个黑盒
channel是指定的0~15的一个值,因为MIDI文件给我们提供了默认的16个通道,每一个通道可以对应一种乐器,相当于midi支持一首歌曲里同时使用16种乐器进行演奏
当然每一个通道对应的乐器并不是固定的,我们可以代码Message('program_change', channel, program, time=0)改变其对应关系,也就是说虽然同时支持16种乐器,但16种乐器不需要是固定的,上述接口中program代表着乐器Id,更多乐器Id相关可以跳转至此:【Midi音色表】
音轨,音轨与通道并不是一一对应,而是可以多对多的关系。音轨是逻辑上的划分,比如可以将钢琴的左手演奏放在track 1,右手演奏放在track 2。但是输出时候,都是对应输出到钢琴的通道。你也可以只设置一个track 1,并且在里面记录着不同通道的消息。另外,还经常将track 0用来存储midi的作者、版权、音轨名、乐器名等元信息
事件也叫做消息(在mido库中使用message表示)。包括三种事件meta event元消息,midi event主消息, sysex event系统消息
元消息上文有提及是作者,版权,音乐名等信息的存在track0中,系统消息是存储midi系统不同版本的信息,主要需要留意的就是midi消息
midi有多种信息类型,我们可以参考mido文档去查阅:【mido库文档】,而我们主要需要关注两个信息note_on和note_off
note_on消息,可以理解为音符的开始(按下),其格式为Message('note_on', note, velocity, time, channel)
其中note是0~127的一个数字,代表音符的高低,通过实践证明60代表的音高是C4
velocity代表音强,也是0~127的一个数字,默认为64,若要体现音符强度的变化可以修改它
time是时间变量,是十分复杂的一个参数,在note_on信息这里可以理解为该音符写在前一个音符结束多久之后,单位是微秒(ms)
channel同上一个函数一样,代表通道的编号,即将这个音符写到哪个通道之上,这可能起到更改乐器的效果
note_off消息,可以理解为音符的结束(抬起),一般紧跟在note_on消息之后,其格式与上面的相同Message('note_off', note, velocity, time, channel)
note参数与note_on消息保持一致,否则有可能不能成功写入
velocity同note_on保持一致就好
time在此处表示的意义是音符的持续时间,也是以微秒(ms)为单位
channel也是表示通道号,与note_on保持相同即可
看了那么多字感觉头都大了,好像也不太理解说的是什么。那下面还是直观一些用Mido库给我们提供的案例来分析一下Midi文件的结构吧【mido库示例】
这段示例代码虽然短,可是已经将编写MIDI文件的基本思路完全表达出来了:
1. 首先创建一个MidiFile对象
2.创建一个(或多个)MidiTrack对象,并将其append到MidiFile中
3.向一个(或多个)MidiTrack对象内添加Message对象(包括program_change(变更通道对应乐器)、note_on(按下)、note_off(抬起)等)
4.保存MidiFile对象
我把这段代码命名为makeMidi.py,执行一遍就能获取到我们生成的new_song.midi文件
然后就是读取这个midi,mido库同样给出了示例,逻辑很简单就是打印出这个midi文件每一个音轨track的信息
最后我们看一下我们打出的日志,我把解析的脚本命名为readMidi.py,执行后可以看到我们刚才输入的信息,在音轨0的32ms后执行一次的点击动作,对应的音色是64
如果我们不想打印出全部信息,指向关注不同track之间的note_on和note_off的信息,我们可以msg.type == “note_on/note_off”这样来判别
当我们能够解析出midi的信息之后就可以和音效制作乐谱的同学沟通啦。距离实现音游玩法也跟进了一步,待玩法进度推进后我再继续更新~~
感谢阅读,点赞,关注!!!