Sign up ×
Stack Overflow is a community of 4.7 million programmers, just like you, helping each other. Join them, it only takes a minute:

I've created a simple text-based game in Python which I'm using in conjunction with libPd (wrapper for Pure Data). All the game code was written before the audio was implemented and works as intended; similarly the libPd script itself also works perfectly on its own. However getting them to play nice together is proving to be tricky.

I assume it's to do with while loops and my usage of them.

Below is an extract from the game code -

    while True:

command = raw_input().lower()

    if command == "commands":
        print '"look around"'
        print '"explore"'
        print '"inventory"'
        print '"examine"'
        print '"take"'
        print '"combine"'
        print '"quit"'
    elif command == "look" or command == "look around":
        char.look()

...etc... ...etc...

While the libPd script on its own is as follows -

    while True:

if not ch.get_queue():
    for x in range(BUFFERSIZE):
            if x % BLOCKSIZE == 0:
                outbuf = m.process(inbuf)
            samples[selector][x][0] = outbuf[(x % BLOCKSIZE) * 2]
            samples[selector][x][1] = outbuf[(x % BLOCKSIZE) * 2 + 1]
    ch.queue(sounds[selector])
    selector = int(not selector)
libpd_release()

I originally tried indenting the entire game code within the libPd section but that caused the audio to only play once a command was typed, stopping once the print messages had been returned.

How do I go about combining the two so that the music is constant whilst the player is free to run through the rest of the commands/game?

share|improve this question
1  
sounds like you might be looking for multiple threads. One to have the game loop and antoher to play the music. – Pow-Ian Jul 12 '13 at 20:15
    
I suggest you take a look at the concepts of threading. Your audio will need to run in a different thread from your game code. Check out Python's threading module. – SamStudio8 Jul 12 '13 at 20:16

2 Answers 2

up vote 1 down vote accepted

Your problem is that you have to sit around waiting for raw_input() to return, but at the same time you have to keep processing audio messages off the queue as soon as they come in. How can you do both at the same time?


First, there's the event-loop style that you're using today.

If you can write a function that waits on either input or audio messages, whichever comes first you can rewrite your program around a loop that waits on that function. This is hard to do in general. (GUI frameworks and network server frameworks can help, but either would be a bit silly for your text game.)

You can fake it by only waiting a short time for each line, e.g., by using select.select on sys.stdin with a short timeout. But this is a lot of work, and it's hard to balance responsiveness and performance with a design like that.


Alternatively, you can use threads. Here's what it would look like:

def play_music():
    while True:
        if not ch.get_queue():
            for x in range(BUFFERSIZE):
                    if x % BLOCKSIZE == 0:
                        outbuf = m.process(inbuf)
                    samples[selector][x][0] = outbuf[(x % BLOCKSIZE) * 2]
                    samples[selector][x][1] = outbuf[(x % BLOCKSIZE) * 2 + 1]
            ch.queue(sounds[selector])
            selector = int(not selector)
        libpd_release()

play_music_thread = threading.Thread(target=play_music)
play_music_thread.daemon = True
play_music_thread.start()

while True:
    command = raw_input().lower()

    if command == "commands":
        print '"look around"'
        print '"explore"'
        print '"inventory"'
        print '"examine"'
        print '"take"'
        print '"combine"'
        print '"quit"'
    elif command == "look" or command == "look around":
        char.look()

If you want to be able to do clean shutdown, instead of just having the music thread killed in the middle of whatever it's doing when you quit, it's a bit more complicated… but not that much. Basically, with a Condition, Event, Queue, or just a boolean variable and a Lock, you can easily build a way to signal the background music thread from the main thread.

share|improve this answer
    
Thanks for taking the time to help me out. I tried the threading solution as you posted it but it was a no-go in terms of audio output. Am I missing something simple? There's an audible click as if it initializes briefly but nothing after that. I had a similar result whilst trying to combine the two initially before I posted this Question. – CapricornOne Jul 12 '13 at 23:24
    
@CapricornOne: Without seeing the rest of your code, it's hard to be sure… but I suspect that you're trying to share some variable between the music thread and some other code in the main thread—maybe selector is a shared global, or maybe ch.get_queue() is using a list instead of a Queue.Queue, or maybe you're trying to clear the queued-up buffer while the other thread is trying to play it, or…? – abarnert Jul 12 '13 at 23:49
    
In general, for any object that's used by two threads, you have to share a Lock as well, and then write with mylock: around each line or chunk or code that modifies or access the object. Self-synchronizing types like Queue and Lock, and immutable types like int and tuple, don't need this, but anything else generally does. – abarnert Jul 12 '13 at 23:50
    
I thought that "selector" was the issue originally but I'm just not sure. There also shouldn't be any crossover with the two threads as far as I can tell. If you'd like to take a look at the full code I've upped it here pastebin.com/mW8yKFYG – CapricornOne Jul 13 '13 at 0:05
    
Try moving the inbuf, ch, sounds, and samples variables all into the top of the play_music function, and also the stuff at the top of the file (BUFFERSIZE = 4096 through patch = libpd_open_patch). That ensures that you're not touching libpd or pygame at all from the main thread. – abarnert Jul 13 '13 at 0:10

The while True: loops in your code are "blocking", which means that while that loop is running then no other loop runs on the same thread. A simple example of running multiple threads is:

import threading
import time

class MyGame(object):

    def __init__(self):
        # an "event" is used to tell the threads when to stop
        # this is "thread safe", meaning it can be used by a number
        # of different threads without causing problems
        self.stop_event = threading.Event() 

        # create and start a thread to run your sound
        self.sound_thread = threading.Thread(target=self.sound, args=[self.stop_event])
        print "Starting sound thread"
        self.sound_thread.start()

        # create and start a thread to run your game
        self.game_thread = threading.Thread(target=self.game, args=[self.stop_event])
        print "Starting game thread"
        self.game_thread.start()

    def sound(self, stop_event):
        print "Entering sound thread"
        while not stop_event.is_set():
            # put the bit that used to be in the While True loop in here
            time.sleep(0.5)
        print "Exiting sound thread"

    def game(self, stop_event):
        print "Entering game thread"
        while not stop_event.is_set():
            # put the bit that used to be in the While True loop in here
            time.sleep(0.5)
        print "Exiting game thread"

    def stop(self):
        """Used to stop the threads from running"""
        print "Stopping threads"
        self.stop_event.set()

        self.sound_thread.join()
        print "Sound thread stopped"

        self.game_thread.join()
        print "Game thread stopped"

Have a look at the Python Threading documentation for more information - its very thorough. To run the program you would do something like:

game = MyGame()
time.sleep(2)
game.stop()

In your console you will see something like

>>> Starting sound thread
>>> Entering sound thread
>>> Starting game thread
>>> Entering game thread
>>> Stopping threads
>>> Exiting sound thread
>>> Sound thread stopped
>>> Exiting game thread
>>> Game thread stopped
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.