本文主要讲解MIDI前五章主要介绍文件分析MIDI歌曲规范,并引用大量的实例加深理解,学会查看MIDI歌曲的构成使读者能够写出如何MIDI对格式歌曲有初步了解。6章描述MIDI中速度的规范,以及实际歌曲如何描述歌曲的速度,并列出它们之间的转换。7章节将具体分析几首MIDI歌曲,并阐述如何快速分析标准化和标准化的歌曲;然后举出不规范的例子,最后单独分析格式0具体分析格式0和格式1相互转化。使用标准化的标准化描述MIDI歌曲具有现实意义,可以快速掌握歌曲的组成,但现实中很多人简化了标准,加剧了可读性的难度,导致了各种歌曲格式转换的难度。最后附上几首歌MIDI歌曲的原文供读者理解和查询。
第一章MIDI结构和书写格式
MIDI格式歌曲自诞生以来,因以下原因迅速扩展和广泛应用:
①MIDI文件结构在在网络传输中,通常采用7位数据传输方式(不考虑字节的最高,大大提高了传输速度;
②MIDI歌曲体积小,易于传输;
缺点是:因为MIDI文件的结构相当于一个文本文档,记录了如何进行音乐(相当于歌曲的乐谱),所以它的回放音色效果完全取决于MIDI乐器硬件设施。
MIDI文件由大量的数据块组成,可以从附件组成1观察到一行也就是一块,共有四个字节,32位长度,具体构成如表1所示:
类型 |
长度 |
数据 |
4个字节 |
4个字节 |
4个字节 |
表1
其中类型是ASCII码"MThd"或"MTrk",除类型和长度外,其他数据还占字节数和表(1)的块结构是MIDI与下表不同的是,文件以12个字节一行存储MIDI文件结构:
头块 (Header Chunk) |
头块标记 (MThdchunktype) |
头块数据长度6 <lengthofheaderdata> |
6字节的数据 <headerdata> |
音轨块1 |
块标记(MTrk) (MTrkchunktype) |
音轨数据长度 <lengthoftrackdata> |
音轨数据(一连串按时间顺序排列的事件)<trackdata> |
…… |
…… |
…… |
…… |
音轨块n |
块标记(MTrk) (MTrkchunktype) |
音轨数据长度 <lengthoftrackdata> |
音轨数据 <trackdata> |
表2MIDI文件结构
其中<trackdata>=<MTrkevent> <MTrkevent>…..=<delta-time><event>
<音轨数据>=<MTrk事件> <MTrk事件>…..=<时间差><事件>
其中<event>=<MIDIevent>|<sysexevent>|<meta-event> <事件>=<MIDI事件>|<系统码事件>|<元-事件>
表(2)定义了MIDI两种类型的文件:文件头块和音轨块,MIDI文件通常从文件头块开始,然后是一个或多个音轨块。
文件头块描述了整个MIDI文件信息;
音轨块由歌曲名称、音轨名称、序列、音轨结构、拍子、调号、乐器、音符等一系列事件组成。音轨数据块是实际存储歌曲数据的地方,本质上是一系列MIDI事件和非MIDI事件,并且每个事件前面都带有时间差(delta-tme)。
MIDI文件使用单条或多条音轨格式记录事件,只要时间差设置的合适,不同格式间还可以相互转化。
第二章 MIDI文件头块
本章主要讲述MIDI文件的头块的具体构成,包含的信息:头块类型、头块长度和数据部分。头块长度是可变常量,每个字节的最高位为标志位;数据部分包括:格式、音轨数、分区,总共有3个16bit字节,需要先存储高位。以下是头块的语法和格式(16进制):
<头块类型> <头块长度> <格式> <音轨数> <分区>
<Header chunk type> <length> <format> <ntrks> <division>
<4d 54 68 64 ><00 00 00 06>< ff ff>< nn nn> < dd dd>
前4个字节是头块类型,表示“MThd”的ASCII码,每个MIDI文件都以这四个字符为开头;
“00 00 00 06”是头块长度,表明文件头块描述信息的字节数,对于目前的MIDI标准这个值是固定的,只能是6;
“ff ff”定义了整个文件的组织结构,表明MIDI文件的格式,一共有三种情况;
“nn nn”是音轨块的块的个数;“dd dd”是一个四分音符的tick数。
例1:4D 54 68 64 00 00 00 06 00 01 00 0A 01 E0
00 01同步多音轨
00 0A文件中总共有10个音轨块,其中包括1块总音轨,9块分音轨
01 E0 分区,是16进制,最高位不是标志位,换算为10进制是480=2^8+2^7+2^6+2^5的
例子来自附件1
2.1 格式
MIDI文件有三种格式,在MIDI文件中存储的形式是16进制,具体作用如下表:
ff ff |
格式 |
指定midi的格式 |
00 00 |
格式0 |
单音轨:头块的后面只有一个音轨块 。 |
00 01 |
格式1 |
多音轨,且同步:头块的后面有2个或者以上的音轨块,所有音轨块垂直同步,也就是说所有的音轨块开始演奏的时间相同;或者是其他的措辞都在同一时间开始,并且可以表现一首歌的不同部分。 |
00 02 |
格式2 |
多音轨,但不同步:头块的后面有多个音轨块,所有音轨块不同步,音轨块播放时间指令由开始时间决定,这种格式的MIDI文件有很大的复杂度,所以这种格式比较少见。 |
表
格式0,是一个多通道音轨块,存储了不同的音轨块的信息。从附件3里可以看到事件前需要添加通道状态标志,以便播放歌曲时能区别出来事件发生在那一通道,这是最简单的格式,但也使文件体积增大。
格式1,是一个垂直一维表,不同通道的信息垂直分布。这种格式最为常用。
格式2,是一个水平一维表,支持多个独立模式的程序要能够保存和读取格式2的数据。
MIDI文件格式不同,内部的复杂度会有较大差别,三种格式的复杂度是递增的,而且前一种格式总可以近似的看作后一种格式的特例,所以只要面向最复杂的类型2做好各种处理,相对简单的类型0和1只要稍作修改即可支持,也即不同格式间可以互相转换。
2.2 音轨数
“nn nn”表明轨道块数,等于实际音轨块数加上一个全局的音轨块。
在解析MIDI文件时,通常根据音轨块数,就可以判断MIDI歌曲总共含有多少个音轨块,特别是对于格式1、2,这对程序的编写是非常有利的。
注意:自己编写MIDI文件的歌曲时,多加一个音轨,就要更改”nn nn”的数目。
2.3 分区
“dd dd”也即分区,是把一个四分音符长度的时间平均分成DTT“delta-time ticks”(DTT微小时间事件片)份,DTT是MIDI文件中的时间计量单位,一个DTT比1秒短的多。在MIDI文件中是以16进制存储的,是可变常量,但是高位字节的最高位设置为0。具体作用如下表:
dd dd |
指定基本时间格式类型 |
类型1:定义一个四分音符的tick数,tick是MIDI中的最小时间单位 类型2:定义每秒中SMTPE帧的数量及每个SMTPE帧的tick |
在MIDI文件中,通常设置分区为120,但MIDI文件也支持更改这个值。由于这种情况在实际应用中比较少见,因此可以通过统一转换为120的方式,在不影响正确性的前提条件下,降低解析的复杂度。本文的附件中的分区都是变化的,不是120。
如果division是负数,则表示delta-time的一秒钟的分割量,因此音轨事件发生可以用精确时间代替韵律时间来表示。表示如下:第一字节是-24,-25,-29,-30四个值之一,相对应4种标准的SMPTE和MIDI事件码,和表示每秒的帧数。第二字节(保存正数)是帧的解析度:通常值可能是4(MIDI时间码解析度), 8, 10, 80 (比特解析度)或100。 系统允许定义额外的基于时间码的音轨,也允许25帧每秒和40个单位每帧解析度的基于毫秒的音轨。) 例2:01 E0
可以写成0000 0001 1110 0000所以换算为10进制是480=2^8+2^7+2^6+2^5
第三章 音轨块结构
在第一章中已经知道音轨块的语法:
<Track data> = <MTrk event>+<MTrk event>…… = <delta-time> <event>
<音轨数据>= <MTrk 事件>+<MTrk 事件>…… = <时间差> <事件>
<event> = <MIDI event> | <sysex event> | <meta-event> <事件> = <MIDI 事件> | <系统码事件> | <元数据-事件>
MIDI文件的音轨块包括:全局音轨和分音轨。
首先是全局音轨块,主要包括:歌曲的附加信息(比如标题和版权),歌曲速度和系统码(sysex)等。
接着是分音轨块,主要包括:MIDI事件、非MIDI事件和系统码事件,每个事件前面都有时间差。事件的基本格式为:种类+参数。
不管是全局音轨还是含有音符的分音轨,都以“4D 54 72 68”开头,它其实是ASCII字符“MTrk”,其后跟着一个4字节的整数,它标志了该轨道的字节数,这不包括前面的4个字节和本身的4个字节。
通过简单的计算得出一个音轨块的长度最多是(2^32+8)个字节,由于音轨块长度是可变常量,最小为1个字节,最大是4个字节,字节的最高位没有标志位,完全采用10进制转为16进制的计算方法,从这种意义上来讲,音轨块中MIDI事件个数也是有限制的。
3.1 时间差(delta-time)
时间差是可变常量(variable length quantity),含义是将要发生的事件与前一事件之间的时间差值。如果音轨第一个事件发生在开头,或者两个事件同时发生,<delta-time>设为零。
注意:时间差区别于文件头块的DTT,两者的具体含义是不同的,只是都是长度变量。时间差的10进制在转为16进制时,字节的最高位是标志位。具体方如下:
对于0-127tick, 标志位为0,用一个字节(8位表示);对于大于127tick,标志位为1,用多个字节表示,也即除了最后一个字节的最高有效位是0外,其它字节最高位是1。
优点:这种记录方法允许一个数值被一次一个字节地读取,如果发现某个字节的最高有效位是0,那么它就是这个数值的最后一个字节。
依照MIDI说明,全部delta-time的长度最多不超过4字节,并且最大值为0FFFFFFF。
例1: 65535tick(10进制)=83 FF 7F(16进制)
65535tick=128^2*3+128^1*127+128^0*127
1000 0011=83 由于远远大于127,所以标志位为1(3的16进制0000 0011把最高位0改为1,也即1000 0011)
1111 1111=FF由于大于127,所以标志位为1(127的16进制0111 1111把最高位0改为1,也即1111 1111)
0111 1111=7F由于小于127,所以标志位为0(127的16进制0111 1111)
3. 2 MIDI事件(MIDI events)
查文章《MIDI作曲系统简介》通过把MIDI的不同通道分配给各种音色,就能产生乐器合奏的效果,音源最大的特点是可以根据需要调整音色的参数,以编辑新的,甚至是本来不存在的音色。
MIDI事件也叫MIDI events,常见的有音符事件、控制器事件和系统信息事件等。
事件组成:种类+参数。
种类用状态字节来区分,总是大于等于80H。
参数用数据字节来区分,总是小于80H。
因此可以很容易区分状态字节和数据字节。在状态字节中,用数据的低4为表示通道号,高4位表示不同的命令。
这里有个例外就是meta-event,状态字节是FF,需要一个长的参数区分不同事件,3.3 章节给出了此类事件的规范。如何参照下表读取MIDI事件,第5章将给出详细介绍。下表中的x是音轨通道,总共有16个(0是第一通道)。
种类 |
参数(16进制) |
|
字节 |
含义 |
|
8x |
松开音符 Note off |
音符(00—7F):松开的音符 |
力度:00—7F |
||
9x |
按下音符 Note on |
音符(00-7F);按下的音符 |
力度:00-7F |
||
Ax |
Key after Touch |
音符:00-7F |
力度:00-7F |
||
Bx |
控制器 |
控制器号码:00-7F |
控制器参数:00-7F |
||
Cx |
Program changes |
乐器号码:00-7F |
Dx |
Aftertouch |
值:00-7F |
Ex |
滑音 |
音高(pitch)低位:pitch mod 128 |
音高(pitch)高位:pitch div 128 |
||
F0 |
系统码 |
系统码字节总数:长度变量(有标志位) |
系统码:不含开头的F0,但包括结尾的F7 |
||
FF |
其他格式 |
格式种类:00-FF |
数据占用的字节总数:长度变量(有标志位) |
||
数据 |
||
00-7F |
上次激活格式的参数(8x,9x,Ax,Bx,Cx,Dx,Ex) |
3.3 非MIDI事件(Non- MIDI events)
非MIDI事件。非MIDI事件也叫meta-event(元事件),是MIDI文件中的非MIDI信息,语法规定如下:
FF<种类><字节数><数据>
FF <type> <length> <bytes>
所有的meta-event都以0xFF开头,接着是事件种类(总小于128)、数据的长度值length(用长度变量表示,无标志位)、数据。如果没有数据,那么长度为0。元事件的规范如下表:
下表详细的列出了FF的详细情况,对于字节数由数据决定的情况,表中以
“--”表示
种类 |
字节数 |
数据 |
|
字节 |
含义 |
||
00 |
设置轨道音序 |
02 |
音序号 00 00-FF FF |
01 |
歌曲备注 |
-- |
文本信息 |
音轨文本 |
文本信息 |
||
02 |
歌曲版权 |
-- |
版权信息 |
03 |
歌曲标题 |
-- |
歌曲标题:用于全局音轨,第一次使用表示主标题,第二次表示副标题 |
音轨名称 |
-- |
音轨名 |
|
04 |
乐器名称 |
-- |
音轨文本(同01/2) |
05 |
歌词 |
-- |
歌词 |
06 |
标记 |
-- |
用文本标记(marker) |
07 |
开始点 |
-- |
用文本记录开始点(同01/2) |
08 |
Program name |
-- |
歌曲文件的名字 |
09 |
设备名字 DeviceName |
-- |
MIDI设备的名字 |
20 |
MIDI通道 |
01 |
MIDI通道,0通常为第一通道 |
21 |
MIDI接口 |
01 |
接口号码 |
2F |
音轨结束标志 |
00 |
无 |
51 |
速度 |
03 |
3字节整数,1个4分音符的微妙数 |
54 |
SMPTE时间 |
05 |
SMPTE的开始时间(时,分,秒,帧,复帧) |
58 |
节拍 |
04 |
分子 |
分母:00(1),01(2),02(4),03(8)等 |
|||
节拍器时钟 |
|||
一个4分音符包括的32分音符的个数 |
|||
59 |
调号 |
02 |
升降号数:-7~-1(降号),0(c),1~7(升号) |
大小调:0(大调),1(小调) |
|||
7F |
音符特定信息 |
-- |
音符特定信息 |
3. 4 系统码事件(sysex event)
系统码,又叫MIDI专有信息是跟音源(音色库)交流最精确的语言,专门用来调整MIDI设备内部参数设置的指令,由十六进制数构成。 系统码分三个部分 <1>固定的开头 F0 4X 10 4X F0: 系统码开头 4X: 厂商代码YAMAHA为43 ROLAND为41 KORG 为42 10: 音源设备编号 4X: 当前音源格式的选择….XG为4C….GS为42 <2>核心部分 例如: F0 43 10 4C 00 00 04 7F F7 后面的00 0X 04 表示工作在主控音量上,其中0X表示设置的通道,7F 代表取值范围
1. 复位码 一. GS F0 41 10 42 12 40 00 7F 00 41 F7 二. XG F0 43 10 4C 00 00 7E 00 F7 三. GM F0 7E 7F 09 01 F7 Msb( most singnificant bit) 、Lsb (least singnificant bit ),都是两位的十六进制数, 因为XG格式的音源系统码是开放的,所以就以XG标准的音源来讲,如果你只有GS标准的音源…那可以把YAMAHA格式的设置成GS在YAMAHA音源下的TG300B模式 系统码:F0 43 71 7E 09 01 F7或者 F0 41 10 42 12 40 00 7F 00 41 F7 重要:若想让MIDI乐器正确响应系统码,在开始先用复位码把它设置成相应的模式 用于传输大量的数据给一个MIDI设备,例如patch寄存器、音序器、waveform数据;
也有可能传送特别的信息给一个模型设备,例如在一个Roland Physical Modeling Synth中,可能被用来为一个操作员设置反馈水平。
状态描述:0xF0-0xF7 其中低字节代表使用的MIDI的不同音轨
数据描述:在0xF0-0xF7之间,可能有任意的数据bytes,最重要的是头一个数据bytes(紧跟在0xF0后),应该是制造商的ID值。
对每一个MIDI设备定义它自己的sysex event,并且所有的MIDI sysex event必须是以0xF0开始,以0xF7(结束标志)结束,没有0xF0状态标志是不可能发生的,虽然0xF7作为结束标志的,但是事实上,任何的状态都有可能引起系统信息终止,若一个系统码事件未传达,这种情况就没必要以一个F7作为标志来结束事件。
系统码可以写在任何音轨,不过通常写在全局音轨中,时间差设成00。
对于制造商的ID,MMA已经针对多种多样的制造商的ID设定了特殊的值,以便设备确定信息是否来自传送的制造商。例如,Roland设备的IDbytes是0x41,如果MIDI设备收到的ID不是0x41,设备将忽略剩余的bytes值,包括0xF7。
例2:传输信息F0 43 12 00 07 F7
F0 05 43 12 00 07 F7。
F0是标志位,05是5个字节,内容是43 12 00 07 F7。
F7结束标志,使得在读MIDI文件时知道已经读取整个系统信息。 注意:若单个系统码事件被分割成几部分,每一部分在不同时段传输,每一部分(除了第一个)的系统码事件必须以F7开头,不以F7结尾(除了最后一个,因为它必须要以F7结尾),而且各部分事件之间不能含有其它可传输的MIDI事件。
例3:假设发送字节F0 43 12 00,接着延时200-tick,又接着字节43 12 00 43 12 00,延时100-tick,字节43 12 00 F7,在MIDI文件里头是:
F0 03 43 12 00 81 48 200-tick 时间差 F7 06 43 12 00 43 12 00 64 100-tick 时间差 F7 04 43 12 00 F7
例4: 附件3的TITANIC.MID
00 F0 0A 41 10 42 12 40 00 7F 00 41 F7
第四章 MIDI事件< MIDI event>构成
4.1 音符
当你在MIDI键盘上按下一个琴键,你不是在制造一个声音而是发出一条MIDI指令,至于这个信息能发什么声音,完全取决于电缆另一端的MIDI乐器(如果有的话)。MIDI电缆里完全没有音频数据。
给出音符的符号,如何找到相对应的16进制数,本节给出了2种方法:
(1)针对熟识音乐的人来讲,代入公式就可以得到,快速有效;
(2)对于普通人来讲,查询表格就可以得到。
音符标号,也就是音高,MIDI规格有128个音符标号,中央C的音符标号被定义为60。音符标号值越小音高就越低,值越大音高就越高。
4.1.1 运算查找法
已经知道音符的16进制数,求音符的符号。假设音符是N0,其中音名N,音阶为0
公式:N=B mod 12 ;0=B div 12-1 (1)
其中B表示音符的字节的10进制数,N的10进制数值参照下表:
音名 |
C |
#C |
D |
#D |
E |
F |
#F |
G |
#G |
A |
#A |
B |
10进制数值 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
例1:已经知道某一音符的16进制为45,求对应的音符为A4
45(16进制)=69(10进制)
根据公式(1)得出69 mod 12 = 9 =N; 4= 69 div 12-1
参照N的10进制数得出音符为A,音阶为4
已经知道音符的符号,求对应的16进制数。假设音符是N0,其中音名为N,音阶为0
公式:10进制公式:(0+1)*12+N (2)
例2:计算音符G2的16进制为2B
代入公式(2)为(2+1)*12+7=43,16进制为2B
4.1.2 图表查找法
音符的有效范围是0-127,16进制是00-7FH,可以直接参照下表查找。
MIDI音符代码表
编号 |
音符代码 |
所在的音阶 |
音调 |
编号 |
音符代码
|
所在的音阶 |
音调 |
||
|
(二进制码) |
(十六进制码) |
|
|
|
(二进制码) |
(十六进制码) |
|
|
0 |
0000000 |
00 |
-1 |
C |
64 |
1000000 |
40 |
4 |
E |
1 |
0000001 |
01 |
-1 |
C# |
65 |
1000001 |
41 |
4 |
F |
2 |
0000010 |
02 |
-1 |
D |
66 |
1000010 |
42 |
4 |
F# |
3 |
0000011 |
03 |
-1 |
D# |
67 |
1000011 |
43 |
4 |
G |
4 |
0000100 |
04 |
-1 |
E |
68 |
1000100 |
44 |
4 |
G# |
5 |
0000101 |
05 |
-1 |
F |
69 |
1000101 |
45 |
4 |
A |
6 |
0000110 |
06 |
-1 |
F# |
70 |
1000110 |
46 |
4 |
A# |
7 |
0000111 |
07 |
-1 |
G |
71 |
1000111 |
47 |
4 |
B |
8 |
0001000 |
08 |
-1 |
G# |
72 |
1001000 |
48 |
5 |
C |
9 |
0001001 |
09 |
-1 |
A |
73 |
1001001 |
49 |
5 |
C# |
10 |
0001010 |
0A |
-1 |
A# |
74 |
1001010 |
4A |
5 |
D |
11 |
0001011 |
0B |
-1 |
B |
75 |
1001011 |
4B |
5 |
D# |
12 |
0001100 |
0C |
0 |
C |
76 |
1001100 |
4C |
5 |
E |
13 |
0001101 |
0D |
0 |
C# |
77 |
1001101 |
4D |
5 |
F |
14 |
0001110 |
0E |
0 |
D |
78 |
1001110 |
4E |
5 |
F# |
15 |
0001111 |
0F |
0 |
D# |
79 |
1001111 |
4F |
5 |
G |
16 |
0010000 |
10 |
0 |
E |
80 |
1010000 |
50 |
5 |
G# |
17 |
0010001 |
11 |
0 |
F |
81 |
1010001 |
51 |
5 |
A |
18 |
0010010 |
12 |
0 |
F# |
82 |
1010010 |
52 |
5 |
A# |
19 |
0010011 |
43 |
0 |
G |
83 |
1010011 |
53 |
5 |
B |
20 |
0010100 |
14 |
0 |
G# |
84 |
1010100 |
54 |
6 |
C |
21 |
0010101 |
15 |
0 |
A |
85 |
1010101 |
55 |
6 |
C# |
22 |
0010110 |
16 |
0 |
A# |
86 |
1010110 |
56 |
6 |
D |
23 |
0010111 |
17 |
0 |
B |
87 |
1010111 |
57 |
6 |
D# |
24 |
0011000 |
18 |
1 |
C |
88 |
1011000 |
58 |
6 |
E |
25 |
0011001 |
19 |
1 |
C# |
89 |
1011001 |
59 |
6 |
F |
26 |
0011010 |
1A |
1 |
D |
90 |
1011010 |
5A |
6 |
F# |
27 |
0011011 |
1B |
1 |
D# |
91 |
1011011 |
5B |
6 |
G |
28 |
0011100 |
1C |
1 |
E |
92 |
1011100 |
5C |
6 |
G# |
29 |
0011101 |
1D |
1 |
F |
93 |
1011101 |
5D |
6 |
A |
30 |
0011110 |
1E |
1 |
F# |
94 |
1011110 |
5E |
6 |
A# |
31 |
0011111 |
1F |
1 |
G |
95 |
1011111 |
5F |
6 |
B |
32 |
0100000 |
20 |
1 |
G# |
96 |
1100000 |
60 |
7 |
C |
33 |
0100001 |
21 |
1 |
A |
97 |
1100001 |
61 |
7 |
C# |
34 |
0100010 |
22 |
1 |
A# |
98 |
1100010 |
62 |
7 |
D |
35 |
0100011 |
23 |
1 |
B |
99 |
1100011 |
63 |
7 |
D# |
36 |
0100100 |
24 |
2 |
C |
100 |
1100100 |
64 |
7 |
E |
37 |
0100101 |
25 |
2 |
C# |
101 |
1100101 |
65 |
7 |
F |
38 |
0100110 |
26 |
2 |
D |
102 |
1100110 |
66 |
7 |
F# |
39 |
0100111 |
27 |
2 |
D# |
103 |
1100111 |
67 |
7 |
G |
40 |
0101000 |
28 |
2 |
E |
104 |
1101000 |
68 |
7 |
G# |