I have a hobby project: building a sequencer in Python.
Goal is: to be able to steer external MIDI devices from my laptop.
I am running the script on Linux and for connecting to the synthesizers I am using an USB-MIDI cable.
(and for the time being I am putting aside the questions of timing precision of MIDI or latency of USB cable ...)
The issue where I a have some uncertainties is the clock running in python. I need to play notes on the external devices in given rhythm pattern (that's the purpose of the sequencer) and for that I need to trigger the MIDI note in a precise moment. Currently I have only a very simple implementation using steady row of 'beats' per minute. That means, I am running a script in a thread which every x miliseconds triggers some method which then takes care of playing correct notes on assigned devices.
How do I best achieve this steady pattern of beats, with high timing precision?
My current implementation is a combination of sleep() and busy waiting, like this:
while self._running:
while time.perf_counter() < nextTime:
# we are not yet at the next beat, so we wait:
diff = nextTime - time.perf_counter()
if diff > (self.defaultMargin * self.defaultSleep):
time.sleep(self.defaultSleep)
# now we have reached the nextTime moment
# so we execute the MIDI calls etc ...
self.__execute()
nextTime += self._wait
Now, I am curious about two things:
- The processor is now switching from idle to 100% multiple times a second. It is indeed audible, as a clicking coming out of my laptop, in the rhythm of the music. My concern is, is this kind of usage healthy for the processor?
- And more importantly, is this kind of design good at all?
What I gathered so far is that sleep() is not guaranteed to sleep exactly for given time, it could be rather longer; and on top of that behaving differently on Linux and Windows. That's why I am combining it with busy waiting, to reach the precise timing. But it does not feel neither efficient, nor elegant.
Can anybody give me an idea how I could solve this timing better? All the DAWs and similar software must be solving this somehow.
BTW: I am writing it in Python but would not mind going deeper to C if it would be necessary.
Every advice would be appreciated. Thanks!