This project has moved. For the latest updates, please go here.

MIDI: optimal way to know which note is "on" at specified time?

Dec 31, 2014 at 5:29 AM
My current project is a program to play the audio to a song along with a carefully crafted MIDI file that represents the audio. The program reads a MIDI file for specific tracks and notes within those tracks. I use NAudio to do so. The value of each note I care about is stored in a List of the MIDINote class I created for this:
public class MIDINote
public int NoteNumber { get; set; }
public double NoteStart { get; set; }
public double NoteLength { get; set; }
public bool Played { get; set; }
The NoteStart and NoteLength are in seconds (the conversion from absolute time works beautifully to 5 decimal places, no need to worry).

Now here's the part I'm hoping you can show me how to do better.

I have a timer that among many other things increases the value of a counter. After increasing that value, I want to know exactly which notes, if any, are "on" at that point (i.e. either start exactly then, or have already started but still haven't passed). Then I do a variety of things with the notes.

The way I'm doing it, I iterate through each note in the track (i.e. each note I stored when I first read the MIDI), and go until the time matches the current counter time. Display that note. Then the timer ticks, counter increases, and repeat. But now we're repeating from the beginning of the track when counting, so while arriving at Note 1 in the track would be fast, I can assume the amount of iterations to get to Note 2000 is immense and probably redundant. This is the code snippet i'm talking about:

foreach (var note in track)
if (note.NoteStart + note.NoteLength < PlaybackSeconds || note.Played) continue;
if (note.NoteStart > PlaybackSeconds) break;
//do my stuff here with this note
note.Played = true;
Even with my limited knowledge of the inner workings of things, I can see that this is not the optimal way to manage this process. Is there a more efficient way to store and then access this information that would cut down on processing time?

My development machine is an Intel Core i7, 16GB of DDR3 RAM and a SSD - so things run very quickly on my end, albeit with a fraction of a second delay no matter what I've tried (even setting the timer to 50ms). But a friend testing the program on his end is getting as much as 4 second delay, which is absolutely unacceptable.

Any suggestions on how to improve this would be appreciated. Thanks!
Jan 9, 2015 at 10:26 PM
Even though that loop isn't particularly efficient, there is no way it should take 4 seconds even for several thousand notes.
I'd probably keep a running track of which notes were on though, so at each timer tick, you can simply see if the currently on notes have finished, and if the next note(s) have started.