标准 MIDI 文件规范 1.0
本文转自 STANDARD MIDI FILES SPECIFICATION – MIDI.org,如有侵权,请联系删除。
发布者:
MIDI 制造商协会
洛杉矶,加利福尼亚
RP001
1996 年 2 月修订版
版权所有 © 1988, 1994, 1996 MIDI 制造商协会
保留所有权利。未经 MIDI 制造商协会书面许可,不得以任何形式或任何手段复制本文件的任何部分,包括信息存储和检索系统。
MMA
POB 3173
La Habra CA 90632-3173
引言
本文档概述了 MIDI 文件的规范。MIDI 文件的目的是为不同计算机上的程序之间提供带时间戳的 MIDI 数据交换方式。该设计的主要目标之一是实现紧凑的表示方式,使其非常适合基于磁盘的文件格式,但可能不适合用于需要快速访问的程序内存存储(可以在读取或写入文件时轻松转换为快速访问格式)。此格式不是为了取代任何程序的常规文件格式,尽管在需要时可以用于该目的。
MIDI 文件包含一个或多个 MIDI 流,并为每个事件提供时间信息。它支持歌曲、序列和轨道结构,以及速度和节拍信息。轨道名称和其他描述信息可以与 MIDI 数据一起存储。该格式支持多轨和多序列,使得支持多轨的程序在转换文件时能够使用该格式。
该规范定义了文件中使用的 8 位二进制数据流。数据可以存储在二进制文件中,经过特殊编码以提高 MIDI 传输效率,转换为十六进制 ASCII,或以符号化形式翻译为可打印的文本文件。本规范主要讨论 8 位数据流的内容,而不涉及 MIDI 文件在 MIDI 上的传输方法。
序列、轨道、块:文件结构
约定
在本文档中,位 0 表示字节的最低有效位,位 7 表示最高有效位。
在 MIDI 文件中,一些数字以称为“可变长度数量”的形式表示。这些数字以每字节 7 位的方式表示,最高有效位在前。除最后一个字节外,所有字节的第 7 位都设置为 1,最后一个字节的第 7 位设置为 0。如果数字在 0 到 127 之间,则以一个字节表示。
以下是以可变长度数量表示的数字示例:
数字(十六进制) | 表示形式(十六进制) |
---|---|
00000000 | 00 |
00000040 | 40 |
0000007F | 7F |
00000080 | 81 00 |
00002000 | C0 00 |
00003FFF | FF 7F |
00004000 | 81 80 00 |
00100000 | C0 80 00 |
001FFFFF | FF FF 7F |
00200000 | 81 80 80 00 |
08000000 | C0 80 80 00 |
0FFFFFFF | FF FF FF 7F |
允许的最大数字是 0x0FFFFFFF,因此在编写可变长度数字的例程中,该表示必须适合 32 位。理论上可以表示更大的数字,但在每分钟 500 拍的快速节奏下,以 96 分音符的两倍持续四天,足够长用于表示任何延迟时间!
文件
对于任何文件系统而言,MIDI 文件就是一系列 8 位字节。在 Macintosh 上,该字节流存储在文件的数据部分(类型为'Midi'),或在剪贴板上(数据类型为'Midi')。大多数其他计算机将 8 位字节流存储在文件中,命名或存储约定会根据需要定义。
块
MIDI 文件由块组成。每个块有一个 4 字符的类型和一个 32 位长度,表示块中的字节数。此结构允许设计新的块类型,当程序遇到在其开发之前引入的块类型时,可以轻松忽略。你的程序应当能够识别不熟悉的块并将其视为不存在。
每个块以 4 个 ASCII 字符的类型开始。然后是一个 32 位长度,最高有效字节在前(例如,长度为 6 表示为 00 00 00 06)。该长度指的是后续数据字节的数量,不包括类型和长度本身。因此,一个长度为 6 的块在磁盘文件中实际上占用 14 个字节。这种块结构类似于 Electronic Arts 公司的 IFF 格式,此处描述的块可以很容易地放入 IFF 文件中。但 MIDI 文件本身不是 IFF 文件:它不包含嵌套块,块的长度也不要求是偶数。将其转换为 IFF 文件只需填充奇数长度块,并将整个内容放入一个 FORM 块中。
MIDI 文件包含两种类型的块:头块和轨道块。头块提供有关整个 MIDI 文件的最小信息。轨道块包含最多 16 个 MIDI 通道的数据流。通过多个轨道块,可实现多轨、多 MIDI 输出、模式、序列和歌曲等概念。
MIDI 文件总是以头块开始,后跟一个或多个轨道块。
MThd <头数据长度>
<头数据>
MTrk <轨道数据长度>
<轨道数据>
MTrk <轨道数据长度>
<轨道数据>
...
块描述
头块
文件开头的头块指定了文件数据的基本信息。完整块的语法如下:
<头块> = <块类型> <长度> <格式> <轨道数> <时间单位>
如上所述,<块类型>
是 4 个 ASCII 字符'MThd';<长度>
是一个 32 位表示的数字 6(高位字节在前)。
数据部分包含三个 16 位字,以最高有效字节在前的顺序存储。
第一个字<格式>
指定了文件的总体组织形式。只有三个<格式>
值被定义:
- 0:文件包含一个多通道轨道。
- 1:文件包含一个或多个同时进行的轨道(或 MIDI 输出)。
- 2:文件包含一个或多个独立的单轨模式。
第二个字 <轨道数>
是文件中轨道块的数量。对于格式 0 的文件,它始终为 1。
第三个字 <时间单位>
指定了增量时间的含义。它有两种格式,一种用于度量时间,另一种用于基于时间码的时间:
如果
<时间单位>
的第 15 位是 0,则第 14 到 0 位表示构成四分音符的增量时间“刻度”数量。例如,如果<时间单位>
为 96,则文件中两个事件之间的时间间隔为八分音符时,增量时间为 48。如果
<时间单位>
的第 15 位是 1,文件中的增量时间对应于秒的细分,符合 SMPTE 和 MIDI 时间码的方式。第 14 到 8 位包含-24、-25、-29 或-30 之一,分别对应于四种标准的 SMPTE 和 MIDI 时间码格式(-29 对应于 30 刻度帧),表示每秒帧数。这些负数以二进制补码形式存储。第二个字节(正数)表示帧内的分辨率:典型值可能是 4(MIDI 时间码分辨率)、8、10、80(位分辨率)或 100。该系统允许精确指定基于时间码的轨道,但也可以通过指定 25 帧/秒和每帧 40 个单位的分辨率来实现基于毫秒的轨道。如果文件中的事件存储为 30 帧时间码的位分辨率,则<时间单位>
字应为 E250 十六进制。
格式 0、1 和 2
格式 0 文件包含一个头块,后跟一个轨道块。这是数据最具互换性的表示形式。对于需要简单单轨播放器的程序来说,它非常有用,这类程序主要用于让合成器发出声音,但同时关注其他功能,如混音器或音效设备。即使你的程序基于轨道,能够生成这种格式也很有价值,以便与这些简单程序一起使用。此外,或许有人会编写一种将格式 1 转换为格式 0 的工具,这可能在某些情况下非常有用,以节省在程序中实现此功能的时间。
格式 1 或 2 文件包含一个头块,后跟一个或多个轨道块。支持多个同时轨道的程序应能够保存和读取格式 1 的数据,这是一种纵向一维形式,即轨道的集合。支持多个独立模式的程序应能够保存和读取格式 2 的数据,这是一种横向一维形式。提供这些最小功能将确保最大化的互换性。
在使用带有计算机和 SMPTE 同步器的 MIDI 系统中,使用歌曲指针和时钟同步,节奏图(描述整个轨道的速度,并可能包括节拍信息,以便推导小节号)通常由计算机创建。为了在同步器中使用它们,必须将这些数据从计算机转移出去。为了便于同步器从 MIDI 文件中提取这些数据,速度信息应始终存储在第一个 MTrk 块中。对于格式 0 文件,速度将在轨道中分布,速度图读取器应忽略其中的中间事件;对于格式 1 文件,速度图必须存储在第一个轨道中。
对于格式 0 文件,至少应在单个多通道轨道的开头包含速度和节拍的元事件。对于格式 1 文件,这些元事件应包含在第一个轨道中。对于格式 2 文件,每个时间上独立的模式都应至少包含初始的速度和节拍信息。
我们可能会定义其他格式 ID,以支持其他结构。遇到未知格式 ID 的程序,仍可以读取文件中的其他 MTrk 块(如果用户可以理解它们并将其组织为某种结构)。此外,未来可能会向 MThd 块中添加更多参数:即使长度超过 6,程序也应读取并尊重该长度。
轨道块
轨道块(类型为 MTrk)是实际存储歌曲数据的地方。每个轨道块都是 MIDI 事件(以及非 MIDI 事件)的连续流,前面有增量时间值。轨道块的格式(如下所述)对于所有三种 MIDI 文件格式(0、1 和 2)完全相同。
MTrk 块的语法如下(+
表示“一个或多个”:至少应存在一个 MTrk 事件):
<轨道块> = <块类型> <长度> <MTrk事件>+
MTrk 事件的语法非常简单:
<MTrk事件> = <增量时间> <事件>
<增量时间>
以可变长度数量存储,表示在接下来的事件发生之前的时间量。如果轨道中的第一个事件发生在轨道的最开头,或两个事件同时发生,则使用增量时间 0。增量时间始终存在(不存储增量时间 0 时,任何其他值至少需要两个字节,而大多数增量时间不为零)。增量时间的单位为头块中指定的“刻度”。
<事件> = <MIDI事件> | <sysex事件> | <meta事件>
MIDI 事件 是任何 MIDI 通道消息。使用运行状态:如果前一个事件是相同状态的 MIDI 通道消息,可以省略 MIDI 通道消息的状态字节。每个 MTrk 块中的第一个事件必须指定状态。增量时间不是事件本身,而是 MTrk 事件语法的组成部分。请注意,运行状态在增量时间之间也适用。
sysex 事件 用于指定 MIDI 系统独占消息,既可以作为一个整体,也可以以数据包形式出现,或者作为“转义”来传输原本不合法的字节,例如系统实时消息、歌曲指针或选择、MIDI 时间码等。标准的完整系统独占消息在 MIDI 文件中以如下方式存储:
F0 <长度> <F0之后要传输的字节>
其中,长度以可变长度数量存储,表示后续字节的数量,不包括 F0 和长度本身。例如,要传输的消息 F0 43 12 00 07 F7 在 MIDI 文件中存储为 F0 05 43 12 00 07 F7。需要包括 F7,以便 MIDI 文件的读取器知道已读取完整的消息。
还有一种不暗示 F0 应传输的 sysex 事件形式。这种形式可以用作“转义”,以传输原本不合法的内容,如系统实时消息或其他特殊数据。此事件使用 F7 代码:
F7 <长度> <要传输的所有字节>
一些合成器制造商规定其系统独占消息需要分成小包传输。每个包只是整个系统独占消息的一部分,但其传输时间很重要。例如 CZ 补丁传输或 FB-01 的“系统独占模式”中使用的微音阶数据。F0 和 F7 sysex 事件可以组合使用,将语法完整的系统独占消息拆分为按时间传输的数据包。
F0 sysex 事件用于系列中的第一个包,传输时包含 F0。F7 sysex 事件用于后续包,不以 F0 开头(F7 不算作系统独占消息的一部分)。
一个语法完整的系统独占消息必须以 F7 结束,即使实际设备没有发送一个,以便在不查看 MIDI 文件中下一个事件的情况下知道已到达 sysex 消息的结尾。如果它存储为一个完整的 F0 sysex 事件,最后一个字节必须是 F7。如果它被分成多个包,最后一个包的最后一个字节必须是 F7。多包系统独占消息之间不得有可传输的 MIDI 事件。
多包系统独占消息的 MIDI 文件示例:假设要传输的字节为 F0 43 12 00,随后延迟 200 个刻度,再传输字节 43 12 00 43 12 00,再延迟 100 个刻度,最后传输字节 43 12 00 F7,这在 MIDI 文件中表示为:
F0 03 43 12 00
81 48 // 200个刻度的增量时间
F7 06 43 12 00 43 12 00
64 // 100个刻度的增量时间
F7 04 43 12 00 F7
在读取 MIDI 文件时,如果遇到没有前面的 F0 系统独占事件来开始多包系统独占消息序列的 F7 系统独占事件,应假设该 F7 事件作为“转义”使用。在这种情况下,不需要以 F7 结尾,除非希望 F7 被传输。
元事件 指定了对该格式或音序器有用的非 MIDI 信息,语法如下:
FF <类型> <长度> <数据>
所有元事件都以 FF 开头,接着是一个事件类型字节(始终小于 128),然后是以可变长度数量表示的数据长度,最后是数据本身。如果没有数据,则长度为 0。与块一样,未来可能会设计出尚未为现有程序所知的新元事件,因此程序必须正确忽略它们,并且预期会遇到它们。程序不得忽略未识别元事件的长度,并且不应因其大于预期而感到意外。如果发生这种情况,程序必须忽略它们已知内容之后的所有数据。然而,程序不得在元事件末尾添加任何自己的内容。
系统独占事件和元事件会取消当前生效的运行状态。运行状态不适用于这些消息,且不得用于它们。
meta 事件
以下是定义的几个 meta 事件,程序无需支持所有这些事件。
在每个 meta 事件的语法描述中,使用了一套约定来描述事件的参数。每个事件开始的 FF
,事件的类型,以及不含可变数据量的事件长度,均以十六进制直接表示。像 dd
或 se
这样的符号,由两个小写字母组成,表示 8 位的值。四个相同的小写字母(如 wwww
)表示 16 位值,以最高有效字节优先的顺序存储。六个相同的小写字母(如 tttttt
)表示 24 位值,也以最高有效字节优先的顺序存储。符号 len
表示 meta 事件语法中的长度部分,即一个数字,按可变长度数量存储,用以指定 meta 事件中其后的数据字节数。符号 text
和 data
表示由长度部分指定的任意数量的(可能为文本)数据字节。
一般来说,同一时间发生在轨道中的 meta 事件可以按任意顺序出现。如果使用版权事件,应尽早放置在文件中,以便于被注意到。序列号和序列/轨道名称事件(如果存在)必须出现在时间 0 处。轨道结束事件必须作为该轨道中的最后一个事件出现。
初步定义的 meta 事件包括:
FF 00 02 ssss
序列号可选事件,必须在轨道开头出现,在任何非零增量时间和任何可传输的 MIDI 事件之前。用于指定序列的编号。在格式 2 的 MIDI 文件中,用于标识每个“模式”,以便“歌曲”序列使用 Cue 消息引用这些模式。如果 ID 编号省略,则使用文件中序列的顺序位置作为默认值。在格式 0 或 1 的 MIDI 文件中,此编号应包含在第一个(或唯一)轨道中。如果需要传输多个多轨序列,则必须作为格式 1 文件组来实现,每个文件具有不同的序列号。
FF 01 len text
文本事件任何描述内容的文本。通常建议在轨道的开头放置一个文本事件,内容包括轨道的名称、预定的编排描述以及用户希望放入的任何其他信息。文本事件也可以在轨道的其他位置出现,用作歌词或描述提示点的文本。该事件中的文本应使用可打印的 ASCII 字符,以确保最大程度的互通性。然而,也可以使用带有高位的其他字符代码,用于不同程序之间在同一计算机上交换文件,这些程序支持扩展字符集。对于不支持非 ASCII 字符的计算机上的程序,应忽略这些字符。
Meta 事件类型 01 至 0F 保留用于各种类型的文本事件,每种文本事件都符合上述文本事件的规范,但用于不同的目的。
FF 02 len text
版权声明包含版权声明的可打印 ASCII 文本。声明应包括字符“(C)”、版权年份和版权持有人。如果一个 MIDI 文件中有多个音乐片段,所有的版权声明应集中在此事件中,以便在文件开头显示。此事件应为第一个轨道块中的第一个事件,时间为 0。
FF 03 len text
序列/轨道名称如果是格式 0 的轨道,或者格式 1 文件中的第一个轨道,则为序列的名称。否则,为轨道的名称。
FF 04 len text
乐器名称描述该轨道中将使用的乐器类型。可以与 MIDI 前缀 Meta 事件一起使用,指定此描述适用的 MIDI 通道,或者通道也可以作为文本直接写入事件中。
FF 05 len text
歌词要演唱的歌词。通常,每个音节将是一个独立的歌词事件,并在该事件的时间点开始。
FF 06 len text
标记通常在格式 0 轨道中,或者格式 1 文件中的第一个轨道。序列中该点的名称,例如排练字母或节名称(“第一段”,“副歌”,等)。
FF 07 len text
提示点描述电影、视频屏幕或舞台上在该音乐得分点发生的事件(例如,“车撞进房子”,“帷幕拉开”,“她扇了他的脸”,等)。
FF 20 01 cc
MIDI 通道前缀此事件中的 MIDI 通道(0-15)可用于将一个 MIDI 通道与随后的所有事件(包括系统独占和 meta 事件)关联。此通道“有效”直到遇到下一个正常的 MIDI 事件(包含通道信息)或下一个 MIDI 通道前缀 meta 事件。如果 MIDI 通道对应于“轨道”,此消息可帮助将多个轨道结合成一个格式 0 文件,同时保持它们的非 MIDI 数据与轨道关联。这种功能在 Yamaha 的 ESEQ 文件格式中也有类似实现。
FF 2F 00
轨道结束此事件是必需的。它指定轨道的确切结束点,以便精确指定轨道的长度,这对于需要循环或连接的轨道是必要的。
FF 51 03 tttttt
设置节奏(单位:微秒每 MIDI 四分音符)此事件指示节奏变化。另一种表达“微秒每四分音符”的方式是“每 MIDI 时钟的微秒数”。将节奏表示为每拍所需的时间,而不是每时间单位多少拍,能够实现与基于时间的同步协议(如 SMPTE 时间码或 MIDI 时间码)的绝对精确长期同步。这个节奏分辨率提供的精度使得一首 4 分钟的曲目以 120 拍每分钟的速度,能够在曲目结束时保持 500 微秒的准确度。理想情况下,这些事件应仅在 MIDI 时钟位置处发生——此约定旨在确保或至少提高与其他同步设备的兼容性,以便将此格式中存储的时间签名/节奏图轻松传输到其他设备。
FF 54 05 hr mn se fr ff
SMPTE 偏移如果存在此事件,它指定轨道块应开始的 SMPTE 时间。应出现在轨道的开头,即在任何非零增量时间之前,并且在任何可传输的 MIDI 事件之前。小时数必须以 SMPTE 格式编码,就像在 MIDI 时间码中一样。在格式 1 文件中,SMPTE 偏移必须与节奏图一起存储,在其他轨道中没有意义。ff 字段包含分数帧,以每帧 100 分之一为单位,即使在指定了不同帧细分的 SMPTE 轨道中,增量时间仍按此单位表示。
FF 58 04 nn dd cc bb
拍号拍号表示为四个数字。nn 和 dd 表示拍号的分子和分母,按照标记方式表示。分母是负的 2 的幂:2 代表四分音符,3 代表八分音符,依此类推。cc 参数表示每个节拍中的MIDI 时钟数。bb 参数表示 MIDI 认为的每个四分音符(24 个 MIDI 时钟)中有多少个标记的 32 分音符。之所以添加这一点,是因为已经有多个程序允许用户指定 MIDI 认为的四分音符(24 个时钟)应以或与其他东西相关的方式进行标记。
因此,表示 6/8 拍号的完整事件,其中节拍器每三拍八分音符点击一次,但每四分音符有 24 个时钟,每小节 72 个时钟,应该是(十六进制):
textFF 58 04 06 03 24 08
即 6/8 拍(8 是 2 的 3 次方,因此是 06 03),每个附点四分音符 36 个 MIDI 时钟(24 十六进制!),每个 MIDI 四分音符八个标记的 32 分音符。
FF 59 02 sf mi
调号sf = -7: 7 个降号
sf = -1: 1 个降号
sf = 0: C 大调
sf = 1: 1 个升号
sf = 7: 7 个升号
mi = 0: 大调
mi = 1: 小调FF 7F len data
音序器特定的元事件特定音序器的要求可以使用此事件类型:数据的第一个字节或字节序列为制造商 ID(通常为一个字节,或者如果第一个字节是 00,则为三个字节)。如同 MIDI 系统独占一样,定义该事件的制造商应发布其使用方法,以便他人理解如何使用它。这个事件类型可能被音序器用作唯一的文件格式,而具有特定功能的音序器则可能仍然使用标准特性。
程序片段和示例 MIDI 文件
以下是一些用于读取和写入 MIDI 文件中可变长度数字的示例代码。代码使用 C 语言,使用 getc
和 putc
函数从文件 infile
和 outfile
中读取和写入单个 8 位字符。
WriteVarLen (value)
register long value;
{
register long buffer;
buffer = value & 0x7f;
while ((value >>= 7) > 0)
{
buffer <<= 8;
buffer |= 0x80;
buffer += (value & 0x7f);
}
while (TRUE)
{
putc(buffer,outfile);
if (buffer & 0x80)
buffer >>= 8;
else
break;
}
}
doubleword ReadVarLen ()
{
register doubleword value;
register byte c;
if ((value = getc(infile)) & 0x80)
{
value &= 0x7f;
do
{
value = (value << 7) + ((c = getc(infile)) & 0x7f);
} while (c & 0x80);
}
return (value);
}
以下是该摘录的 MIDI 文件示例。首先展示的是格式 0 的文件,其中所有信息混合在一起;然后展示的是格式 1 的文件,其中所有数据分为四个轨道:一个用于节奏和拍号,三个用于音符。使用每四分音符 96 个“刻度”的分辨率。虽然 4/4 拍号和 120 的速度被隐含,但在此明确陈述。
这个例子所表示的 MIDI 流的内容在这里进行了分解:
增量时间 (十进制) | 事件代码 (十六进制) | 其他字节 (十进制) | 备注 |
---|---|---|---|
0 | FF 58 | 04 04 02 24 08 | 4 个字节:4/4 拍,24 个 MIDI 时钟/点击,8 个 32 分音符/24 个 MIDI 时钟 |
0 | FF 51 | 03 500000 | 3 个字节:500,000 微秒每四分音符 |
0 | C0 | 5 | 通道 1,程序变更 5 |
0 | C1 | 46 | 通道 2,程序变更 46 |
0 | C2 | 70 | 通道 3,程序变更 70 |
0 | 92 | 48 96 | 通道 3,音符开启#48,强奏 |
0 | 92 | 60 96 | 通道 3,音符开启#60,强奏 |
96 | 91 | 67 64 | 通道 2,音符开启#67,中强奏 |
96 | 90 | 76 32 | 通道 1,音符开启#76,钢琴 |
192 | 82 | 48 64 | 通道 3,音符关闭#48,标准 |
0 | 82 | 60 64 | 通道 3,音符关闭#60,标准 |
0 | 81 | 67 64 | 通道 2,音符关闭#67,标准 |
0 | 80 | 76 64 | 通道 1,音符关闭#76,标准 |
0 | FF 2F | 00 | 轨道结束 |
格式 0 的完整 MIDI 文件内容以十六进制表示如下:
首先是头块:
4D 54 68 64 MThd
00 00 00 06 块长度
00 00 格式 0
00 01 一个轨道
00 60 每四分音符96个刻度
然后是轨道块。它的头部,接着是事件(注意某些地方使用了运行状态):
4D 54 72 6B MTrk
00 00 00 3B 块长度 (59)
增量时间 | 事件 | 备注 |
---|---|---|
00 | FF 58 04 04 02 18 08 | 拍号 |
00 | FF 51 03 07 A1 20 | 节奏 |
00 | C0 05 | |
00 | C1 2E | |
00 | C2 46 | |
00 | 92 30 60 | |
00 | 3C 60 | 运行状态 |
60 | 91 43 40 | |
60 | 90 4C 20 | |
81 40 | 82 30 40 | 双字节增量时间 |
00 | 3C 40 | 运行状态 |
00 | 81 43 40 | |
00 | 80 4C 40 | |
00 | FF 2F 00 | 轨道结束 |
格式 1 的文件表示稍有不同。
首先是头块:
4D 54 68 64 MThd
00 00 00 06 块长度
00 01 格式 1
00 04 四个轨道
00 60 96 每四分音符96个刻度
然后是节奏和拍号轨道的轨道块。它的头部,接着是事件:
4D 54 72 6B MTrk
00 00 00 14 块长度 (20)
增量时间 | 事件 | 备注 |
---|---|---|
00 | FF 58 04 04 02 18 08 | 拍号 |
00 | FF 51 03 07 A1 20 | 节奏 |
83 00 | FF 2F 00 | 轨道结束 |
接下来是第一个音乐轨道的轨道块。此示例中使用了 MIDI 的音符开启/关闭运行状态约定:
4D 54 72 6B MTrk
00 00 00 10 块长度 (16)
增量时间 | 事件 | 备注 |
---|---|---|
00 | C0 05 | |
81 40 | 90 4C 20 | |
81 40 | 4C 00 | 运行状态:音符开启,力度 = 0 |
00 | FF 2F 00 | 轨道结束 |
然后是第二个音乐轨道的轨道块:
4D 54 72 6B MTrk
00 00 00 0F 块长度 (15)
增量时间 | 事件 | 备注 |
---|---|---|
00 | C1 2E | |
60 | 91 43 40 | |
82 20 | 43 00 | 运行状态 |
00 | FF 2F 00 | 轨道结束 |
接着是第三个音乐轨道的轨道块:
4D 54 72 6B MTrk
00 00 00 15 块长度 (21)
增量时间 | 事件 | 备注 |
---|---|---|
00 | C2 46 | |
00 | 92 30 60 | |
00 | 3C 60 | 运行状态 |
83 00 | 30 00 | 双字节增量时间,运行状态 |
00 | 3C 00 | 运行状态 |
00 | FF 2F 00 | 轨道结束 |
计算增量时间
下面是一个如何将累计的增量时间转换为毫秒的例子。在最简单的情况下,所需的两条信息是:
- SMF 头块定义了“division”(每四分音符的增量时钟数)。例如,96 = 每四分音符 96 个时钟(ppq)(参考:SMF 1.0,第 4 页)。
- 节奏设置,它是一个非 MIDI 数据 Meta 事件,通常出现在 SMF 的第一个轨道中的时间增量时间 0 处。如果未指定节奏,默认节奏为 120 bpm。节奏以每四分音符的微秒数表示。例如,500000 = 120 bpm(参考:SMF 1.0,第 5、9 页)。
要将增量时间的时钟转换为毫秒,您只需做一个简单的代数计算:
时间(毫秒) = (时钟数) * (节奏(微秒/四分音符) / 分辨率(时钟/四分音符)) / 1000
例如,如果 Set Tempo 值为 500000 微秒每四分音符,分辨率为每四分音符 96 个时钟,则在 SMF 中第 6144 个时钟的时间为:
时间 = 6144 * (500000 / 96) / 1000 = 32000 毫秒
上述例子是一个非常简单的情况。实际上,SMF 文件中可以包含多个 Set Tempo Meta 事件,这些事件分布在文件的各个位置,因此为了计算任何时钟点的正确经过时间,必须执行一个持续的计算。
请注意,尽管时间签名不需要进行上述计算,但如果想要计算特定小节/拍的经过时间,则需要使用时间签名。与 Set Tempo 更改类似,时间签名也可以在 SMF 中随时发生变化,通常需要进行持续的计算以确定任何小节/拍的正确经过时间。