如何获得MIDI事件的准确时间

我正在尝试读取一个MIDI文件,我想在C#中确定它的NoteOn事件的确切时间。
我试图使用绝对时间,但输出类似于256632。
这个号码是多少? 这是我的代码行返回时间:

(note as NoteOnEvent).AbsoluteTime 

MIDI文件仅包含增量时间。 在每个MIDI事件之前包含1到4个字节之间的可变长度值。 您正在使用的库有助于为您提供AbsoluteTime属性。 简单地计算每个事件的增量时间。

该单位是“delta ticks”。 增量刻度的长度不是固定值,它在MIDI文件头中指定。 NAudio将其公开为MidiFile.DeltaTicksPerQuarterNote属性。 因此,您需要将从AbsoluteTime获得的值除以此值,以从歌曲的开头获取音符位置。

这当然仍然是一个相对值,它取决于歌曲的节奏。 您打四分音符的比率。 建议的速度也包含在文件中,它是NAudio中的TempoEvent。 它的MicrosecondsPerQuarterNote属性告诉您后续事件的四分音符应该有多长。 请注意,歌曲中可能有多个速度事件。

三角洲倍

附加到MIDI文件中每个事件的时间是与前一个事件的偏移。 此偏移称为增量时间

正如Hans Passant所说的那样,它被存储在一个文件中,该事件是一种称为可变长度数量(VLQ)的特殊forms的事件。 是的,标准说VLQ表示必须符合32位(4字节)。 但是,从理论上讲(网上有一些真实的文件),更大的数字是可能的,所以如果提供MIDI文件解析的库使用比常规int更大的整数类型,那就更好了。 NAudio的AbsoluteTime是一个不错的选择。

绝对时间

AbsoluteTime属性只返回所有前面的delta-times和当前事件的一个的总和。

时间的意义

每个delta时间或绝对值实际意味着什么取决于MIDI文件的时间分割 。 有两种不同类型的时间划分:

  • 每季度滴答笔记
  • SMPTE时间分部

在第一种情况下,所有时间都表示为刻度数 。 例如,如果除法是96,则delta时间48定义八分音符的间隔。 正如Hans Passant所说,NAudio将其暴露为MidiFile.DeltaTicksPerQuarterNote属性

在第二种情况下,所有时间都以与SMPTE和MIDI时间码一致的方式表示为细分数。 在实践中,它是非常非常罕见的时分类型。

“可理解的”时间表示

我认为时间最“可理解”的格式是公制时间 (小时,分钟,秒)。 正如Hans Passant和CL所说,您需要通过MIDI文件了解速度的变化,以计算MIDi事件的度量时间。

有一些库可以为您进行此计算。 例如,使用DryWetMIDI,您可以使用以下代码获取事件的度量时间:

 MidiFile midiFile = MidiFile.Read("Some MIDI file.mid"); TempoMap tempoMap = midiFile.GetTempoMap(); MetricTimeSpan timeOf10thEvent = midiFile.GetTimedEvents() .Skip(9) .First() .TimeAs(tempoMap); 

使用此库,您还可以获得时间条形,节拍(使用BarBeatTimeSpan )或整个音符长度的一小部分(使用MusicalTimeSpan )。

音乐时间的计算使用时间签名的变化。 由MIDI文件的速度图提供的速度和时间签名更改都可以通过MidiFile GetTempoMap扩展方法获得。