0

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.

Caspian Ahlberg
  • 934
  • 10
  • 19
  • 1
    Do I understand correctly that the song should be played in the loop regardless of user actions? Currently `audio` sits on the main thread and will wait for `input` and other functions in the loop to complete. – Lukasz Tracewski Oct 26 '19 at 06:29
  • Yes. Regardless of how users move, the song should keep playing. – Caspian Ahlberg Oct 27 '19 at 01:00
  • 1
    Did the solution work / help you? I noticed you have never accepted any answer, consider reading https://stackoverflow.com/help/someone-answers – Lukasz Tracewski Oct 29 '19 at 15:33
  • Lukasz - it never worked. I'm not sure if you saw me say so the first time, but just wanted to let you know. – Caspian Ahlberg Nov 19 '19 at 02:19

2 Answers2

0

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.

Lukasz Tracewski
  • 10,794
  • 3
  • 34
  • 53
  • This did not work for me. It did this weird thing where it played a distorted version of the audio. It sounded like it was playing itself layered many times, and I don't know why. Please tell me if you have the same problem. – Caspian Ahlberg Oct 29 '19 at 16:14
0

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.

droptop
  • 1,372
  • 13
  • 24