Search code examples
pythonaudiopygameraspberry-pibluetooth

Audio playback drops the beginning of the sound when played in a loop


I am trying to play an audio file in a loop (this example repeats it 3 times). The audio file is very short. It is just someone saying "Hello, world".

import pygame


def wait_for_player():
    while pygame.mixer.music.get_busy():
        continue


def play_file(filename):
    pygame.init()
    pygame.mixer.music.load(filename)   
    for i in range(3):
        pygame.mixer.music.play()       
        wait_for_player()


if __name__ == '__main__':
    play_file('hello_world.mp3')

What I hear sounds like this:

"Hello, world"
"o, world"
"o, world"

That is, the audio is clipped briefly at the beginning on subsequent replays. On a different device the same code and the same audio file sounds like this:

"o, world"
"Hello, world"
"Hello, world"

Each of these devices used bluetooth - one on a Raspberry Pi, the other on a Windows laptop. I tried again on a couple other devices without bluetooth and each time the audio played correctly. So maybe it's a bluetooth problem.

I tried padding a second or two of silence before the "hello", but it made no difference (except for spacing out the timing between each play - the audio was still clipped as it was without the padded silence).

My next step is to pad with a (hopefully) imperceptible bit of audio to "prime the pump", but I would rather have code that works than a workaround. Is there anything I can do programatically to ensure the entire sound plays each and every time?

ETA: Here is my workaround. I copied this gist into tone.py. It wraps around mixer.Sound to make it easy to play a note. The modified code below plays a low frequency note at low volume. This is enough to "prime the pump" and it now works on all devices. It's still a workaround, so hopefully someone will have a better answer - but I can go live with this if I have to.

from time import sleep
import pygame
from tone import Note


def wait_for_player():
    while pygame.mixer.music.get_busy():
        continue


def play_file(filename):
    pygame.init()
    Note(0.08, volume=0.01).play(-1)
    sleep(1)
    pygame.mixer.music.load(filename)
    pygame.mixer.music.play(loops=3)
    wait_for_player()


if __name__ == '__main__':
    play_file('hello_world.mp3')

Solution

  • Here is my workaround. I copied this gist into tone.py. It wraps around mixer.Sound to make it easy to play a note. The modified code below plays a low frequency note at low volume. This is enough to "prime the pump" and it now works on all devices. It's still a workaround, so hopefully someone will have a better answer - but I can go live with this if I have to.

    from time import sleep
    import pygame
    from tone import Note
    
    
    def wait_for_player():
        while pygame.mixer.music.get_busy():
            continue
    
    
    def play_file(filename):
        pygame.init()
        Note(0.08, volume=0.01).play(-1)
        sleep(1)
        pygame.mixer.music.load(filename)
        pygame.mixer.music.play(loops=3)
        wait_for_player()
    
    
    if __name__ == '__main__':
        play_file('hello_world.mp3')