generally I'm all in favour of cleaning up the sequencer code. Below are just some ideas and smaller concerns that came to my mind while reading your post:
Am Mo., 9. Dez. 2019 um 19:46 Uhr schrieb Tom M. via fluid-dev <address@hidden
2. A meaningful ordering?
As per MIDI standard, events are ordered by tick count. If two events share the same tick count, the order is undefined. In such a case almost all MIDI players out there fall back to the order the events appear in the "stream" (e.g. file). IMO, this doesn't make sense. Let me remind you of an issue , where a progChange and a NoteOn happen at the same tick. Question: Which instrument will the NoteOn trigger? The new program or the old program?
Hm... I disagree. I think it makes perfect sense to fall back to the order of events in the stream. It's closer to the way real-time MIDI messages over a serial connection work. And if I understood the problem in  correctly, then the issue occurred because the program change events where in the wrong track. So the abc to midi converter was to blame, not the synth playing the MIDI file.
To overcome this problem I'm suggesting to implement an ordering function which makes sure that NoteOn events are always last (within the same tick count).
As you say: almost all MIDI players fall back to the event order, so why shoudl Fluidsynth be different? Why is "Note-On last" the better order? How do you know that that special (and seemingly Fluidsynth specific) ordering is what the author of the MIDI file wanted?
3. Simpler implementation?
I just had a quick try using C++ std::priority_queue. The results are very promising in terms of performance. Whereas the UI thread of my program previously was stuck in  for seconds, with the C++ implementation I'm unable to notice any delay.
Simpler and faster implementation sounds great, IMO! What trips me up though: if your program was really stuck in that loop for seconds, I'm wondering how many events you were actually adding to the sequencer... Millions? And if you really are adding that many events... could you explain why?
5. The system timer
I see no reason to retain support for the system timer. The only time source for dispatching events at the right time should be the sample timer. Whether this actually happens in real-time or not, depends on whoever calls the rendering functions of the synth, e.g. audio driver vs. fast-file-renderer.
Just thinking out loud and I'm not sure if this actually applies here... but isn't there a global timing event that MIDI devices can send to each other to synchronise playback position and playback speed? So if we would ever support that, wouldn't we need an adjustable timing source? But maybe that only applies to real-time MIDI playback, not events read from a file. Not sure...
6. Time Scale
Also, I see no reason to keep the time scaling. The default scale is 1 tick per milisecond. There is no way to get a higher resolution, you can only make the resolution worse. Why would you want to do this? (Perhaps in case of the system timer, to avoid that it fires a callback every milisecond. But this is not a reason anymore, see 5.)
Similar comment as above and again, just thinking out loud: wouldn't it be a great feature to allow slower or faster MIDI playback?
To address those issues I see two ways to go:
1. Next to the current sequencer implementation, add a new C++ implementation for the sequencer and decide at cmake time whether to use the old pure C or the new C++ implementation.
2. Replace the current implementation of the sequencer with some heavy glib usage.
Any opinions or comments highly welcome.
I'm all for cleaning up the code base, but adding another implementation in C++ is not cleanup IMO... it adds more technological debt because we now have to keep two different implementations in sync. So personally I would vote to have only a single implementation and don't really care if it is C or C++. But I do wonder: what can C++ do that C can't? And especially: why can C++ do it faster?