使用NAudio从MIDI文件中读取音符

时间:2014-05-27 11:51:09

标签: c# midi naudio

任务是使用NAudio库从MIDI文件中获取所有音符和时间。到目前为止,我从文件中得到了所有笔记,但我无法得到他们的时间。

            Note noteOn = new Note(); //custom class Note
            MidiFile midi = new MidiFile(open.FileName);
            List<TempoEvent> tempo = new List<TempoEvent>();

            for (int i = 0; i < midi.Events.Count(); i++)
            {
                foreach (MidiEvent note in midi.Events[i])
                {
                    TempoEvent tempoE;

                    try { tempoE = (TempoEvent)note; tempo.Add(tempoE); }
                    catch { }

                    if (note.CommandCode == MidiCommandCode.NoteOn)
                    {
                        var t_note = ( NoteOnEvent)note;

                        var noteOffEvent = t_note.OffEvent;

                        noteOn.NoteName.Add(t_note.NoteName);
                        noteOn.NoteNumber.Add(t_note.NoteNumber);
                        noteOn.NoteVelocity.Add(t_note.Velocity);
                        noteOn.NoteLenght.Add(t_note.NoteLength);

                        double d = (t_note.AbsoluteTime / midi.DeltaTicksPerQuarterNote) * tempo[tempo.Count() - 1].Tempo;

                        noteOn.StartTime.Add(TimeSpan.FromSeconds(d));
                    }

                }
            }

问题:

1)只获取NoteOnEvents或不查看的笔记列表?如果我理解正确,每个音符都有“开始”和“结束”,开始由NoteOnEvent定义,“结束”由NoteOffEvent定义。如果我查看这两个事件(NoteOnNoteOff),我会得到重复的备注。我是对的吗?

2)如何获得笔记的时间?根据{{​​3}},我得到了一些值,但似乎第一个音符的时间是正确的,但其他人没有。同样在这篇文章中,有一条评论说计算时间的公式必须是:

((note.AbsTime - lastTempoEvent.AbsTime) / midi.ticksPerQuarterNote) * tempo + lastTempoEvent.RealTime.

我不知道参数lastTempoEvent.RealTimetempo。这是最后一次节奏活动的节奏还是?

3)读取MIDI文件非常慢,对于较小的文件,没关系,但对于大文件则没有。这个小文件有~150 NoteOnEvents,这个更大的文件有~1250 NoteOnEvents,这不是那么“重”。为什么这么慢?

2 个答案:

答案 0 :(得分:6)

  1. 在MIDI文件中,音符具有单独的音符开和音符关事件。 NAudio已经搜索了相应的音符关闭事件并为您计算了长度,因此您不需要自己处理音符关闭事件。 (但是,速度可能会在音符开启和音符关闭事件之间发生变化,因此您必须分别计算两次。)

  2. 这些是对值的描述,而不是实际的字段名称。 tempo是最后一个节奏事件的MicrosecondsPerQuarterNote值。 lastTempoEvent.RealTime是您为最后一个速度事件计算的时间(以微秒为单位)。

    最后一个速度事件是具有最大绝对时间的速度事件,该事件仍然在此事件的绝对时间之前。 该节奏事件可能在另一个轨道中,因此在处理事件之前合并所有轨道(将midi.Events.MidiFileType设置为零)可能是个好主意。

答案 1 :(得分:0)

您可以查看提供MIDI文件解析的其他.NET库。例如,使用DryWetMIDI,您可以使用以下代码获取MIDI文件中包含的所有注释:

MidiFile file = MidiFile.Read("Great Song.mid");
IEnumerable<Note> notes = file.GetNotes();

Note具有TimeLength属性。由MIDI文件的时间分割定义的这些属性返回的值的单位。但是你可以用更易于理解的格式获得笔记的时间和长度。对于小时,分钟,秒,您可以写:

TempoMap tempoMap = file.GetTempoMap();
MetricTimeSpan metricTime = note.TimeAs<MetricTimeSpan>(tempoMap);
MetricTimeSpan metricLength = note.LengthAs<MetricTimeSpan>(tempoMap);

使用TimeAsLengthAs方法,您无需自行进行任何计算。 MetricTimeSpan的实例可以隐式投放到TimeSpan

相关问题