Question for devs: timing of midi playback

Are there any devs patient and willing to entertain a question from a programming noob?
In a DAW like Ardour, how is the timing of midi playback handled? Is this something that could be explained to a newbie such as myself? I’m not demanding a detailed explanation, I’m just hoping for a broad overview or any concepts that could point me in the right direction.

For example, I have tried 2 methods that seem to be too imprecise for accurate playback:

  1. I run a while loop, and in each iteration, if clock time >= ideal message time, I send the MIDI message. This method, in the way I implemented it, also had the disadvantage of eating up more processing power than I would think is necessary.
  2. I tried to schedule each midi message with an event scheduler. This was also too imprecise.

Any thoughts would be greatly appreciated,
Thanks!

Which OS are you using?

tl;dr: This is something an application should not directly concern itself with. It is abstracted by various driver APIs.

Inside Ardour all MIDI events are timestamped. Eventually, for each process cycle, the timestamp of the MIDI event is given in samples since cycle start.

Once the signal leaves Ardour it depends on the backend:

  • When using JACK, Ardour plays no further role, JACK MIDI events are timestamped using samples.
  • When using Coreaudio, Apple’s CoreMIDI handles sending the events at the correct time.
  • When using ALSA MIDI Sequencer, the kernel driver does the timing.
  • Windows’ WinMME also accepts timestamps, and plays the event at a scheduled time.
  • Only In case of ALSA raw, there is indeed a while() loop, not unlike the one you have described in (1). see ardour/alsa_rawmidi.cc at master · Ardour/ardour · GitHub
1 Like

If there are no messages, the thread can sleep (ALSA raw uses pthread_cond_wait).

If there is a message to be sent, but that is still far in the future, the thread can likewise sleep. In this case Ardour uses the select() system call. select sleeps at most the given time, and then the thread spins until the time is right to flush the data.

Note that sleep(), usleep() suspend execution of the calling thread at least the given time. So they are not practical here.

1 Like

Thank you so much for the detailed information! Your info has given me a lot to experiment with. I will play with the strategic sleep in the while loop, but I suspect the best method will be to hand off the duties to the drivers, as you explained in the first response.

I really appreciate your help.

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.