When I play a sound every 0.5 second with PyGame:
import pygame, time
pygame.mixer.init()
s = pygame.mixer.Sound("2.wav")
for i in range(8):
pygame.mixer.Channel(i).play(s)
time.sleep(0.5)
it doesn't respect the timing correctly at all.
It's like there are pause of 0.2 sec than 0.7 sec then 0.2 sec again, it's very irregular.
Notes:
I know that
time.sleep()
is not the most accurate in the world, but even with the more accurate solutions from here, the problem is still presentTested on a RaspberryPi
The problem is still there if I play many different files
s[i].play()
, with i in a big range. So the problem doesn't come from the fact it tries to replay the same file
Here is the reason:
Even if we decrease the audio buffer to the minimum supported by the soundcard (1024 or 512 samples instead of
pygame
's default 4096), the differences will still be there, making irregulat what should be a "metronome beat".I'll update with a working solution as soon as I find one. (I have a few ideas in this direction).
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 attributetime.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 fromrtmixer.Mixer.time
.