I am trying to make a very simple metronome on a Raspberry Pi that plays a .wav file at a set interval, but the timing is audibly inaccurate. I really can't figure out why, is python's time module that inaccurate?
I don't think the code that handles playing the audio is the bottleneck since if I put it in a loop with no timer it will rattle consistently. With the simple code below, the sound will play on beat a few times and then one beat will be off randomly, over and over.
import pygame
from time import sleep
pygame.mixer.pre_init(44100, -16, 2, 2048)
pygame.mixer.init()
pygame.init()
BPM = 160
sound = pygame.mixer.Sound('sounds/hihat1.wav')
while True:
sound.play()
sleep(60/BPM)
I expect to get the sound to repeat every X milliseconds with an accuracy of at least +/-10ms or so. Is that unrealistic? If so please suggest an alternative.
the issue turned out to be using overly large chunk sizes which likely caused pygame to play sounds late as earlier chunks had already been queued. my first suggestion was that I'd expected the OP's code to slowly drift over time, suggesting that something like this would do better:
import pygame
from time import time, sleep
import gc
pygame.mixer.pre_init(44100, -16, 2, 256)
pygame.mixer.init()
pygame.init()
BPM = 160
DELTA = 60/BPM
sound = pygame.mixer.Sound('sounds/hihat1.wav')
goal = time()
while True:
print(time() - goal)
sound.play()
goal += DELTA
gc.collect()
sleep(goal - time())
i.e. keep track of the "current time" and adjust sleep
s according to how much time has elapsed. I explicitly perform a "garbage collect" (i.e. gc.collect()
) before each sleep to keep things more deterministic.