pythonaudiopython-vlcwinsoundpython-playsound

How to play different sound consecutively and asynchronously?


To be specific, I need to play sound in a while loop which is fast to execute. And the audio needs to be played separately. I've tried various functions/libraries: playsound, winsound, vlc. But none of them meet my demand.

Either the sounds are overlapped, or I need to wait for the sound to finish to continue the next line of code, which blocks the whole process, making the program running with unbearable lags.

Matters in playsound, winsound, vlc:

So any advice?


Solution

  • If you're just after playing a sound and having it interrupt whatever was playing before, winsound does just that:

    import winsound
    from time import sleep
    
    winsound.PlaySound("sound.wav", winsound.SND_ASYNC | winsound.SND_FILENAME)
    sleep(1)
    winsound.PlaySound("sound.wav", winsound.SND_ASYNC | winsound.SND_FILENAME)
    sleep(3)
    

    In this example (assuming sound.wav is longer than a second), the sound will start playing, be interrupted after 1 second and start playing again. The second sleep is there to avoid the script ending before the sound stops (stopping the script stops the sound).

    If you want to queue up sounds to play one after the other, while your code keeps running:

    import threading
    import queue
    import winsound
    from time import sleep
    
    q = queue.Queue()
    
    
    def thread_function():
        while True:
            sound = q.get()
            if sound is None:
                break
            winsound.PlaySound(sound, winsound.SND_FILENAME)
    
    
    if __name__ == "__main__":
        t = threading.Thread(target=thread_function)
        t.start()
        q.put("sound.wav")
        print('One...')
        sleep(1)
        q.put("sound.wav")
        print('Two...')
        sleep(1)
        q.put("sound.wav")
        print('Three...')
        q.put(None)
        t.join()
    

    This simple example queues up a sound which the thread starts playing, then while it is playing, it queues up the next one and a bit later a third. You'll noticed that the sounds play one after the other and the program only stops when the sounds complete playing (and the thread stops due to the None at the end of the queue).

    If you're looking to play one sound over the other and have them mix, using winsound won't work, but you can use libraries like pyglet.

    For example:

    import pyglet
    
    
    window = pyglet.window.Window()
    effect = pyglet.resource.media('sound.wav', streaming=False)
    
    
    @window.event
    def on_key_press(symbol, modifiers):
        effect.play()
    
    
    @window.event
    def on_draw():
        window.clear()
    
    
    if __name__ == "__main__":
        pyglet.app.run()
    

    This example opens a window and every time you press a key, it'll play the sound immediately, without interrupting previous sounds. The program ends immediately when you close the window.