基本概念与构成
MIDI 是一种音频格式, 它用来描述声音在什么时间以什么音色多高的音调被发出或终止.一个 MIDI 文件中通常包含多个音轨 (track), MIDI 文件可以配置多个音轨以并行方式播放还是串行方式播放, 一般应用上多个音轨当然是同时播放比较好.
为什么要有多个音轨呢? 这个... 写代码的时候一般也不会把所有代码塞在一个函数里面吧.
而一个音轨就是一系列事件构成的. 事件包括了演奏或停止直接与发声有关的, 以及换乐器这样的控制指令.
文件内容组成
MIDI 文件全景可以用下面的产生式来描述.MIDI =>
文件头部信息 音轨集
# 至少得有一个音轨
音轨集 =>
音轨 音轨集
|
音轨
文件头部信息详情
文件头部信息 =>
"MThd" 0x00000006 音轨类型 音轨数 节拍描述
接下来 0x00000006 是一个四字节类似大端码表示的整数 (是 0x00 0x00 0x00 0x06 而不是 0x06 0x00 0x00 0x00), 它的含义是文件头部剩下多少个字节. 本来这东西设计是可变的, 不过实际上后面 "音轨类型" "音轨数" "节拍描述" 每个都是固定的 2 字节, 因此这里直接填 6 就行.
音轨类型有三种取值
- 0 : 只有一个音轨
- 1 : 多个音轨, 同时播放
- 2 : 多个音轨, 串行播放
音轨数也是一个 2 字节整数, 表示后面实际含有的音轨数量.
节拍描述是一个 2 字节整数, 表示一个四分音符 tick 数. (本人没学过乐理, 这信息是照搬过来的)
音轨结构详情
音轨 =>
"MTrk" 音轨内容长度 事件集 0x00ff2f00
音轨内容长度是 4 字节整数, 大端表示. 这个整数指示音轨中事件集的字节数加上末尾的 0x00ff2f00 填充物这 4 个字节.
事件集与事件结构
事件集 =>
事件 事件集
|
ε
事件 => 相对时间 事件类型 事件参数
比较麻烦的是, 这个时间并不是固定字节长度的, 它的构成大概如下:
- 如果这个数在 0~127 之间, 则用 1 字节表示, 否则
- 最后一个字节存放这个数对 128 的模, 再将这个数地板除 128 得到的商以递归的方式在前面 (较低位置) 表示
- 除了最后一个字节, 其它字节最高为均为 1 (这样判别何处结束)
时间数据结束之后, 后面便是 1 字节事件类型数据. 它的高 4 位表示指令类型
- 8 : 发声停止
- 9 : 发声开始
- a : 触后音符
- b : 控制器转换
- c : 音色 (乐器) 转换
- d : 触后通道
- e : 音高设置
其低 4 位是通道号 (至多 16 个通道). 在 MIDI 输出时, 一个通道同时只能产生一种音色的声音, 切换音色后, 该通道发声就会持续地改变.
指令决定了其后事件参数的数据长度, 比如发声开始 (0x90) 后面跟 2 字节数据, 第一字节表示音调, 第二字节表示弹奏力度 (貌似音量); 而乐器设置 (0xe0) 后只需 1 字节 (0~127), 表示转换的乐器种类编号.
实例
纸上谈兵无益, 先来个具体的例子. 由于 MIDI 是二进制文件, 产生它需要搞点小技巧. Shell 中的 xxd 是个不错的选择, 它能将输入的十六进制数变成二进制数据, 如$ echo -n "61 62 63 64" | xxd -r -p
$ cat | xxd -r -p > output.midi
Place MIDI data here
$ xxd -r -p input-file > output.midi
下面是一个单音轨 MIDI 例子, 音轨上只有一个音符
4d546864 00000006 0001 0001 0060
4d54726b 0000000d
05 90 6040
8148 80 6040
00ff2f00
接下来一行则是第一音轨头, 以及音轨数据长度 (共 13 字节);
05 90... 这行是一次发声, 从 05 tick 开始;
8148 80... 这行是发声结束, 8148 即上面演算过, 用来表示 200 tick 的时间值;
最后 00ff2f00 是音轨结束.
(在 Linux 播放 MIDI 内容看这里)
接下来尝试在音轨中更换乐器, 然后再次发声
4d546864 00000006 0001 0001 0060
4d54726b 00000019
05 90 6040
8148 80 6040
00 c0 40
05 90 6040
8148 80 6040
00ff2f00
下面是一个二音轨的例子, 其中第一个音轨仍以钢琴声演奏, 而第二音轨则是以萨克斯风演奏 (通过 0x1 通道)
4d546864 00000006 0001 0002 0060
4d54726b 0000000d
05 90 6040
8148 80 6040
00ff2f00
4d54726b 00000010
00 c1 40
70 91 6040
8148 81 6040
00ff2f00