How to play different sound consecutively and asynchronously in python?

0

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: playsound: has block option but will block the process(with block = True) or overlap the sound(block = False).

winsound: with SND_ASYNC option, say I have audio A and audio B(which needs to be played after audio A), if audio B is played, audio A will stop immediately.

vlc: [000001ba245794d0] mmdevice audio output error: cannot initialize COM (error 0x80010106), which is kind of weird and nothing useful was found on google. And this method seems not a good option to me and others. I use this simply for its is_playing() function and I can place the next sound to a queue.

So any advice? Thanks!

python
audio
playsound
winsound
asked on Stack Overflow Apr 14, 2020 by Dean Chong • edited Apr 14, 2020 by Dean Chong

2 Answers

1

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.

answered on Stack Overflow Apr 14, 2020 by Grismar • edited Apr 14, 2020 by Grismar
0

This can be easily done with playsound.

playsound.playsound('alert.mp3', False)

Setting the second argument to False makes the function run asynchronously.

answered on Stack Overflow May 16, 2021 by Wes

User contributions licensed under CC BY-SA 3.0