How do I stop Simpleaudio from playing a file twice simulaneously?

1.3k Views Asked by At

I am using simpleaudio in a text-based adventure game. I am using Python 3. In my main loop, I want to achieve this: play a short audio clip, and once it finishes, start playing the same audio clip again. My problem is, that I cannot figure out how to make Simpleaudio do this.

I have read all of the API documentation to no avail. I have tried many different things regarding loops, and none of them work.

import simpleaudio as sa

def audio(audiofile):
    filename = "/Users/*********/Desktop/Programming/Python Files/Adventure Game/Audio Files/" + audiofile
    wave_obj = sa.WaveObject.from_wave_file(filename)
    play_obj = wave_obj.play()

A = True
while True:
audio("openingtune.wav")
clear_screen()
firstinput = input("> ")
user_input1(firstinput)

# I didn't include the other functions because they're not relevant to this. 

Note: the "while true" loop refreshes every so often (after input has been taken, and output has been given). I have no idea how to do this: once the song has finished playing one time, it should start over at the same point.

2

There are 2 best solutions below

0
droptop On BEST ANSWER

Contrary to the suggestions, simpleaudio doesn't play the audio on the main thread. Per the docs..

The module implements an asynchronous interface, meaning that program execution continues immediately after audio playback is started and a background thread takes care of the rest. This makes it easy to incorporate audio playback into GUI-driven applications that need to remain responsive.

The while loop should simply check if the thread has completed and play the audio again if it has.

import simpleaudio as sa


class AudioPlayer:
    def __init__(self):
        self.play_obj = None

    def play(audiofile):
        filename = "/Users/*********/Desktop/Programming/Python 
                    Files/Adventure Game/Audio Files/" + audiofile
        wave_obj = sa.WaveObject.from_wave_file(filename)
        self.play_obj = wave_obj.play()
    
    def is_done():
        if self.play_obj:
            return not self.play_obj.is_playing()
        return True

player = AudioPlayer()
while True:
    clear_screen()
    if player.is_done():
        player.play("openingtune.wav")
    ...

If you're continuously playing the same file you might want to consider just passing the file string to constructor once and for all. Strings are immutable, so the string in the player.play() call is created and passed by copy on every iteration of the loop, which is nuts if it's always the same string.

1
Lukasz Tracewski On

Your current approach does not work because you play the audio on the main thread. It starts to play audio as intended on the first go, but then there's a sequence of actions after audio, i.e. all user input. In the meantime, the audio might stop playing and won't be executed until the next pass of the loop.

Solution: start audio on a separate thread.

Code:

import simpleaudio as sa
from threading import Thread

def audio(audiofile):
    filename = "/Users/*********/Desktop/Programming/Python Files/Adventure Game/Audio Files/" + audiofile
    wave_obj = sa.WaveObject.from_wave_file(filename)
    while(True):
        play_obj = wave_obj.play()

# Let's play the music on a separate thread
audio_thread = Thread(target=audio, args=("openingtune.wav",))
audio_thread.start()

# And now the main game loop
A = True
while True:
    clear_screen()
    firstinput = input("> ")
    user_input1(firstinput)

Probably you will want to introduce some controls to stop playing audio.