问题
Task is to get all notes and their time from MIDI file using NAudio library. So far I get all notes from file but I can't get their time.
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));
}
}
}
Questions:
1) To get just list of notes I just look in NoteOnEvents
or not? If I understand this correctly, each note has 'start' and 'end', start is defined by NoteOnEvent
and 'end' is defined by NoteOffEvent
. If I look in both events (NoteOn
and NoteOff
) I would get duplicate notes. Am I right?
2) How to get note's time? According to this post , I get some values but it seems that the first note's time is correct, but others don't. Also in this post, there is a comment which says the formula for calculating time must be:
((note.AbsTime - lastTempoEvent.AbsTime) / midi.ticksPerQuarterNote) * tempo + lastTempoEvent.RealTime.
I don't know parameters lastTempoEvent.RealTime
and tempo
. It's it tempo of last tempo event or?
3) Reading MIDI file it's very slow, for a smaller files it's ok, but for a big files don't. This small files have ~150 NoteOnEvents
and this bigger files have ~1250 NoteOnEvents
, which isn't so 'heavy'. Why is so slow?
回答1:
In MIDI files, a note has separate note-on and note-off events. NAudio already searches for the corresponding note-off event and calculates the length for you, so you don't need to handle note-off events yourself. (However, the tempo might change between the note-on and note-off events, so you have to compute the two times separately.)
These are descriptions of the values, not the actual field names.
tempo
is theMicrosecondsPerQuarterNote
value of the last tempo event.lastTempoEvent.RealTime
is the time (in microseconds) that you computed for the last tempo event.The last tempo event is the tempo event with the largest absolute time that is still before this event's absolute time. That tempo event is likely to be in another track, so it might be a good idea to merge all tracks (set
midi.Events.MidiFileType
to zero) before handling the events.
回答2:
You can take a look at other .NET libraries that provide MIDI file parsing. For example, with DryWetMIDI you can get all notes contained in a MIDI file with this code:
MidiFile file = MidiFile.Read("Great Song.mid");
IEnumerable<Note> notes = file.GetNotes();
Note
has Time
and Length
properties. Units of values returned by these properties defined by the time division of a MIDI file. But you can get time and length of a note in more understandable format. For hours, minutes, seconds you can write:
TempoMap tempoMap = file.GetTempoMap();
MetricTimeSpan metricTime = note.TimeAs<MetricTimeSpan>(tempoMap);
MetricTimeSpan metricLength = note.LengthAs<MetricTimeSpan>(tempoMap);
Using TimeAs
and LengthAs
methods you don't need to do any calculations by yourself. Instance of MetricTimeSpan
can be implicitly casted to TimeSpan
.
来源:https://stackoverflow.com/questions/23888692/reading-notes-from-midi-file-using-naudio