| 下面的程序是可以播放midi音乐的。并且有3个灯用来和音乐中的小节同步(三个等依次为 强,次强,弱)。该程序只对单轨音乐进行解码。从开始做到调通灯光,我花了1周的时间。 希望有用的到的朋友。
#i nclude "reg52.h" void send_data(unsigned char *dat,unsigned int len);
//只需要把这里替换成你想换的歌曲 //unsigned long idata time_wait=0xffff; unsigned short idata tempo; unsigned char xdata song_buffer[BUFFER_LEN]; unsigned short idata counter=0; //0---2/4
void timer1_ISR (void) interrupt 3 void led_blink() if(ledindex >= max) void ChangeSpeed (unsigned long mydata) unsigned int get_wait_time(unsigned long wait)
void send_char(unsigned char c) SBUF = c; } void send_data(unsigned char *dat,unsigned int len) void begin_play() //默认是4/4拍 //给base_count附上初值,假设每拍0.5s wait2 = get_wait_time(tempo/2);
if(thing_index)
next_is_begin = 0; unsigned char do_event() int read_track_header() x = read4byte();
//Assume zero
static unsigned char *song_ptr=song;//改变量为了调试.
if(read_midi_header() == 0) |
2007年11月18日星期日
Midi文件解码器(8052)
Midi文件解码器(8052)
2007年11月15日星期四
FW: [嘻嘻哈哈] 标准MIDI文件格式
标准MIDI文件格式
达思挺・考德威尔
标准的MIDI文件格式就像奇异的兽。总体看来,它是那样的让你无法抗拒。当然,你怎样看它无关紧要,可是用足够多的描述符描述一段音乐并使它能够重现,可不是很少的工作就可以完成的。然而,它虽然复杂,但是真正理解之后,MIDI文件格式的结构还是很直观明了的。
在这里我必须放弃一些东西,因为毕竟我不是MIDI也不是MIDI文件专家!最近我为我的PC准备了一块Gravis 超音频音效卡,利用它听完几段MIDI文件(.mid)之后,想:"呵,我要是能够制作自己的MIDI(.mid)文件该多好啊!"嗯,经过烦人的几个小时之后,我发现,那些并不是没有价值的工作。但是,我是不会让一个冗长的文件格式就能够阻止的(此外,我告诉过我的妻子,计算机不是很难用的,而且我十分憎恨当一个伪君子)。那么,在这篇文章中如果发现什么错误,请让我知道,我会修改它的。同时,这份文档的范围并没有提供所有类型的MIDI命令和任何可能的文件配置!这篇基本指南将使读者能够(以中等的时间投资)制作出MIDI类型的文件。
1.概述:
一个MIDI文件基本上由两个部分组成,头块和轨道块。第二节讲述头块,第三节讲述轨道块。一个MIDI文件有一个头块用来描述文件的格式、许多的轨道块等内容。一个轨道可以想象为像一个大型多音轨录音机那样,你可以为某种声音、某种乐谱、某种乐器或者你需要的任何东西分配一个轨道。
2.头块:
头块出现在文件的开头,有三种方式来描述文件。头块看起来一直是这样的:
4D 54 68 64 00 00 00 06 ff ff nn nn dd dd
前4个字节等同于ASCII码MThd,接着MThd之后的4个字节是头的大小。它将一直是00 00 00 00 06,因为现行的头信息将一直是6字节。
ff ff是文件的格式,有3种格式:
0-单轨
1-多规,同步
2-多规,异步
单轨,很显然就只有一个轨道。同步多轨意味着所有轨道都是垂直同步的,或者其他的措辞为他们都在同一时间开始,并且可以表现一首歌的不同部分。异步多轨没有必要同时开始,而且可以完全的不同步。
nn nn 是MIDI文件中的轨道数。
dd dd 是每个4分音符delta-time节奏数(这之后将做详细介绍)。
3.轨道块:
头块之后剩下的文件部分是轨道块。每一个轨道包含一个头,并且可以包含你所希望的许多MIDI命令。轨道头与文件头及其相似:
4D 54 72 6B xx xx xx xx
与头一致,前4个字节是ASCII吗,这个是MTrk,紧跟MTrk的4个字节给出了以字节为单位的轨道的长度(不包括轨道头)。
在头之下是MIDI事件,这些事件同现行的可以被带有累加的MIDI合成器端口接受和发送的数据是相同的。一个MIDI 事件先于一个delta-time。一个delta-time是一个MIDI事件被执行后的节奏数,每个四分之一音符的节奏数先前已经定义在了文件的头块中。这个delta-time是一个可变长度的编码值。这种格式虽然混乱,可是允许根据需要利用多位表示较大的数值,这不会因为需求小的数值情况下以添零的方式浪费掉一些字节!数值被转换为7位的字节,并且除了最后一个字节以最高有效位是0外,各个字节最有意义的一位是1,。这就允许一个数值被一次一个字节地读取,你如果发现最高有效位是0,则这就是这个数值的最后一位(意义比较小)。依照MIDI说明,全部delta-time的长度最多超过4字节。
delta-time 之后就是MIDI事件,每个MIDI事件(除了正在运行的事件外)带有一个最高有效位总是1的命令字节(值将>128)。大部分命令的列表在附录A 中。每个命令都有不同的参数和长度,但是接下来的数据将是最高有效位为零(值将<128)。这里有个例外就是meta-event,最高有效位可以是1。然而,meta-events需要一个长的参数以区分。
微小失误就可以导致混乱的是运行模式,这是现行MIDI命令所忽略的地方,并且最终发行的MIDI命令是假定的。这就意味这如果包含了命令,那么MIDI事件就是由delta-time与参数组成而转换的。
4.综述:
如果这份说明仅仅是使问题更加混乱,那么以下提供的例子可能有助于澄清问题!同时,两个公用程序和一个图解文件包含在这个文档里面:
DEC.EXE――这个公共程序是将二进制文件(比如.MID)转换成以十进制表示的对应每个字节的有标记界限的文本文件。
REC.EXE――这个公共程序是将有标记界限的十进制数文本文件对应的每一字节转换成二进制文件。
MIDINOTE.PS――这是一个对应键盘和五线谱的音符数字附录页。
附录A
1.MIDI事件命令
每个命令字节有两部分,左nybble(4位)包含现行的命令,右nybble包含将被执行的命令的通道号,这里有16各MIDI通道8个MIDI命令(命令nybble必须最高有效位是1的)。在下表中,X表示MIDI通道号。所有的音符即数据字节都<128(最高有效位是0)。
十六进制 二进制 数据 描述
8x 1000xxxx nn vv 音符关闭 (释放键盘)
nn=音符号
vv=速度
9x 1001xxxx nn vv 音符打开 (按下键盘)
nn=音符号
vv=速度
Ax 1010xxxx nn vv 触摸键盘以后
nn=音符号
vv=速度
Bx 1011xxxx cc vv 调换控制
cc=控制号
vv=新值
Cx 1100xxxx pp 改变程序(片断)
pp=新的程序号
Dx 1101xxxx cc 在通道后接触
cc=管道号
Ex 1110xxxx bb tt 改变互相咬和的齿轮 (2000H 表明缺省或没有改变)(什么意思搞不懂:)
bb=值的低7位(least sig)
tt=值的高7位 (most sig)
下表是没有通道的 meta-events列表 ,他们的格式是:
FF xx nn dd
所有的 meta-events 是以 FF 开头的命令 (xx),长度,或者含在数据的字节数(nn),现行的数据(dd)
十六进制 二进制 数据 描述
00 00000000 nn ssss 设定轨道的序号
nn=02 (两字节长度的序号)
ssss=序号
01 00000001 nn tt .. 你需要的所有文本事件
nn=以字节为单位的文本长度
tt=文本字符
02 00000010 nn tt .. 同文本的事件, 但是用于版权信息
nn tt=同文本事件
03 00000011 nn tt .. 序列或者轨道名
nn tt=同文本事件
04 00000100 nn tt .. 轨道乐器名
nn tt=同文本事件
05 00000101 nn tt .. 歌词
nn tt=同文本事件
06 00000110 nn tt .. 标签
nn tt=同文本事件
07 00000111 nn tt .. 浮点音符
nn tt=同文本事件
2F 00101111 00 这个事件一定在每个轨道的结尾出现
51 01010001 03 tttttt 设定拍子
tttttt=微秒/四分音符
58 01011000 04 nn dd cc bb 拍子记号
nn=拍子记号分子
dd=拍子记号分母2=四分之一
3=8分拍, 等等.
cc=节拍器的节奏
bb=对四分之一音符标注的第32号数字
59 01011001 02 sf mi 音调符号
sf=升调/降调(-7=7 降调, 0=基准C调,7=7 升调)
mi=大调/小调(0=大调, 1=小调)
7F 01111111 xx dd .. 音序器的详细信息
xx=被发送的字节数
dd=数据
下表列出了控制整个系统的系统消息。这里没有MIDI通道数 (这些一般仅应用于MIDI键盘等.)
十六进制 二进制 数据 描述
F8 11111000 同步所必须的计时器
FA 11111010 开始当前的队列
FB 11111011 从停止的地方继续一个队列
FC 11111100 停止一个队列
下表列出的是与音符相对应的命令标记。
八度音阶|| 音符号
# ||
|| C | C# | D | D# | E | F | F# | G | G# | A | A# | B
-----------------------------------------------------------------------------
0 || 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11
1 || 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23
2 || 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35
3 || 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47
4 || 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59
5 || 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71
6 || 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83
7 || 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95
8 || 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107
9 || 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119
10 || 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 |
参考资料:
"MIDI Systems and Control" Francis Rumsey 1990 Focal Press
"MIDI and Sound Book for the Atari ST" Bernd Enders and Wolfgang Klem 1989 M&T Publishing, Inc.
MIDI file specs and general MIDI specs were also obtained by sending e-mail to LISTSERV@AUVM.AMERICAN.EDU with the phrase GET MIDISPEC PACKAGE in the message.
达思挺・考德威尔
标准的MIDI文件格式就像奇异的兽。总体看来,它是那样的让你无法抗拒。当然,你怎样看它无关紧要,可是用足够多的描述符描述一段音乐并使它能够重现,可不是很少的工作就可以完成的。然而,它虽然复杂,但是真正理解之后,MIDI文件格式的结构还是很直观明了的。
在这里我必须放弃一些东西,因为毕竟我不是MIDI也不是MIDI文件专家!最近我为我的PC准备了一块Gravis 超音频音效卡,利用它听完几段MIDI文件(.mid)之后,想:"呵,我要是能够制作自己的MIDI(.mid)文件该多好啊!"嗯,经过烦人的几个小时之后,我发现,那些并不是没有价值的工作。但是,我是不会让一个冗长的文件格式就能够阻止的(此外,我告诉过我的妻子,计算机不是很难用的,而且我十分憎恨当一个伪君子)。那么,在这篇文章中如果发现什么错误,请让我知道,我会修改它的。同时,这份文档的范围并没有提供所有类型的MIDI命令和任何可能的文件配置!这篇基本指南将使读者能够(以中等的时间投资)制作出MIDI类型的文件。
1.概述:
一个MIDI文件基本上由两个部分组成,头块和轨道块。第二节讲述头块,第三节讲述轨道块。一个MIDI文件有一个头块用来描述文件的格式、许多的轨道块等内容。一个轨道可以想象为像一个大型多音轨录音机那样,你可以为某种声音、某种乐谱、某种乐器或者你需要的任何东西分配一个轨道。
2.头块:
头块出现在文件的开头,有三种方式来描述文件。头块看起来一直是这样的:
4D 54 68 64 00 00 00 06 ff ff nn nn dd dd
前4个字节等同于ASCII码MThd,接着MThd之后的4个字节是头的大小。它将一直是00 00 00 00 06,因为现行的头信息将一直是6字节。
ff ff是文件的格式,有3种格式:
0-单轨
1-多规,同步
2-多规,异步
单轨,很显然就只有一个轨道。同步多轨意味着所有轨道都是垂直同步的,或者其他的措辞为他们都在同一时间开始,并且可以表现一首歌的不同部分。异步多轨没有必要同时开始,而且可以完全的不同步。
nn nn 是MIDI文件中的轨道数。
dd dd 是每个4分音符delta-time节奏数(这之后将做详细介绍)。
3.轨道块:
头块之后剩下的文件部分是轨道块。每一个轨道包含一个头,并且可以包含你所希望的许多MIDI命令。轨道头与文件头及其相似:
4D 54 72 6B xx xx xx xx
与头一致,前4个字节是ASCII吗,这个是MTrk,紧跟MTrk的4个字节给出了以字节为单位的轨道的长度(不包括轨道头)。
在头之下是MIDI事件,这些事件同现行的可以被带有累加的MIDI合成器端口接受和发送的数据是相同的。一个MIDI 事件先于一个delta-time。一个delta-time是一个MIDI事件被执行后的节奏数,每个四分之一音符的节奏数先前已经定义在了文件的头块中。这个delta-time是一个可变长度的编码值。这种格式虽然混乱,可是允许根据需要利用多位表示较大的数值,这不会因为需求小的数值情况下以添零的方式浪费掉一些字节!数值被转换为7位的字节,并且除了最后一个字节以最高有效位是0外,各个字节最有意义的一位是1,。这就允许一个数值被一次一个字节地读取,你如果发现最高有效位是0,则这就是这个数值的最后一位(意义比较小)。依照MIDI说明,全部delta-time的长度最多超过4字节。
delta-time 之后就是MIDI事件,每个MIDI事件(除了正在运行的事件外)带有一个最高有效位总是1的命令字节(值将>128)。大部分命令的列表在附录A 中。每个命令都有不同的参数和长度,但是接下来的数据将是最高有效位为零(值将<128)。这里有个例外就是meta-event,最高有效位可以是1。然而,meta-events需要一个长的参数以区分。
微小失误就可以导致混乱的是运行模式,这是现行MIDI命令所忽略的地方,并且最终发行的MIDI命令是假定的。这就意味这如果包含了命令,那么MIDI事件就是由delta-time与参数组成而转换的。
4.综述:
如果这份说明仅仅是使问题更加混乱,那么以下提供的例子可能有助于澄清问题!同时,两个公用程序和一个图解文件包含在这个文档里面:
DEC.EXE――这个公共程序是将二进制文件(比如.MID)转换成以十进制表示的对应每个字节的有标记界限的文本文件。
REC.EXE――这个公共程序是将有标记界限的十进制数文本文件对应的每一字节转换成二进制文件。
MIDINOTE.PS――这是一个对应键盘和五线谱的音符数字附录页。
附录A
1.MIDI事件命令
每个命令字节有两部分,左nybble(4位)包含现行的命令,右nybble包含将被执行的命令的通道号,这里有16各MIDI通道8个MIDI命令(命令nybble必须最高有效位是1的)。在下表中,X表示MIDI通道号。所有的音符即数据字节都<128(最高有效位是0)。
十六进制 二进制 数据 描述
8x 1000xxxx nn vv 音符关闭 (释放键盘)
nn=音符号
vv=速度
9x 1001xxxx nn vv 音符打开 (按下键盘)
nn=音符号
vv=速度
Ax 1010xxxx nn vv 触摸键盘以后
nn=音符号
vv=速度
Bx 1011xxxx cc vv 调换控制
cc=控制号
vv=新值
Cx 1100xxxx pp 改变程序(片断)
pp=新的程序号
Dx 1101xxxx cc 在通道后接触
cc=管道号
Ex 1110xxxx bb tt 改变互相咬和的齿轮 (2000H 表明缺省或没有改变)(什么意思搞不懂:)
bb=值的低7位(least sig)
tt=值的高7位 (most sig)
下表是没有通道的 meta-events列表 ,他们的格式是:
FF xx nn dd
所有的 meta-events 是以 FF 开头的命令 (xx),长度,或者含在数据的字节数(nn),现行的数据(dd)
十六进制 二进制 数据 描述
00 00000000 nn ssss 设定轨道的序号
nn=02 (两字节长度的序号)
ssss=序号
01 00000001 nn tt .. 你需要的所有文本事件
nn=以字节为单位的文本长度
tt=文本字符
02 00000010 nn tt .. 同文本的事件, 但是用于版权信息
nn tt=同文本事件
03 00000011 nn tt .. 序列或者轨道名
nn tt=同文本事件
04 00000100 nn tt .. 轨道乐器名
nn tt=同文本事件
05 00000101 nn tt .. 歌词
nn tt=同文本事件
06 00000110 nn tt .. 标签
nn tt=同文本事件
07 00000111 nn tt .. 浮点音符
nn tt=同文本事件
2F 00101111 00 这个事件一定在每个轨道的结尾出现
51 01010001 03 tttttt 设定拍子
tttttt=微秒/四分音符
58 01011000 04 nn dd cc bb 拍子记号
nn=拍子记号分子
dd=拍子记号分母2=四分之一
3=8分拍, 等等.
cc=节拍器的节奏
bb=对四分之一音符标注的第32号数字
59 01011001 02 sf mi 音调符号
sf=升调/降调(-7=7 降调, 0=基准C调,7=7 升调)
mi=大调/小调(0=大调, 1=小调)
7F 01111111 xx dd .. 音序器的详细信息
xx=被发送的字节数
dd=数据
下表列出了控制整个系统的系统消息。这里没有MIDI通道数 (这些一般仅应用于MIDI键盘等.)
十六进制 二进制 数据 描述
F8 11111000 同步所必须的计时器
FA 11111010 开始当前的队列
FB 11111011 从停止的地方继续一个队列
FC 11111100 停止一个队列
下表列出的是与音符相对应的命令标记。
八度音阶|| 音符号
# ||
|| C | C# | D | D# | E | F | F# | G | G# | A | A# | B
-----------------------------------------------------------------------------
0 || 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11
1 || 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23
2 || 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35
3 || 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47
4 || 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59
5 || 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71
6 || 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83
7 || 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95
8 || 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107
9 || 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119
10 || 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 |
参考资料:
"MIDI Systems and Control" Francis Rumsey 1990 Focal Press
"MIDI and Sound Book for the Atari ST" Bernd Enders and Wolfgang Klem 1989 M&T Publishing, Inc.
MIDI file specs and general MIDI specs were also obtained by sending e-mail to LISTSERV@AUVM.AMERICAN.EDU with the phrase GET MIDISPEC PACKAGE in the message.
--
由 Pegasus 于 11/12/2007 11:26:00 下午 在 嘻嘻哈哈 上发表
2007年11月13日星期二
C++编译中经常遇到的错误
C++编译中经常遇到的错误
1、fatal error C1010: unexpected end of
file while looking for precompiled header directive。
file while looking for precompiled header directive。
寻找预编译头文件路径时遇到了不该遇到的文件尾。(一般是没有#include
"stdafx.h")
2、fatal error C1083: Cannot open
include file: 'R…….h': No such file or directory
include file: 'R…….h': No such file or directory
不能打开包含文件"R…….h":没有这样的文件或目录。
3、error C2011: 'C……': 'class' type
redefinition
redefinition
类"C……"重定义。
4、error C2018: unknown character
'0xa3'
'0xa3'
不认识的字符'0xa3'。(一般是汉字或中文标点符号)
5、error C2057: expected constant
expression
expression
希望是常量表达式。(一般出现在switch语句的case分支中)
6、error C2065: 'IDD_MYDIALOG' :
undeclared identifier
undeclared identifier
"IDD_MYDIALOG":未声明过的标识符。
7、error C2082: redefinition of formal
parameter 'bReset'
parameter 'bReset'
函数参数"bReset"在函数体中重定义。
8、error C2143: syntax error: missing
':' before '{'
':' before '{'
句法错误:"{"前缺少";"。
9、error C2146: syntax error : missing
';' before identifier 'dc'
';' before identifier 'dc'
句法错误:在"dc"前丢了";"。
10、error C2196: case value '69'
already used
already used
值69已经用过。(一般出现在switch语句的case分支中)
11、error C2509: 'OnTimer' : member
function not declared in 'CHelloView'
function not declared in 'CHelloView'
成员函数"OnTimer"没有在"CHelloView"中声明。
12、error C2511: 'reset': overloaded
member function 'void (int)' not found in 'B'
member function 'void (int)' not found in 'B'
重载的函数"void
reset(int)"在类"B"中找不到。
reset(int)"在类"B"中找不到。
13、error C2555: 'B::f1': overriding
virtual function differs from 'A::f1' only by return type or
calling convention
virtual function differs from 'A::f1' only by return type or
calling convention
类B对类A中同名函数f1的重载仅根据返回值或调用约定上的区别。
14、error C2660: 'SetTimer' : function
does not take 2 parameters
does not take 2 parameters
"SetTimer"函数不传递2个参数。
15、warning C4035: 'f……': no return
value
value
"f……"的return语句没有返回值。
16、warning C4553: '= =' : operator has
no effect; did you intend '='?
no effect; did you intend '='?
没有效果的运算符"=
=";是否改为"="?
=";是否改为"="?
17、warning C4700: local variable
'bReset' used without having been initialized
'bReset' used without having been initialized
局部变量"bReset"没有初始化就使用。
18、error C4716: 'CMyApp::InitInstance'
: must return a value
: must return a value
"CMyApp::InitInstance"函数必须返回一个值。
19、LINK : fatal error LNK1168: cannot
open Debug/P1.exe for writing
open Debug/P1.exe for writing
连接错误:不能打开P1.exe文件,以改写内容。(一般是P1.Exe还在运行,未关闭)
20、error LNK2001: unresolved external
symbol "public: virtual _ _thiscall C……::~C……(void)"
symbol "public: virtual _ _thiscall C……::~C……(void)"
连接时发现没有实现的外部符号(变量、函数等)。
Best Regards
Miao Jia Hong
Miao Jia Hong
MIDI编曲
MIDI编曲
| 低阶MIDI的API包括字首为midiIn和midiOut的函式,它们分别用於读取来自外部控制器的MIDI序列和在内部或外部的合成器上播放音乐。尽管其名称为「低阶」,但使用这些函式时并不需要了解MIDI卡上的硬体介面。 要在播放音乐的准备期间打开一个MIDI输出设备,可以呼叫midiOutOpen函式: error = midiOutOpen (&hMidiOut, wDeviceID, dwCallBack, dwCallBackData, dwFlags) ; 如果呼叫成功,则函式传回0,否则传回错误代码。如果参数设定正确,则常见的一种错误就是MIDI设备已被其他程式使用。 该函式的第一个参数是指向HMIDIOUT型态变数的指标,它接收後面用於MIDI输出函式的MIDI输出代号。第二个参数是设备ID。要使用真实的 MIDI设备,这个参数范围可以是从0到小於由midiOutGetNumDevs传回的数值。您还可以使用MIDIMAPPER,它在 MMSYSTEM.H中定义为-1。大多数情况下,函式的後三个参数设定为NULL或0。 一旦打开一个MIDI输出设备并获得了其代号,您就可以向该设备发送MIDI讯息。此时可以呼叫: error = midiOutShortMsg (hMidiOut, dwMessage) ; 第一个参数是从midiOutOpen函式获得的代号。第二个参数是包装在32位元DWORD中的1位元组、2位元组或者3位元组的讯息。我在前面讨论过,MIDI讯息以状态位元组开始,後面是0、1或2位元组的资料。在dwMessage中,状态位元组是最不重要的,第一个资料位元组次之,第二个资料位元组再次之,最重要的位元组是0。 例如,要在MIDI通道5上以0x7F的速度演奏中音C(音符是0x3C),则需要3位元组的Note On讯息: 0x95 0x3C 0x7F midiOutShortMsg的参数dwMessage等于0x007F3C95。 三个基础的MIDI讯息是Program Change(可为某一特定通道而改变乐器声音)、Note On和Note Off。打开一个MIDI输出设备後,应该从一条Program Change讯息开始,然後发送相同数量的Note On和Note Off讯息。 当您一直演奏您想演奏的音乐时,您可以重置MIDI输出设备以确保关闭所有的音符: midiOutReset (hMidiOut) ; 然後关闭设备: midiOutClose (hMidiOut) ; 使用低阶的MIDI输出API时,midiOutOpen、midiOutShortMsg、midiOutReset和midiOutClose是您需要的四个基础函式。 现在让我们演奏一段音乐。BACHTOCC,如程式22-8所示,演奏了J. S. Bach著名的风琴演奏的D小调《Toccata and Fugue》中托卡塔部分的第一小节。 |
2007年11月11日星期日
Standard MIDI File Format
Standard MIDI File Format
Dustin Caldwell
The standard MIDI file format is a very strange beast. When viewed as a
whole,
describing a piece of music in enough detail to be able to reproduce it
accurately is no small task. So, while complicated, the structure of the midi
file format is fairly intuitive when understood.
I must insert a disclaimer here that I am by no means an expert with
midi nor midi files. I recently obtained a Gravis UltraSound board for my
and upon hearing a few midi files (.MID) thought, "Gee, I'd like to be able to
make my own .MID files." Well, many aggravating hours later, I discovered that
this was no trivial task. But, I couldn't let a stupid file format stop me.
(besides, I once told my wife that computers aren't really that hard to use,
and I'd hate to be a hypocrite) So if any errors are found in this
information, please let me know and I will fix it. Also, this document's scope
does not extend to EVERY type of midi command and EVERY possible file
configuration. It is a basic guide that should enable the reader (with a
moderate investment in time) to generate a quality midi file.
1. Overview
A midi (.MID) file contains basically 2 things, Header chunks and Track
chunks. Section 2 explains the header chunks, and Section 3 explains the track
chunks. A midi file contains ONE header chunk describing the file format,
etc., and any number of track chunks. A track may be thought of in the same
way as a track on a multi-track tape deck. You may assign one track to each
voice, each staff, each instrument or whatever you want.
2. Header Chunk
The header chunk appears at the beginning of the file, and describes the
file in three ways. The header chunk always looks like:
4D 54 68 64 00 00 00 06 ff ff nn nn dd dd
The ascii equivalent of the first 4 bytes is MThd. After MThd comes the 4-byte
size of the header. This will always be 00 00 00 06, because the actual header
information will always be 6 bytes.
ff ff is the file format. There are 3 formats:
0 - single-track
1 - multiple tracks, synchronous
2 - multiple tracks, asynchronous
Single track is fairly self-explanatory - one track only. Synchronous multiple
tracks means that the tracks will all be vertically synchronous, or in other
words, they all start at the same time, and so can represent different parts
in one song. Asynchronous multiple tracks do not necessarily start at the same
time, and can be completely asynchronous.
nn nn is the number of tracks in the midi file.
dd dd is the number of delta-time ticks per quarter note. (More about this
later)
3. Track Chunks
The remainder of the file after the header chunk consists of track chunks.
Each track has one header and may contain as many midi commands as you like.
The header for a track is very similar to the one for the file:
4D 54 72 6B xx xx xx xx
As with the header, the first 4 bytes has an ascii equivalent. This one is
MTrk. The 4 bytes after MTrk give the length of the track (not including the
track header) in bytes.
Following the header are midi events. These events are identical to the
actual data sent and received by MIDI ports on a synth with one addition. A
midi event is preceded by a delta-time. A delta time is the number of ticks
after which the midi event is to be executed. The number of ticks per quarter
note was defined previously in the file header chunk. This delta-time is a
variable-length encoded value. This format, while confusing, allows large
numbers to use as many bytes as they need, without requiring small numbers to
waste bytes by filling with zeros. The number is converted into 7-bit bytes,
and the most-significant bit of each byte is 1 except for the last byte of the
number, which has a msb of 0. This allows the number to be read one byte at a
time, and when you see a msb of 0, you know that it was the last (least
significant) byte of the number. According to the MIDI spec, the entire delta-
time should be at most 4 bytes long.
Following the delta-time is a midi event. Each midi event (except a
running midi event) has a command byte which will always have a msb of 1 (the
value will be >= 128). A list of most of these commands is in appendix A. Each
command has different parameters and lengths, but the data that follows the
command will have a msb of 0 (less than 128). The exception to this is a meta-
event, which may contain data with a msb of 1. However, meta-events require a
length parameter which alleviates confusion.
One subtlety which can cause confusion is running mode. This is where
the actual midi command is omitted, and the last midi command issued is
assumed. This means that the midi event will consist of a delta-time and the
parameters that would go to the command if it were included.
4. Conclusion
If this explanation has only served to confuse the issue more, the
appendices contain examples which may help clarify the issue. Also, 2
utilities and a graphic file should have been included with this document:
DEC.EXE - This utility converts a binary file (like .MID) to a tab-delimited
text file containing the decimal equivalents of each byte.
REC.EXE - This utility converts a tab-delimited text file of decimal values
into a binary file in which each byte corresponds to one of the decimal
values.
MIDINOTE.PS - This is the postscript form of a page showing note numbers with
a keyboard and with the standard grand staff.
Appendix A
1. MIDI Event Commands
Each command byte has 2 parts. The left nybble (4 bits) contains the actual
command, and the right nybble contains the midi channel number on which the
command will be executed. There are 16 midi channels, and 8 midi commands (the
command nybble must have a msb of 1).
In the following table, x indicates the midi channel number. Note that all
data bytes will be <128 (msb set to 0).
Hex Binary Data Description
8x 1000xxxx nn vv Note off (key is released)
nn=note number
vv=velocity
9x 1001xxxx nn vv Note on (key is pressed)
nn=note number
vv=velocity
Ax 1010xxxx nn vv Key after-touch
nn=note number
vv=velocity
Bx 1011xxxx cc vv Control Change
cc=controller number
vv=new value
Cx 1100xxxx pp Program (patch) change
pp=new program number
Dx 1101xxxx cc Channel after-touch
cc=channel number
Ex 1110xxxx bb tt Pitch wheel change (2000H is normal or no
change)
bb=bottom (least sig) 7 bits of value
tt=top (most sig) 7 bits of value
The following table lists meta-events which have no midi channel number. They
are of the format:
FF xx nn dd
All meta-events start with FF followed by the command (xx), the length, or
number of bytes that will contain data (nn), and the actual data (dd).
Hex Binary Data Description
00 00000000 nn ssss Sets the track's sequence number.
nn=02 (length of 2-byte sequence number)
ssss=sequence number
01 00000001 nn tt .. Text event- any text you want.
nn=length in bytes of text
tt=text characters
02 00000010 nn tt .. Same as text event, but used for
copyright info.
nn tt=same as text event
03 00000011 nn tt .. Sequence or Track name
nn tt=same as text event
04 00000100 nn tt .. Track instrument name
nn tt=same as text event
05 00000101 nn tt .. Lyric
nn tt=same as text event
06 00000110 nn tt .. Marker
nn tt=same as text event
07 00000111 nn tt .. Cue point
nn tt=same as text event
2F 00101111 00 This event must come at the end of each
track
51 01010001 03 tttttt Set tempo
tttttt=microseconds/quarter note
58 01011000 04 nn dd ccbb Time Signature
nn=numerator of time sig.
dd=denominator of time sig. 2=quarter
3=eighth, etc.
cc=number of ticks in metronome click
bb=number of 32nd notes to the quarter
note
59 01011001 02 sf mi Key signature
sf=sharps/flats (-7=7 flats, 0=key of C,
7=7 sharps)
mi=major/minor (0=major, 1=minor)
7F 01111111 xx dd .. Sequencer specific information
xx=number of bytes to be sent
dd=data
The following table lists system messages which control the entire system.
These have no midi channel number. (these will generally only apply to
controlling a midi keyboard, etc.)
Hex Binary Data Description
F8 11111000 Timing clock used when synchronization is
required.
FA 11111010 Start current sequence
FB 11111011 Continue a stopped sequence where left
off
FC 11111100 Stop a sequence
The following table lists the numbers corresponding to notes for use in note
on and note off commands.
Octave|| Note Numbers
# ||
|| C | C# | D | D# | E | F | F# | G | G# | A | A# | B
-----------------------------------------------------------------------------
0 || 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11
1 || 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23
2 || 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35
3 || 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47
4 || 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59
5 || 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71
6 || 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83
7 || 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95
8 || 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107
9 || 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119
10 || 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 |
BIBLIOGRAPHY
"MIDI Systems and Control" Francis Rumsey 1990 Focal Press
"MIDI and Sound Book for the Atari ST" Bernd Enders and Wolfgang Klemme
1989 M&T Publishing, Inc.
MIDI file specs and general MIDI specs were also obtained by sending e-mail
to LISTSERV@AUVM.AMERICAN.EDU with the phrase GET MIDISPEC PACKAGE
in the message.
------------------------------- DEC.CPP ------------------------------------
/* file dec.cpp
by Dustin Caldwell (dustin@gse.utah.edu)
*/
#include <dos.h>
#include <stdio.h>
#include <stdlib.h>
void helpdoc();
main()
{
FILE *fp;
unsigned char ch, c;
if((fp=fopen(_argv[1], "rb"))==NULL) /* open file to read */
{
printf("cannot open file %s\n",_argv[1]);
helpdoc();
exit(-1);
}
c=0;
ch=fgetc(fp);
while(!feof(fp)) /* loop for whole file */
{
printf("%u\t", ch); /* print every byte's decimal equiv. */
c++;
if(c>8) /* print 8 numbers to a line */
{
c=0;
printf("\n");
}
ch=fgetc(fp);
}
fclose(fp); /* close up */
}
void helpdoc() /* print help message */
{
printf("\n Binary File Decoder\n\n");
printf("\n Syntax: dec binary_file_name\n\n");
printf("by Dustin Caldwell (dustin@gse.utah.edu)\n\n");
printf("This is a filter program that reads a binary file\n");
printf("and prints the decimal equivalent of each byte\n");
printf("tab-separated. This is mostly useful when piped \n");
printf("into another file to be edited manually. eg:\n\n");
printf("c:\>dec sonata3.mid > son3.txt\n\n");
printf("This will create a file called son3.txt which can\n");
printf("be edited with any ascii editor. \n\n");
printf("(rec.exe may also be useful, as it reencodes the \n");
printf("ascii text file).\n\n");
printf("Have Fun!!\n");
}
---------------------------- REC.CPP ----------------------------------
/* File rec.cpp
by Dustin Caldwell (dustin@gse.utah.edu)
*/
#include <dos.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
void helpdoc();
main()
{
FILE *rfp, *wfp;
unsigned char ch, c;
char s[20];
if((rfp=fopen(_argv[1], "r"))==NULL) /* open the read file */
{
printf("cannot open file %s \n",_argv[1]);
helpdoc();
exit(-1);
}
if((wfp=fopen(_argv[2], "wb"))==NULL) /* open the write file */
{
printf("cannot open file %s \n",_argv[1]);
helpdoc();
exit(-1);
}
c=0;
ch=fgetc(rfp);
while(!feof(rfp)) /* loop for whole file */
{
if(isalnum(ch)) /* only 'see' valid ascii chars */
{
c=0;
while(isdigit(ch)) /* only use decimal digits (0-9) */
{
s[c]=ch; /* build a string containing the number */
c++;
ch=fgetc(rfp);
}
s[c]=NULL; /* must have NULL terminator */
fputc(atoi(s), wfp);/* write the binary equivalent to file */
}
ch=fgetc(rfp); /* loop until next number starts */
}
fclose(rfp); /* close up */
fclose(wfp);
}
void helpdoc() /* print help message */
{
printf("\n Text File Encoder\n\n");
printf("\n Syntax: rec text_file_name binary_file_name\n\n");
printf("by Dustin Caldwell (dustin@gse.utah.edu)\n\n");
printf("This is a program that reads an ascii tab-\n");
printf("delimited file and builds a binary file where\n");
printf("each byte of the binary file is one of the decimal\n");
printf("digits in the text file.\n");
printf(" eg:\n\n");
printf("c:\>rec son3.txt son3.mid\n\n");
printf("(This will create a file called son3.mid which is\n");
printf("a valid binary file)\n\n");
printf("(dec.exe may also be useful, as it decodes binary files)\n\n");
printf("Have Fun!!\n");
}
订阅:
博文 (Atom)