本文分析midi文件的组成和工作原理。
midi文件中没有任何音频数据,它仅仅是记录了该使用什么乐器演奏,演奏乐器的节拍,演奏力度等等。而真正发出音频信号数据到扬声器的是音源文件。音源文件是预先录制好的一种乐器的声音,一种乐器用一个固定的编号来表示,称之为键值,演奏的轻重程度称为力度,一种乐器演奏的时长称为tick。
废话不多,下面开始分析midi文件组成。
数据格式:
<标志符串>(4字节) + <头块数据区长度>(4字节) + <头块数据区>(6字节)
标志符串"MThd" ,MThd是头块类型,用十六进制表示就是4d 54 68 64。
头块数据区长度,指的是后面接着的头块数据区长度,因为长度是6字节,所以固定显示为00 00 00 06。
头块数据区,共有6字节,分别为ff ff nn nn dd dd。
ff ff 指定midi文件格式,一般有3种:
nn nn 指定轨道数,一般都会大于1,因为除了演播主音轨外,还会有全局音轨。
dd dd 指定基本时间格式。
dd dd 的最高位为标记位。
0为采用ticks计时,后面的数据为一个4分音符的ticks;
1为SMPTE格式计时,后面的数值则是定义每秒中SMTPE帧的数量及每个SMTPE帧的tick。
用我们举例的midi来看看,dd dd 的数据为01 E0,表示采用ticks计时,1E0转十进制为480,也就是每个4分音符,包含480ticks。后面事件时间都是以ticks为单位。
音轨块的数据格式:
<标志符串>(4字节) + <音轨块数据区长度>(4字节) + <音轨块数据区>(多个MIDI事件构成)
在头块之后,剩余的部分是一个或者多个音轨块。每个音轨块的结构如上面所示,包含3部分。
第一部分是音轨块的标志符,用ASCII码字符串表示为"MTrk",用十六进制表示就是4d 54 72 6b。
第二部分是音轨块数据区,长度也为固定4字节,指定后面的数据区长度。
第三部分是音轨块的数据区,它是由多个midi事件构成,如下面所示:
< delta time > + < MIDI 消息 >
事件大体上可以分为音符、控制器和系统信息这几个种类。对于这些事件,都有统一的表达结构:种类+参
数。
“delta time”单位是tick,用动态字节表示。
MIDI消息是由一个状态字节加多个数据字节构成,状态字节最高位一直是1,所以它的范围是128255之间。数据字节最高位一直是0,所以它的范围是0127之间。
消息可分为通道消息和系统消息。
下面是MIDI通道消息的构成:
通道消息是对单一的MIDI Channle起作用,Channle的表示使用状态字节的低4位(用X表示)表示,即0~F。
状态字节 | 状态功能 | 数据字节 |
---|---|---|
8X | 松开音符 | 1字节音符号,2字节力度 |
9X | 按下音符 | 1字节音符号,2字节力度 |
AX | 触后音符 | 1字节音符号,2字节力度 |
BX | 控制器变化 | 1字节控制器号,2字节参数 |
CX | 改变乐器 | 1字节:弯音轮变换值的低字节 / 2字节:弯音轮变换值的高字节 |
EX | 滑音(弯音轮) | 1字节音符号,2字节力度 |
F0 | 系统码 | 系统码字节数:动态字节系统码:不含开头的 F0,但包括结尾的 F7 |
种类字节 | 功能 | 数据字节长度 | 意义 |
---|---|---|---|
00 | 设置轨道音序 | 02 | 音序号 |
01 | 文字事件 | – | 文本信息 |
02 | 版权 | – | 版权信息 |
03 | 歌曲名称 | – | 全局音轨名称 |
04 | 指定乐器 | – | 乐器名称 |
05 | 歌词 | – | 歌词 |
06 | 标记 | – | |
07 | 注释 | – | 动作或事件 |
2F | 音轨结束标记 | 00 | 必须有结束标记 |
要想播放midi文件,首先在当前机器上要有音源文件。
PC机系统自带了一套音源文件,所以在PC机上用任何播放器都可以播放midi文件。
如果在嵌入式系统上播放midi文件,难度就大了点。嵌入式系统上播放midi,就好比是要制作一台电子琴,首先要把音源烧录到flash中,在没有文件系统的情况下,需要简析midi文件,再把midi文件以数组或其它形式的方法保存下来,供代码使用。
一个单片机上使用的midi文件
0x4D , 0x54 , 0x68 , 0x64 , // "MThd"
0x00 , 0x00 , 0x00 , 0x06 , // 头块长度:不包括前4字节和本4字节
0x00 , 0x01 , // 格式;1-多轨,同步
0x00 , 0x03 , // 轨道数 = 3
0x01 , 0xE0 , // 基本时间格式 0x01E0 = 480;
0x4D , 0x54 , 0x72 , 0x6B , // "MTrk"
0x00 , 0x00 , 0x00 , 0x15 , // 21 该规长度:不包括前4字节和本4字节
0x00 , // 时间差
0xFF , 0x58 , 0x04 , 0x04 , 0x02 , 0x18 , 0x08 , // 其他功能、节拍、长度4、分子4、分母4、节拍器时钟24、一个四分音符包含的32分音符的个数8
0x00 , // 时间差
0xFF , 0x51 , 0x03 , 0x0C , 0x35 , 0x00 , // 其他功能、速度、长度3:1个四分音符的微秒数800,000
0x84 , 0xE7 , 0x00 , // 时间差:128^2*4+128*103+0 = 78720
0xFF , 0x2F , 0x00 , // 音轨结束标志。
//======================================================================
0x4D , 0x54 , 0x72 , 0x6B , // "MTrk"
0x00 , 0x00 , 0x09 , 0xC9 , // 2505 该规长度:
0x00 , // 时间差
0xFF , 0x03 , 0x07 , // 歌曲标题,音轨名称,长度7
0x54 , 0x72 , 0x61 , 0x63 , 0x6B , 0x20 , 0x31 , // "Track 1"
0x00 , // 时间差
0xFF , 0x04 , 0x1F , // 乐器名称,长度31,--"Microsoft GS Wavetable SW Synth"
0x4D , 0x69 , 0x63 , 0x72 , 0x6F , 0x73 , 0x6F , 0x66 , 0x74 , 0x20 , 0x47 , 0x53 , 0x20 , 0x57, 0x61 , 0x76 ,
0x65 , 0x74 , 0x61 , 0x62 , 0x6C , 0x65 , 0x20 , 0x53 , 0x57 , 0x20 , 0x53 , 0x79 , 0x6E , 0x74, 0x68 ,
0x00 , // 时间差
0xB0 , 0x0A , 0x40 , // 调换控制,控制号10(声像控制器),(CAKEWALK默认值是64)新值64
0x89 , 0x30 , // 时间差=128*9+48=1200us
0x90 , 0x40 , 0x50 , // 音符打开,音符号64,速度80 (E5 : MI)
0x81 , 0x58 , // 时间差=128*1+88=216us
0x80 , 0x40 , 0x40 , // 音符关闭,音符号64,速度64 (E5 : MI)
0x18 , // 时间差=24us
0x90 , 0x45 , 0x50 , // 音符打开,音符号69,速度80 (A5 : La)
0x81 , 0x58 , // 时间差=128*1+88=216us
0x80 , 0x45 , 0x40 , // 音符关闭,音符号69,速度64 (A5 : La)
0x18 ,
0x90 , 0x47 , 0x50 , // 音符打开,音符号71,速度80 (B5 : Si)
0x81 , 0x58 , // 时间差=128*1+88=216us
0x80 , 0x47 , 0x40 , // 音符关闭,音符号71,速度64 (B5 : Si)
0x18 ,
0xB0 , 0x40 , 0x7F , // 调换控制,控制号64(延音控制器),新值117
0x00 , // 时间差
0x90 , 0x48 , 0x64 , // 音符打开,音符号72,速度100 (C6 : Do)
0x00 , // 时间差
0x90 , 0x45 , 0x50 , // 音符打开,音符号69,速度80 (A5 : La)
0x83 , 0x60 , // 时间差=128*3+96=480us
0x80 , 0x48 , 0x40 , // 音符关闭,音符号72,速度64 (C6 : Do)
0x00 , // 时间差
0x80 , 0x45 , 0x40 , // 音符关闭,音符号69,速度64 (A5 : La)
0x00 , // 时间差
0x90 , 0x4A , 0x64 , // 音符打开,音符号74,速度100 (D6 : RE)
0x81 , 0x70 , // 时间差=128*1+102=230us
0x80 , 0x4A , 0x40 , // 音符关闭,音符号74,速度64 (D6 : RE)
0x00 , // 时间差
0x90 , 0x4C , 0x64 , // 音符打开,音符号76,速度100 (E6 : MI)
0x81 , 0x70 , // 时间差=128*1+102=230us
0x80 , 0x4C , 0x40 , // 音符关闭,音符号76,速度100 (E6 : MI)
0x00 ,
0xB0 , 0x40 , 0x7F , // 调换控制,控制号64(延音控制器),新值117
0x00 ,
0x90 , 0x4A , 0x64 , // 音符打开,音符号74,速度100 (D6 : RE)