老夫n年前拎着上网本在上下班的公交车上写出的MIDI文件解析相关内容,再贴到这里一份~

读书笔记——MIDI文件结构简介

庆祝论坛乔迁新居!我也把我几个月来研究MIDI文件的一些心得体会,贴到坛子上分享。哈哈,每天上下班在路上的一个多小时里,我拎着一个上网本,在公交车上慢慢地敲字~时间宝贵,这一个多小时的时间也要充分利用~

但这些东东毕竟是我个人的理解,难免有错误出现。也欢迎各位大虾批评指正,我会把一些修改的内容通过附录添加到文章末尾。

阅读本文的话,读者需要有基础性的电脑和MIDI音乐相关知识:

· 基本的计算机相关知识,如十六进制数的表示法,十六进制与十进制、二进制之间的转换方法等;
· 基本的乐理知识,如音高、时长、节拍、曲调等;
· MIDI协议基础,如各种MIDI事件的类型和含义,事件状态字、数据长度等的知识;
· MIDI文件的编辑制作经验,如音序器(硬件或软件)基于音轨的操作思想和使用方式等。

参考资料的话,推荐读者去找一下《MIDI原理与开发应用》这本书,国防工业出版社出版,陈学煌、刘永志、潘晓利、马俊编著,红色封皮。同时读者还可以去网上搜索相关的资料,有几个帖子写得也不错。

下面是正文:

  1. MIDI文件结构简介

MIDI文件是二进制文件,其内部主要记录了乐曲播放时,音序器应发送给音源的MIDI指令和每条指令发送的时间点。音序器读取这些时间信息和MIDI指令,通过在相应的时间发送相应的指令,以实现乐曲中音符的顺序播放和节拍信息。除了音序器需要发送的MIDI事件之外,MIDI文件内部也记录了一些辅助信息,如版权信息、音轨名、速度信息、拍号、调号等等,这些信息被称为Meta-event,只用于记录一些曲子的信息,通常并不发送给MIDI系统中的其他设备。

MIDI文件的数据结构被称为“chunk”。每个chunk由最初4字节的“chunk类型”,紧接着4字节的“Chunk大小”,和最后长度可变的“Chunk Data”构成。“Chunk data”的数据长度由“Chunk大小”来规定,即“Chunk大小”只描述了chunk中数据段的长度,而不是整个chunk的长度。

构成MIDI文件的Chunk主要有两种类型:一种是Header Chunk(MThd),另一种是Track Chunk(MTrk)。

Header Chunk位于整个MIDI文件的起始处,是必须存在的,其起始标记就是ASCII码形式的“MThd”字符串。Track Chunk的起始标记,依然是ASCII码形式的“MTrk”字符串,并且Track Chunk整块分布于MIDI文件之中的任何位置,数量也不定,从1块到若干块皆可。实际上一个MIDI文件就是由一个Header Chunk和若干Track Chunk组成。读者若使用一个十六进制编辑软件(如UltraEdit)打开并查看一个MID文件时,便能找到这两部分。

MIDI文件可能容纳的chunks只有Header Chunk和Track Chunk,其它的非法数据结构将被忽略(作者最新补充:有些厂商通过规定其他的Chunk以实现特殊功能,但有时候当其他厂商的音序器不支持这些Chunk时,有可能会破坏这些数据,所以要慎重编辑)。在MIDI的Chunk文件结构中,自长度区以后的数据格式,是严格规定好的。

这里要探讨不同数量的MTrk chunk所构成的MIDI文件的类型。MIDI文件的类型通常分为三种,分别是MIDI 0格式文件、MIDI 1格式文件、MIDI 格式2文件。

它们的相同点是:无论哪一种格式的MIDI文件,都要具备一个MThd chunk,和至少一条MTrk chunk。不同点是:它们各自的MTrk chunk数量不同,并且各个 chunk之间的播放方式略有区别。很多入门级电子琴和具备播放简单和弦铃声的手机,只能播放MIDI 0格式文件;平时用音序器软件编辑MIDI时,又最好保存为MIDI 1格式文件。这种现象是有原因的。

MIDI 0格式文件只有一个MTrk chunk。在这个chunk中,包含了整个MIDI文件中的MIDI事件,包括meta-event、演奏信息、效果器信息等等。所以播放器只需要顺序读取并解析文件,并发送实际的MIDI事件即可。播放MIDI 0格式文件,对于入门级电子琴或者手机这种性能较弱、资源紧张的嵌入式系统来说,相对容易一些。音序器不需要考虑在不同MTrk之间来回跳跃取数,只需要像流媒体文件那样顺序读取并解析就行了。

MIDI 1格式文件具有若干条MTrk chunk,并且chunk之间具有统一的时间信息,也就是说,各个chunk之间的播放是同步进行的。MIDI 1格式文件的第一条MTrk chunk是专用的,称为“Tempo Map”。它包括整个MIDI文件中所有的 meta-event。(笔者新增:后来笔者在玩MIDI时,发现这个Tempo Map有时可能是软件自己创建,也就是说用户在音序器中见到的第一轨,未必就是MIDI文件中的第一个MTrk Chunk即Tempo Map;反过来说,MIDI文件中的Tempo Map有可能不会出现在音序器中)从第二条MTrk chunk开始,每一条MTrk chunk都记录着各自的演奏和效果器等信息。音序器在播放时,将使用统一的时间等信息,同步播放各个chunk。这就像cakewalk软件播放MIDI文件一样,在一个时间轴的滚动下(音轨区的那条标志播放位置的竖线),各个音轨同时播放。实际上cakewalk软件也仅支持MIDI 0和MIDI 1文件。

MIDI 2格式文件也具有若干条MTrk chunk,但每个chunk具有独立的时间信息,也就是说各个chunk的播放并不是同步的,而是每个chunk都遵循自己的时间信息,chunk之间没有统一的时间联系,各自播放。这种文件目前笔者也未曾见过,所以笔者在此不再过多解释此格式的文件。

综上所述,在解读MIDI文件时,首先要找到各个块,也就是一个MThd chunk和若干MTrk chunk的ASCII字符串。这样用户或软件才能根据MThd和MTrk中所记录的信息,确定此MIDI文件的基本参数,并进行下一步更详细的解析。关于MThd和MTrk中详细的解析规则,笔者将在下文中具体解释。

以常用的音序器软件cakewalk为例,在cakewalk软件打开一个MIDI文件后,软件将读取MThd chunk内部的相关信息,从而确定此文件的类型、chunk数目、全局时钟设置等参数。然后软件将读取各个MTrk chunk,并将每个chunk内部包含的MIDI事件列在编辑区内对应的音轨部位。于是,在使用cakewalk打开一个MIDI文件后,用户就可以在编辑区内部看到若干条黄色的音轨,每个音轨内部含有该音轨所包含的MIDI事件,用户可以对不同音轨内的不同事件,甚至整条音轨,进行参数设置。

  1. MThd chunk结构

MThd chunk中保存了此MIDI的一些基本信息,如文件格式(MIDI 0、1、2格式)、此MIDI文件的音轨数(从1到多条)、时间类型(使用MIDI Tick或Frame来计时)。

当然,作为既定的标准,MThd Chunk一定是类似这样的数据结构(十六进制):

4D 54 68 64  // ① MThd的ASCII码,为Header Chunk的标志
00 00 00 06  // ② MThd中数据部分的长度,以目前标准均为6字节
hh hh        // ③ MIDI文件类型
ii ii        // ④ 此MIDI文件的音轨数目
jj jj        // ⑤ 此MIDI文件的时间类型

Header Chunk之中的数据结构定义是严格遵循这个标准的。目前的标准中并未规定Header Chunk有其他的数据定义(但以后也许会扩充)。所以说,在Header Chunk中,前八个字节(即①②的数据结构)是固定样式的,数据段的大小也是固定为6字节的。

下面对数据段中的具体数据结构作一个介绍:

数据③标志着该MIDI文件的格式。MIDI文件格式有三种,0、1、2格式,所以可以分别用0000、0001、0002来表示。每种格式的具体含义请见上文。

数据④标志着该MIDI文件中所包含的音轨数目,也可以认为Track Chunk的数目。对于MIDI 0格式文件,此值仅为1,即只有一个Track Chunk;MIDI 1格式文件则可以有多个Track Chunk,而且Track Chunk数目为实际的音轨数目加一(因为第一个Track Chunk是Tempo Map,不记录实际的演奏信息)。MIDI 2格式文件因为笔者未曾遇见,所以不敢妄为解释。

数据⑤标志着该MIDI文件的时间类型。MIDI的时间类型通常有两种,一种是基于TPQN(Ticks Per Quarter-Note,每四分音符所具有的Midi Tick数)的时间度量法,另一种是基于SMPTE时间码的时间度量法。在这里,MIDI文件使用这个十六位数的最高位,标志这两种时间类型。也就是说,这个时间类型如果大于0x8000,则为SMPTE时间码度量法;如果小于0x8000,则为TPQN时间度量法。而此数的后十五位,则记录着具体的Midi Tick数量。

SMPTE本来是用于视频中的协议,所以它的计量单位为“帧”,就是“frame”。视频中有“帧率”的概念,单位为“帧/秒(fps)”。不同的视频标准中有不同的帧率,比如25fps、30fps等等。如果MIDI系统中使用这种时间度量法,那么它所定义的就是,在每一帧中,所具有的Midi Tick数目。这种度量法在单纯的MIDI系统中比较少见,笔者概念也显模糊,故不细谈。

对于大多数只有音频的MIDI系统中,MIDI文件多采用TPQN时间度量法。TPQN是“Ticks Per Quarter-Note(每四分音符中所包含的Midi Tick数量)”的缩写,它的意思可以从字面来理解。这个数值可以是十进制的60-480之间,数值越大,MIDI系统的时间分辨率就越大,也就是说可以演奏时值越小的音符。通常这个数都采用120、240、480,因为这些数都能被2、3、4甚至6、8整除,方便于八分音符、十六分音符、三连音甚至更短音符的演奏,换算成十六进制,就是0x78、0xF0、0x1E0。当然注意,这些十六进制数的最高位都是0。

  1. MTrk chunk结构

Track Chunk内部则包含了一个MIDI文件中记录的实际的MIDI信息和一些辅助信息(如meta-event)。

Track Chunk依然具有和Header Chunk类似的结构,就是“Chunk标志”+“数据段大小”+“数据”。所以它的结构如下所示(十六进制):

4D 54 72 6B  // ① MTrk的ASCII码,为Track Chunk的标志
pp pp pp pp  // ② MTrk中数据部分的长度
xx yy        // ③ Delta-time及MIDI事件
xx yy        // ③ Delta-time及MIDI事件
……           // (省略)③ Delta-time及MIDI事件
00 FF 2F 00  // ④ meta-event事件,此Track结束

数据①依然是Chunk标志,只不过该标志被换成了ASCII码的MTrk,代表接下来的数据为Track Chunk的数据。

数据②依然是此Chunk中所包含数据的大小。当然这个数就不是如同Header Chunk中那样的常数了,而是要精确描述接下来Track Chunk数据段的大小了。

接下来就是Track Chunk中所包含的真正数据了,就是由许多类似③那样的数据堆积起来的大段数据。xx代表了Delta-time,yy代表了真正的MIDI事件。这些MIDI事件才是音序器在播放MIDI文件时需要实时处理和发送的数据。这种结构笔者将在下文中详细介绍。

数据④从严格意义上讲,也属于③的类型。最初的00代表delta-time,随后的FF 2F 00为一段meta-event,代表了本Track结束。

  1. Delta-Time及MIDI事件结构

delta-time,实际上就是“Δt”。任何学习过数学和物理学的人,都能明白“Δt”的含义:它代表着时间差。

MIDI系统中的delta-time,表征着下一个事件距离上一个事件有多长时间,即两个事件之间的时间差。这个时间不是我们日常生活中的时分秒,而是MIDI Tick。音序器通过对自身产生的MIDI Tick进行计数,判断是否该处理下一个MIDI事件。如果Tick数到达delta-time,就处理下一个事件,然后继续判断下一个delta-time是否到达,周而复始。

为了能够表示足够长的时间,Delta-time使用可变长度数的格式,最长可以表示0x0fffffff。

MIDI事件则包括实际需要发送出去的MIDI事件,和meta-event事件。对于实际需要发送的数据,音序器就直接将数据发送出去;如果是meta-event事件,音序器则修改自身的相关参数。

这里要注意MIDI文件的“状态字省略”特点。为了减少MIDI文件的体积,人们规定:如果同一Track Chunk中的下一条MIDI事件,和上一条事件,属于同一类型同一通道的事件(即状态字相同)时,下一条事件的状态字可以省略,而只需记录数据。音序器碰到这种情况时,应自动填充上一事件的状态字。

举个例子,MIDI文件中的事件,大多数都是“Note-On”和“Note-Off”事件,其中“Note-Off”事件也可以用“Note-On”+“力度为0”来表示。所以在MIDI文件中,这种缩略形式会用到很多。比如连续演奏几个音符时,MIDI文件就会使用这种缩略法来减少文件体积。比如在MIDI文件中,常常会看到这种序列(十六进制):

00 93 3C 6B  // 音符0x3C Note-On,力度为0x6B
70 3C 00     // 实际发送的指令为隔0x70 Ticks之后,发送93 3C 00。

因为力度为0,也就相当于让某个音符停止发音。83 3C 6B和93 3C 00的效果是一样的。所以通过这种写法,MIDI文件可以省略掉一个字节的空间。

总结起来,MIDI文件可以用以下的两张图来描述。通过这两张图,相信读者会对MIDI文件的数据结构建立一个直观的认识。本文的目的就在此。有关MIDI文件更细节的说明,请参考相关的MIDI文档和手册。相信在对MIDI文件有了一个基本认识之后,您再翻阅其他技术文档和手册时,就可以更加深刻地理解其含义了。

图一:MIDI 0格式文件的大概结构

图二:MIDI 1格式文件的大概结构

8 Likes

读书笔记:MIDI Tick、Meta-event、变长数表示法、区分MIDI文件中单个字节的含义

1、MIDI Tick与TPQN:

MIDI中常用的时间度量法是采用Midi Tick。Midi Tick是MIDI系统中最小的时间单位,MIDI系统的所有时间功能,如节拍的产生,都是基于Midi Tick的。MIDI系统中的设备,只需要对Tick计数,就可以获得曲子的时间信息。

TPQN是一个缩写,全称是“Ticks Per Quarter-Note”,即“每四分音符所具有的Tick数”。从字面就可以理解其含义。在一个MIDI文件的MThd chunk中,我们能找到这个参数。通常这个值可以取48-480(这个是准确值,原来那个帖子的60-480写错了,抱歉……)之间。

每Tick的绝对时间长度是关联于曲子拍速(tempo)的。比如在一首4/4拍,tempo为60bpm的曲子中,一个四分音符为一拍,发音长度为1秒。我们将一个四分音符的音长规定为480 Ticks,则480 Ticks为1秒,单个Tick的时间长度就是(1/480)秒。如果MIDI文件中要播放一个四分音符,即一拍,只要在播放时对MIDI Tick计数,计满480个MIDITick后停下,就达到了目的。同理,八分音符就是240 Ticks,一个三连音中的每个连音就是160 Ticks。

具体的解释,请参考笔者在前文中所写的MIDI文件内容的具体含义。

2、Meta-event事件

meta-event事件也 MIDI事件的一部分,但它并不会被发送出去。它只是代表了MIDI文件的一些特征,如速度、调号、拍号、版权信息、音轨名称等等。它的数据格式是:

FF xx nn dd dd dd dd ……

FF代表了此数据类型为meta-event;
xx代表了具体的meta-event事件类型,取值范围为00-7F;
nn代表了其后的meta-event数据长度,用可变长度数来表示;
dd dd dd则是具体的数据。

话说0xFF这个数据,既表示meta-event的标志,同时也是MIDI事件“系统复位”的指令码。如何区分这两个数据呢?我的理解是:系统复位事件属于系统实时信息,是不会出现在MIDI文件中的。所以在MIDI文件中的0xFF,都是meta-event的标志,不会是属于系统实时信息的“系统复位”事件;而meta-event事件是不会被音序器发送出去的,所以MIDI系统中接收到的0xFF,只可能是系统复位事件,不会是meta-event。

3、变长数表示法:

举一个可变长度数的例子:81 70,那么这个delta-time实际表示的数为0xF0。怎么来的?0x81的二进制为10000001,后边还有数据;0x70的二进制为01110000,后面没有数据了,这个变长数表示完毕。把粗体部分的数组合起来,就变成了:000 0001111 0000,就是1111 0000,0xF0。

再举个例子:E3 9A B4 70,这个可变长度数实际上表示的是0xC669A70。这串数是这么变化的:0xE3 = 11100011,后面还有数据;0x9A = 10011010,后面还有数据;0xB4 = 10110100,后面还有数据;0x70 = 0111 000,后面没有数据了,这个变长数表示完毕。把粗体部分的数组合起来,就变成了:1100 0110 0110 1001 1010 0111 0000 = 0xC669A70.

找到规律了吗?这种表示法,将每个字节的最高位,作为一个标志位,标志该字节是否是整个可变长度数的最后一字节。如果最高位为1,则表明此字节不是最末尾的字节;如果最高位为0,表明此字节为最后一字节,即可变长度数表示完成。剩下的七位则是实际要表示的数的数位。所以在把可变长度数改为普通整数时,就顺序阅读可变长度数,并且判断当前字节的最高位,如果是1,说明下一个字节依然是这个可变长度数的下一部分;如果是0,就说明本字节已经是这个可变长度数的最后一字节了。然后只需要把每个字节的后七位取出来,按其所处各自字节的先后顺序,也顺序地拼在一起,就是它所代表的整数了。

4、如何区分MIDI文件中的某一个字节是做什么用的?

如果单独看MIDI文件中的某个字节,我们是看不出这个字节代表的含义的。所以在阅读MIDI文件时,最好是从Track Chunk头开始慢慢阅读,顺藤摸瓜,才能找到这个字节的具体含义。

MIDI文件读取的难度也正在此。因为一个MIDI文件中混杂着各种文件标志符(如有多少Track Chunk、MIDI Tick定义等等)、delta-time(而且还是以变长数表示法表示)、MIDI事件码(还包括可发送的事件和meta-event事件)、事件数据(而且不同事件的数据还不定长),等等等等。各种标志和事件数据码大小不一,长短不齐,十分混乱。

所以说单纯地考察MIDI文件中某个字节的数据,其作用是什么,这很难考察出来。比如一个小于0x80的数,它可能是一个delta-time数据结构的最后一字节,也可能是某个Note-On事件的数据部分,还可能是设置MIDI Tick的TPQN。如果仅仅凭借上下文,也很难猜出这个字节数据的作用,因为它的上下文也可能具有不同的含义,依然会造成歧义。

但实际上MIDI文件依然是有规律可循的。解析MIDI文件的线索就在于:MIDI文件中所记录的每种MIDI事件,或是每种MIDI文件标志符,其数据段长度都是规定好的,要么是协议中已经规定死的,要么是文件中具有指定其大小长度的标志。这是MIDI文件最重要的特点。知道了每个事件或者标志的长短,我们才能准确无误地从MIDI文件中提炼出内容。

一个MIDI文件中记录的内容,无非就是一些MIDI文件本身的标志符,和一些MIDI事件数据,还包括一些meta-event事件,还有delta-time结构,有时(或者说常常)具有省略MIDI事件指令码的缩略结构。

文件本身的标志符长度,通常是固定长度的(比如Header Chunk中的内容);MIDI事件和一部分meta-event都具有协议中规定的长度,如多数MIDI事件具有1-3个字节的长度,系统事件和一部分meta-event虽然长度不定,但这些事件中也都标明了各自的数据段长度;delta-time数据结构采用可变长度数,但因其只能存在于MIDI事件之前,或者说存在于两个事件之中,所以也能很容易地被读取,并且根据可变长度数的特征(最高位作为可变长度数结构是否结束的标志),我们也能知道可变长度数的结构何时结束;对于缩略结构,因其只能适用于MIDI事件之中,所以在读取完delta-time结构后,就可以判断下一字节是否大于0x80,如果大于,则是另一个MIDI事件,如果小于,表明这是一个数据段,其事件类型和指令码依然是上一个指令的类型,数据段大小也和上一指令类型相同。

根据不同事件或标志的不同长度,以及各个MIDI事件和标志符之间位置的关系,还有事件指令码与数据段的大小差异,等等诸多MIDI文件的特点,我们还是能够比较容易地将各个MIDI事件与标志符一一分开的。

所以,在解析一个MIDI文件时,我们要从文件头开始,找到各个Chunk后,以Chunk为单位,慢慢地往下解析。Header Chunk的解析相对容易,Track Chunk在解析时注意区分delta-time数据结构,MIDI事件结构,还有缩略结构。

3 Likes

研究MIDI文件结构,发个读书笔记~有关MIDI音乐的拍速、MIDI Tick速度和周期等等的转

4D 54 68 64 // MThd,MIDI头块
00 00 00 06 // MThd头块大小,6字节
00 00       // MIDI 0(零)型文件,所有音符在一个Track中
00 01       // 轨道数,0型文件只有一个Track
01 E0       // 所谓的TPQN,每个四分音符中有0x1E0(480)个MIDI Tick,

4D 54 72 6B // MTrk,MIDI音轨开始
00 00 12 17 // 此音轨长度为0x1217(4631)字节
......      // 省略一些事件
00          // delta-time
FF          // meta-event标志
51 03       // 四分音符的时长(单位为微秒)
07 27 0E    // 四分音符的时长=0x07270E(468750)微秒
......

以上是一个MIDI文件中的一段数据结构,以下是我的笔记,欢迎大家批评指正~

也就是说,绝对时间是通过meta-event中的FF 51 03 xx xx xx来定义的。这个meta-event表明了一个四分音符的绝对时间长度。而一个四分音符包含多少个tick,又是由MThd块中来定义的。所以这样就能换算出一个tick所需要的时间有多长。

人通常是按照曲子的拍速来度量的。拍速就是指曲子的一分钟有多少拍。通常(有不是通常的时候)一拍就是一个四分音符(quarter-note)。所以(仅在)通常情况下,一拍 == 一个四分音符的长度 == 480个MIDI Tick。

所以,根据meta-event事件的FF 51 03 xx xx xx,可以得到一个四分音符的绝对时间长度。如果用一分钟(60秒,即60,000,000微秒)来除这个值,就得到了曲子的拍速Tempo。比如上面例子,得到的四分音符时间为468750微秒,所以它的拍速就是:

Tempo = 60,000,000 / 468,750 = 128 bpm.

而通过 MThd块中对每个四分音符中包含MIDI Tick数的定义(就是所谓的TPQN,Ticks Per Quarter-Note),将这个四分音符的绝对时间长度,除以TPQN,就得到了每个MIDI Tick的绝对时间。所以示例中的每个MIDI Tick的绝对时间就是:

468,750 / 480 = 976.5625 us = 976562.5 ns。

得到每个MIDI Tick的绝对时间之后,就可以根据MCU如主频等的相关参数,转化为MCU定时器的分频系数,从而使用MCU的定时器来产生精确的MIDI Tick。

至于非通常的情况,有可能一个四分音符是两拍(比如说八六拍的拍号6/8,或者是八七拍7/8,此时八分音符是一拍),也有可能是半拍(比如说二二拍的拍号2/2,此时二分音符是一拍)。这些虽然也有,但用的最多的还是四分音符为一拍的,就是常说的四二拍2/4,四三拍3/4,四四拍4/4,还有比较少见的四五拍5/4。

不过无论拍号怎么变,一个四分音符的绝对时间长度,和一个四分音符的MIDI Tick数都是不会变的,所以对于MIDI本身来讲,人所感受到的节拍,还有这些拍号,都无所谓,只是一拍多一倍或者少一半Tick而已。MIDI本身只需要数MIDI Tick够不够就好了。

2 Likes

搬运完工~这个帖子是9年前还年轻时写的。那时白天在公司给SoC写固件,晚上回家就想用STM32写一个MIDI播放器,接上硬音源,以脱离电脑播放MIDI文件。每天坐公交上下班一个多小时的宝贵时间不能浪费,所以就在公交车上做了研究笔记。不曾想这几篇研究笔记,让俺吃了足足9年的老本哇。如今没那个精力再深搞爱好咧,估计这个研究笔记的老本只能继续吃下去咧,PhD毕业的鸭梨真的山大哇…希望这个老本能让更多的童鞋继续走下去,也欢迎童鞋们踊跃并猛烈地批判这个研究笔记中可能存在的某些错误内容吧;童鞋们把俺批斗得越惨,说明MIDI这个行业还是有能人在关注滴;童鞋们把俺踩在脚下碾压得越厉害,诸位在MIDI行业就能走得更高更远哇。作为老玩家,也可能是某些新玩家的引路人,这点儿觉悟还是有的。总之欢迎大家来踩~【(QAQ)抖…抖M 】

4 Likes

能静下心来研究MIDI文件结构是幸福的,特别是用代码成功指挥电脑和乐器演奏的时候。

然而曾经想用 Arduino 搞一个简单的 mid 播放器的我却最终搞成了太阳能MPPT充电控制器……

有同样想法的筒子们加油吧!

1 Like

河总威武,软硬全通!太阳能MPPT不是比MIDI系统有搞头嘛,这可是最近几年的研究热点哇~太阳能电池的最大功率点跟踪算法,原来大学里有个老师在搞,带了几个本科生做创新设计,其中有俺一个室友,所以也跟着稍微了解了一点。当时学识不深,很多东西看不懂。现在偶尔也会看到几篇文献资料,感觉研究热度依然不减~

2 Likes

感谢大佬的分享,最近要写一份MIDI的解码,查了好多资料,大佬说的最好懂

连成一块就看不懂了。。。。但是感谢大佬

感谢大佬的分享,最近要写一份MIDI的解码,查了好多资料,大佬说的最好懂

今天终于读到了《midi原理ykfyy》,把看这个文遇到的问题搞懂了一些
现在人在宿舍,书在图书馆,我凭记忆写一点吧。

每个midi消息的基本结构是
deltatime 状态字节 数据1 数据2
后三者各占1字节。
状态字节占1字节,写成二进制就是8个坑,即0000 0000。第1个坑恒为1,可能是为了标识这是个状态字节;2-4坑能表示0-7,对应8种消息类型,几种是系统消息,另几种是通道消息;5-8坑能表示0-15,决定这个消息发送到哪个通道。
对于数据1,数据2来说,第一位通常是0,应该是为了和状态字节区分吧,所以能表示的范围就是0-127了。具体姿势见下↓

【通道消息】两类:
音符。数据1填音符,数据2填力度(填0即关闭音符)
控制器。数据1填控制器号码,数据2填值

7 Likes

感谢大佬分享,解惑解惑

1 Like

感谢大佬分享,解惑解惑

感谢 , 受教了 。

谢谢大佬的分享!!

天天大神,膜拜一下~

第一次接触midi,兴奋

1 Like

真心不错。支持支持

感谢大佬感谢大佬

嗯,通道信息其实有很多,比如音符开关、乐器更改、音符触后、通道触后、音高轮和控制器。

1 Like