MIDI协议解析以及C++实现-以Unravel.mid为例
最近为了做一个小玩意需要解析MIDI协议,但在单片机上用网上给电脑程序写的库总感觉会有不可预知的延迟和性能消耗,所以决定自己写个MIDI的解析,为此学习了很多东西,特此写下专栏留念
声明一下,像二进制、十六进制、ASCII码,什么是字节的这些东西我默认读者已经接触过了,所以我后面将直接对文件内容进行解析,没了解过的可以根据这几个关键词先去搜索了解下
本文以我从一个群里下的A叔改编的unravel.mid为例
下载链接:https://wws.lanzouq.com/ic7Z6024y0ta
其他mid文件暂时没看过,可能不太通用,如有错误欢迎在评论区指出

正式开始解析
首先我们以16进制打开unravel.mid文件,内容如图
从右边可以看到一些能够直接转为字符的信息,如MThd、MTrk则为一块区域的头

接下来便可以划分区域:
文件开头就是4D54 6864(MThd)直到后面出现的第一个4D54 726B(MTrk)前为一块区域
存储了这个MIDI文件的基本信息
第一个4D54 726B(MTrk)直到后面出现的第一个FF 2F为一块区域
存储了如曲目名称、四分音符的绝对长度等信息
第二个4D54 726B(MTrk)直到后面出现的第一个FF 2F为一块区域
存储了一个轨道的所有MIDI事件信息
若无多个轨道则文件结束
所以MIDI主要可以分成以上三块或者说三种区域
第一块区域:
进入MThd后的4个字节便是后面数据的长度信息
这里内容为6,所以接下来的6字节便是它的数据,内容解释在上面注释好了,想了解其他内容或者更加具体的解释可以看文末参考文献的第一个文档
这里主要提取03C0,用来计算延时时间,后面会提到
第二块区域:
MTrk里面则是的具体的MIDI事件,按顺序由【延时时间(Delta-time)、事件类型、参数1、参数2、......、参数n】组成,其中Delta-time为一个或多个字节(这次的文件最高只有两个字节),后面的参数个数由事件类型决定
第一个MTrk主要都是些关于这个曲目的信息,所以事件基本由都是FF组成,前面的延时在这没有意义就放到后面再提了,在程序中可以直接找FF来提取信息
FF的参数一为信息类型、参数二为信息长度、再后面就是信息内容
从这个MTrk中主要可以提取到以下的信息
FF 01 其他信息
FF 02 版权信息
FF 03 曲目或音轨名称
FF 51 一个四分音符的绝对时间长度,用其除以MThd中的基本时间可以得到“一个Delta-time的延时时间”
FF 2F 结束
第三块区域:
到这里就是主要的演奏信息了,主要事件就是0x90(按下)和0x80(松开)
在程序中如果只是钢琴演奏的话其他事件不去执行也不会有太大问题,只需要注意CX、DX和FF的参数数量
延时:
上面的注释里写好了在程序中如何判断有几个字节需要读取
读取了之后,将高位减去0x80然后组合在一起便是Delta-time
然后乘上前面第二块区域算好的“一个Delta-time的延时时间”便知道需要延时多少微秒
演奏事件:
0x90为按下,0x80为松开,他们后面跟着两个参数分别是音符和力度
如此循环直到FF 2F就结束了
这样整个MIDI协议大体的内容就解析完了,下面放我写的验证程序,如果访问不了Github就直接用网盘链接和使用文档即可
Github链接:https://github.com/AndyXFuture/CppMidi_Analysis-Play
网盘链接:https://wws.lanzouq.com/iq15e024vs3e
使用文档:https://www.bilibili.com/read/cv15852954

参考文献:
MIDI文件格式解析
https://www.jianshu.com/p/59d74800b43b
MIDI tick与绝对时间的换算
https://wenku.baidu.com/view/9b6871f7f90f76c661371ac4.html
(百度文库的免费文档,真的是一股清流啊)