Skip to content

Instantly share code, notes, and snippets.

@farteryhr
Created April 5, 2013 12:54
Show Gist options
  • Save farteryhr/5319066 to your computer and use it in GitHub Desktop.
Save farteryhr/5319066 to your computer and use it in GitHub Desktop.
MIDI以及mid文件的字节格式……查了一圈资料各种机翻……要么就是各种文艺一点没有程序猿风范【滚】所以写个笔记,你一定看不懂的【大雾】
定义 范围(左端点,右端点)
左闭右开区间你懂的
定义 字节
……8位二进制数码……
定义 大端整数
就是由几字节组成的整数,但是首个字节代表的是最大的8位。
定义 变长MIDI整数
MIDI的变长整数记录法,用低7位记录数字,最高位为1表示再读一字节拼起来。
也就是,0-128直接单字节一坨,大于128切成每坨7位,也是按大坨在前,然后除了最后一坨之外,每一字节最高位改成1。
定义 MIDI整数
据说长度一般是一定的,对于一字节值域为0-128,对于2字节定义不太明确貌似是14位怎么的……
定义 音高(1字节MIDI整数)
MIDI标准音高表示法,十进制60表示C4,偏移1等于偏移1半音。
定义 力度(1字节MIDI整数)
开启音符时,作为击发力度(音量)。但是语义上是速度……
语音合成上速度跟力度差远了……姑且当作砸钢琴的速度吧……
因为只表示这一发的力度,区别于其他持续发声的乐器(吹的拉的)的音量可以在中途变化。
关闭音符时,作为释放速度,127最快。不过我目前所见midi似乎都是瞬间释放。
定义子过程 取得通道音高力度(指令和通道)
取<指令和通道>低4位 → 通道号
读1字节MIDI整数 → 音高
读1字节MIDI整数 → 力度
定义子过程 取得事件
读 变长MIDI整数 → 本事件前延迟Tick数
读1字节整数
取高4位
如果高4位是0x8 //关闭/释放音符
取得通道音高力度(当前字节)
如果高4位是0xh //打开/按下音符
取得通道音高力度(当前字节)
如果高4位是0xa //轮指 神之名词。。据说意思就是让已经按下的音符不释放直接再来一发……
取得通道音高力度(当前字节)
如果高4位是0xb //改变控制器
取低4位 → 通道号
读1字节MIDI整数 → 控制器号 //要改变的控制器,控制器很多…… 貌似有些控制器期的意义跟这里有重复……
//参见http://wenku.baidu.com/view/2a4e0e64783e0912a2162a62.html 或者搜索MIDI控制器列表
读1字节MIDI整数 → 新值
如果高4位是0xc //改变乐器。控制器里面貌似也有类似定义的东西……
取低4位 → 通道号
读1字节MIDI整数 → 新的乐器号
如果高4位是0xd //后触/演奏压力/音量 ……准确定义至今不明
//控制器里面有情绪控制(估计是给唱的)、呼吸控制(估计是给吹的拉的)、音量控制(纯粹的音量)
取低4位 → 通道号
读1字节MIDI整数 → 新的音量值
如果高4位是0xe //音高微调/滑音,用2字节表示
//据说就是按照MIDI整数规则组合起来,一共14位,合起来是0x0-0x4000。首个字节需不需要高位填1貌似没说。
//然后采用偏移法而不是二补码,0x2000表示音高不变。但是变化量Pitch Bend Sensitivity具体不清楚。
取2字节 → 新音高
如果高4位是0xf //系统的特殊信息,低4位不再是通道号了
如果低4位是0x0 //系统码,可以包任意数据,交给目标设备解释……
取变长MIDI整数 → 消息长度
取<消息长度>字节 → 消息
//消息最后一字节一定是0xf7,似乎是当成字符串的null-terminator。
//实际上消息中间也可以有0xf7出现,因为规定了读取长度。是潜规则么…
如果低4位是0xf //0xff,Meta-events,传说中的更加特殊事件……
读1字节MIDI整数 → 消息类型
读变长MIDI整数 → 消息长度
读<消息长度>字节 → 消息
取消息类型
如果高4位是0
取低4位
如果是0x0 //只应发生在音轨头部(0时刻)
消息内容(大端整数) → 轨道序号 //据说内容长度只应是2字节。注意起这里貌似都[不]是MIDI整数了。
//低4位是0x1开始的都是文本信息,只是作用不同。
如果是0x1
消息内容 → 通用文本事件
如果是0x2 //只应在音轨头部
消息内容 → 版权信息
如果是0x3 //只应在头部,在多轨中的全局音轨表示歌曲标题,其他音轨中表示音轨标题。
消息内容 → 歌曲标题/音轨标题
如果是0x4
消息内容 → 乐器相关信息
如果是0x5
消息内容 → 歌词
如果是0x6
消息内容 → 标记 //已经搞不懂要这么多种消息拿来干啥了……
如果是0x7
消息内容 → 注释点 //描述舞台动作用的事件……你赢了……
如果是0x2f //音轨结束事件,一定会在每个音轨最后出现一个。包括在音轨长度中了,类似于系统消息的谜之0xf7。
//<消息长度>一定是0x00
退出子过程
如果是0x51 //改变节奏速度
//据说<消息长度>一定是3
消息(<消息长度>字节大端整数) → 四分音符时长 //微秒数!
如果是0x54 //至在百度百科的那篇机翻文里面有提到,意义不明,看样子很少有使用。
//据说<消息长度>一定是5,只应出现在音轨0时刻。
//摘录百度百科
/*
SMPTE 时间同步
FF 54 05 hr mn se fr ff
这一事件,如果存在的话,将指定某一个特定事件开始的SMPTE时间。
它应出现在音轨的开头,在任何非零时间后发生的事件或可传送的MIDI信息之前。
*/
如果是0x58 //改变拍号。应该只能出现在整小节处吧。
//据说<消息长度>一定是4,不知道这里每个数都是MIDI整数还是单字节整数。
取消息第0字节 → 拍号分子
取消息第1字节 → 拍号分母的2的幂次 //0x02→(?/4拍) 0x03→(?/8拍) 以此类推
取消息第2字节 → 四分音符对应Tick数 //这里我查的几个例子给出来的都比较模糊,待考。
取消息第3字节 → 四分音符对应32分音符数 //同待考,略囧,不该永远是8么……例子上各种写十六进制0x24就是32……跪烂
如果是0x59 //改变调号/谱号
//据说<消息长度>一定是2
取消息第0字节 → 升降号数目 //正为升,负为降 又开始用二补码了么……
取消息第1字节 → 大/小调 //0→大调 1→小调
如果是0x7f //真·特殊要求……
消息 → 传说中的真·特殊要求内容
定义 文件结构
循环直至读完
读4字节 → 块头
如果是"MThd" //文件头
读4字节大端整数 → 文件头大小
读<文件头大小>字节
取2字节大端整数 → MIDI类型
//0:一条轨 1:多条轨,同时播放 2:多条轨非同时播放(据说可能是接着播放也可能有各种位置),貌似没人用
取2字节大端整数 → 轨道数
取2字节大端整数 → 每拍Tick数 //相当于分辨率,就是最小时间单位。拍被默认为四分音符。
如果是"MTrk" //轨道
读4字节大端整数 → 该轨道大小
读<该轨道大小>字节
取得事件,直到取得的事件是“音轨结束” //此时应该刚好取完
@YeDaxia
Copy link

YeDaxia commented Apr 4, 2018

赞一个!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment