基本概念与构成
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 (这样判别何处结束)