MIDI文件格式分析
MIDI文件属于二进制文件,这种文件一般都有如下基本结构: 文件头+数据描述
文件头一般包括文件的类型,因为Midi文件仅以.mid为扩展名的就有0类和1类两种,而大家熟悉的位图文件的格式就更多了,所以才会出现文件头这种东西。
而数据描述部份是主体,我们现在来一起分析它的结构:
在每个Midi文件的开头都有如下内容,它们的十六进制代码为:“4d 54 68 64 00 00 00 06 ff ff nn nn dd dd”。
前四个是ASCII字符“MThd”是用来鉴别是否Midi文件,而随后的四个字节是指明文件头描述部分的字节数,它总是6,所以一定是“00 00 00 06”,以下是剩余部分的含义:
以上就是MIDI文件头了,后面的所有内容都是真正做事的,我们先来看看它的构成。
MIDI的数据是由若干个格式相同的子数据构成的,这些子数据在多音轨的格式中记录了一个轨道的所有信息。多加一个音轨,就简单地把数据追加在前一音轨的后面就可以了,不过不要忘记更改文件头中的nn nn(轨道数)。
先看全局音轨。全局音轨包括歌曲的附加信息(比如标题和版权)、歌曲速度和系统码(Sysx)等内容。
不管是全局音轨还是含有音符的音轨,都以“4D 54 72 6B”开头,它其实是ASCII字符“MTrk”,其后跟着一个4个字节的整数,它标志了该轨道的字节数,这不包括前面的4个字节和本身的4个字节。这一点,我们可以在后面的例子中去理解。
接着就是记录数据的地方了,每一个数据有着相同的结构:时间差+事件。
所谓时间差,指的是前一个事件到该事件的时间数,它的单位是tick(MIDI的最小时间单位)。它的构成比较特殊,这里要用二进制来说明。
一个字节有8位,如果仅使用7位,它可以表示0~127这128个数,而剩下的一位,则用来作为标志。如果要表示的数在以上范围,则这个标志为0, 这时,一个7位的字节可以表示0~127tick。如果要表示的数超出了这个范围(比如240),则把标志设置成1,然后记录下高7位,剩下的留给下一个 字节,在该例中240可以分解成128*1+112,这里的1就是第一个字节要记录的,加上标志位,应该为10000001,即十六进制的81;而112 是下一个字节记录的,它的十六进制为70:所以要表示240这个时间,要写成81 70。同理,如果要表示65535tick,则可以先计算出65535=1282*3+1281*127+1280*127,然后得出结果:83 FF 7F。由此,我们反过来也可以知道如何确定时间差:只要标志位为0,则表示结束读取时间差。比如82 C0 03表示1282*2+1281*64+1280*3=40963,如果基本时间为120,则有341:043个四分音符。
以这种方式记录整数的字节称为动态字节,它根据记录的整数改变自身的长度,这在后面还要用到,所以必须熟练计算。
看完了这么麻烦的东西,我们再来看个更麻烦的东西:事件。在这些标准的解释后面,我们会通过一些例子来进一步掌握这些内容。
事件大体上可以分为音符、控制器和系统信息这几个种类。对于这些事件,都有统一的表达结构:种类+参数。
对于一个音符,由于它的有效范围是0~127,所以直接用00~7F作为“种类”,可以认为是个音符,比如3C表示中央C。而一个音符的最重要的参数是力度(也叫速度:velocity)。比如,3C 64 表示一个力度为十进制100的中央C音符。
因为一个字节有8位,所以剩余的一位如果置1,再联合其他的7位,则可以表示各种信息。我们暂且无视一个音轨到底是全局的还是用于记录音符的。它们归根结底都是用来记录各种事件的,只不过有些应出现在全局音轨比较合乎逻辑而已。既然这样,我们就可以从下面的表来看事件:
下表中,x表示音轨0~F,比如81表示松开第二轨的音符。
下表详细地列出了FF的详细情况,对于字节数由数据决定的情况,表中以“--”表示。
这些就是MIDI结构的全部内容,在下一讲,我们将通过一个实例来分析。
qq魔域要书写二进制(十六进制)文件,应该准备好一些工具,比如我自己用的是VC++,因为学习MIDI格式无非是想写它的软件,既然VC++可以编辑二进制文件,就将就着用吧。其次,应该个可以编辑和播放MIDI文件的软件,比如Cakewalk,这样就可以开始了。
首先书写文件头“4d 54 68 64 00 00 00 06”,我们直接写同步多音轨的格式,先写1个音轨,并以120为一个音符的基本时间。这样,随后的字节是:“00 01 00 01 00 78”。
现在,如果用Cakewalk打开会失败,因为我们指定的音轨数为1,但是并没有书写任何音轨,如果改成“00 01 00 00 00 78”再打开,就不会出问题了。所以,今后如果更改了音轨数,千万不要忘记向“上头”汇报。
把轨道数改回01,继续我们的实验。先写音轨的头信息:“4D 54 72 6B”(MTrk),因为我们还不能确定后面有多少字节,所以先把它假设成“00 00 00 00”,以后再回来改。
我们先尝试设置歌曲的速度和节拍等基本信息。假设一个四分音符的时间是半秒,即0.5*106微秒。它的十六进制数是07A120,再看事件表,设 置速度是51,但是在其前面必须
三国战记1是FF,然后它须要3个字节作为参数,因此字节数为03,参数为“07 A1 20”,也就是“FF 51 03 07 A1 20”。这是事件部分,不要忘记在其之前有个参数——时间差。这是一开始就应该设置的参数,因此时间差为00。所以,完整的事件应该是“00 FF 51 03 07 A1 20”,我们把这一段追加在Midi文件末尾。
这时先不要急着用Cakewalk验证,因为我们还没有向“上级”报告,没错,把前面表示字节数的“00 00 00 00”改成“00 00 00 07”,如果用VC++作为二进制文件的编辑器,选择了事件后,可以在状态栏看到选择的字节长。保存后,再用Cakewalk打开,就可以看见速度是 120。
我们再来设置节拍和调号,因为一般用Cakewalk新建一个Midi会默认地设置成4/4,C大调,我们就改设成6/8,A大调。查阅事件表知 道,58和59是分别用来设置节拍和调号的。虽然设置节拍的参数很多,但在现在的系统中,后两个参数是被忽略的,而且Cakewalk还会对其进行修正。 因此,我们只要设置好实际有用的就可以了。分子是6,分母是8,所以第一个参数是06,第二个参数是03(23=8)。最后,补上 前面的时间差和后面的两个被忽略的参数,它应该是“00 FF 58 04 06 03 00 00”;再看调号,A调有3个升号,因此可以这样
的事件可以表示为“00 FF 59 02 03 00”。事实上,大小调是个被忽略的参数。我们统计一下至今为止事件的字节数,然后更改前面的参数,即把“00 00 00 07”改成“00 00 00 15”。保存后用Cakewalk打开,再进入五线谱窗口,就可以马上验证了。细心的你可能已经发现,进入五线谱窗口前和平常有些延迟,这是因为我们并没 有设置好那些可以忽略的字节,而Cakewalk就是在对其进行重新验证,这一点,我们以后再讨论。
用同样的方法,您可以很容易地设置歌曲的标题和版权,这作为一个练习,在这里就不多写了。我们现在学习写一个含有音符的轨道。首先您应该知道要做哪些事:1、写新音轨的信息头;2、向上级汇报多了一个音轨。接下来,我们开始写入一个简单的音符。
假设向第一拍写一个中音A,这里可能要先说明一下,音符是从C0开始一起向上数的,数到中央C(C5)是十六进制的3C,则中音A应该为45,在附 件中有详细的计算方法。我们知道在音乐中一个音符通常有三个属性:音高、力度和时值。可是我们在事件表中并没有看见有什么可以直接设置音符时值的标志。不 错,事实上,音符的时值是由按下的时间和松开的时间决定的。我们假设要写入一个八分音符。它的Tick数是四分音符的一半,即60,十六进制表示成3C校园小说免费完结版。 我们先来看看与音符有关的标志。
在事件表中,9x是用来打开一个音符,我们这里假设使用第7个通道(注意到MIDI有16个通道(Channel),而第10个被默认地用作打击乐,所以,我们在这个阶段(没有学习Sysx之前),先不要使用第10个通道),则9x中的x是6;再看它的参数,一个是音符,这里我们写入45,第二个是力度,我们用70,因为是一开始就触发的,所以前面的时间差还是00。这样我们就在第5个通道以力度112按下了一个中音A。对应的字节描述是“00 96 45 70”。它的时值不用想都知道一定是0,这取决于什么时候把它松开。
特别地,如果一个音符的力度为0,则MIDI认为用户想松开这个键,因为9x已经打开了通道,所以我们直接写入一个带00力度的同一音符就可以决定 这个音符的时值了。根据前面的分析,这个时间差应该是3C,所以我们在写入3C后写上音符45和它的力度00,即“3C 45 00”。统计好字节数并向这一轨的头信息中更新,然后保存到磁盘,用Cakewalk打开并进入事件列表窗口便可以验证了。
在这个基础上,我们再尝试在A的后面增加一个四分音符中音#G。因为96已经打开了通道,我们没有必要每次都使用9x,只要输入事件信息即可。对于 中音#G,它的十六进制是44,相对刚才输入00力度的A来说时间差为00,因此可以表示成“00 44 64”华妃扮演者,这里我们已
经假设力度为100;然后是松开它,因为是四分音符,所以时间差是78H,别忘记力度是00,它的字节应表示成“78 44 00”,做好后面的工作,然后验证看对不对。
我们再做个稍微复杂一点的实验:在原来的基础上,在同一轨的第一拍加上一个附点四分中音D。这里就不能再使用追加的方法了,因为前面的事件已经过了3个八分音符的时间,无论再加上什么,都只会发生在后面,所以我们要在前面插入一些字节。
9x已经打开了通道,我们直接在9x按下的音符后加上一个音符事件“00 3E 64”,这里的00显然是个时间差,3E是中音D,64是力度,也就是说,在按下中音A的同时按下了中音D。我们又按下一个键了,要在什么时候,在哪里松 开才能保证输入的是个附点八分音符呢?首先,它的时值是3个八分音符,即180,这里还有一点要注意,180是个大于128的数,它的动态字节就应该表示 成“81 34”,在哪里输入才好呢?如果你觉得在按下D后输入,或者在任何什么地方输入这个时间差,然后再写上“3E 00”可以表示松开的话就完全误解了时间差的概念。其实,我们只要简单地在松开#G的时候松开D就可以了,所以应该在末尾补上“00 3E 00”。统计好字节数后到Cakewalk中去验证验证吧。这里附有我们目前写下的Midi文件样本。益禾堂奶茶加盟费多少
到目前为此,我们应该可以输入任何形式的音符了,不过MIDI除了音符以外,还可以包括各种控制器和系统码,它们的地位不亚于音符,我们现在马上学习如何使用控制器。
控制器比音符要简单多了,我们尝试在#G前加入相位控制(Pan),它的十进制代码是10,十六进制是0A,我们将参数设置成111,即十六进制的 6F。首先查得控制器是Bx,这里的x和上面一样,也是6。接下来写入控制器号0A,然后是参数6F,别忘了前面的时间差是00。所以这段字节是“00 B6 0A 6F”,它应放在松开#G的事件之前,与按下#G同时。不过,一旦使用了非音符,而后面还有音符事件时,则必须重新通知打开音符,这说起来复杂,做起来还 七号公园歌词是比较容易的,我们只要稍微改写下一个音符事件即可:原本是“时间差+音符+力度”,我们加入一个打开音符的标志,成为“时间差+9x+音符+力度”即 可。校验过头信息后,去Cakewalk中进行更进一步的检验便知它的可行。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论