Search code examples
pythonraspberry-pipygametimingpyaudio

Python: Inaccurate timing playing audio at a set BPM interval (<1s)


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.


Solution

  • 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 sleeps 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.