As you wrote in your own answer, the reason for the timing problems very likely is the fact that the audio callback runs decoupled from the rest of the application.
The audio backend typically has some kind of a clock which is accessible from both inside the callback function and outside of it.
I see two possible solutions:
use a library that allows you to implement the callback function yourself, calculate the starting times of your sounds inside the callback function, compare those times with the current time of the "audio clock" and write your sound to the output at the appropriate position in the output buffer.
use a library that allows you to specify the exact time (in terms of the "audio clock") when to start playing your sounds. This library would do the steps of the previous point for you.
For the first option, you could use the sounddevice module. The callback function (which you'll have to implement) will get an argument named time
, which has an attribute time.outputBufferDacTime
, which is a floating point value specifying the time (in seconds) when the first sample of the output buffer will be played back.
Full disclosure: I'm the author of the sounddevice
module, so my recommendation is quite biased.
Quite recently, I've started working on the rtmixer module, which can be used for the second option.
Please note that this is in very early development state, so use it with caution.
With this module, you don't have to write a callback function, you can use the function rtmixer.Mixer.play_buffer()
to play an audio buffer at a specified time (in seconds). For reference, you can get the current time from rtmixer.Mixer.time
.