创建 MIDI 文件并编码事件时间:为啥应该按时间均匀间隔的音符实际上会变慢?

Posted

技术标签:

【中文标题】创建 MIDI 文件并编码事件时间:为啥应该按时间均匀间隔的音符实际上会变慢?【英文标题】:Creating a MIDI file and encoding event times: why do notes that should be spaced uniformly time-wise actually slow down?创建 MIDI 文件并编码事件时间:为什么应该按时间均匀间隔的音符实际上会变慢? 【发布时间】:2016-12-22 10:51:50 【问题描述】:

我正在尝试编写一个简单的界面来创建 MIDI 文件。作为测试,我尝试创建一个播放大调音阶的文件,所有音符的长度都相同。我得到的文件如下(为了可读性而缩进)

4d 54 68 64 00 00 00 06 00 01 00 02 00 08
   4d 54 72 6b 00 00 00 0b
      00 ff 51 03 00 27 0f
      00 ff 2f 00
   4d 54 72 6b 00 00 00 54
      00 c0 00
      00 90 40 7f
      7d 80 40 7f
      7d 90 42 7f
      81 7a 80 42 7f
      81 7a 90 44 7f
      82 77 80 44 7f
      82 77 90 45 7f
      83 74 80 45 7f
      83 74 90 47 7f
      84 71 80 47 7f
      84 71 90 49 7f
      85 6e 80 49 7f
      85 6e 90 4b 7f
      86 6b 80 4b 7f
      86 6b 90 4c 7f
      87 68 80 4c 7f
      00 ff 2f 00

说明:第一行是文件头。第 2 行是磁道标题。 (在我的界面中,我为打击乐保留了一条轨道,同时也设置了速度。因为在这个例子中我没有打击乐,所以它不包含任何音符。)第 3 行设置速度,第 4 行结束轨道。第 5 行是另一个轨道标题。该曲目包含旋律。第 6 行为通道 0 设置乐器。接下来是通道 0 的 8 个交替音符开和 8 个音符关事件,然后是音轨结束。开始和结束音符的时间是:

00, 7d, 81 7a, 82 77, 83 74, 84 71, 85 6e, 86 6b, 87 68

据我了解,它们应该是均匀间隔的,因为对于事件时间,MIDI 使用 7 位字节格式,其中数字的长度是灵活的,并且除最后一个之外的所有字节都设置了 msnzb。所以 00 应该转换为 0, 7d 应该转换为 125, 81 7a 应该转换为 250,等等。但是由于某种原因,当你播放文件时,它在时间上听起来并不统一,而是变慢了。为什么会这样?我是否误解了对事件时间进行编码的正确方法,如果是,那么正确的方法是什么?或者我的文件是否存在其他问题导致该问题?

【问题讨论】:

【参考方案1】:

MIDI 文件中的时间戳是 delta-times - 您无需编码事件发生的时间,而是编码该轨道上连续事件之间的时间差。如果事件在时间上间隔均匀,则它们的增量时间应该相同。

来自标准:

MTrk 事件的语法非常简单:

<MTrk event> = <delta-time><event>

<delta-time> 存储为可变长度的数量。它表示下一个事件之前的时间量。如果轨道中的第一个事件发生在轨道的最开始,或者如果两个事件同时发生,则使用零增量时间。 Delta 时间总是存在的。 (不存储 0 的 delta-times 需要至少两个字节来存储任何其他值,并且大多数 delta-times 不为零。) Delta-time 是节拍的一部分(或一秒,用于使用 SMPTE 录制轨道次),如标头块中指定的那样。

参见例如http://www.music.mcgill.ca/~ich/classes/mumt306/StandardMIDIfileformat.html

【讨论】:

以上是关于创建 MIDI 文件并编码事件时间:为啥应该按时间均匀间隔的音符实际上会变慢?的主要内容,如果未能解决你的问题,请参考以下文章

解析 MIDI 文件以记录事件

为啥干净的 Midi 文件播放不同? (与米多)

iOS录制一个midi文件

如何合并轨道中的所有 Midi 事件?

Web midi API 和多个同步笔记

为啥 MIDI 音符计时不起作用?