I'm trying to play multiple sounds simultaneously in Pygame. I have a background music and I want a rain sound to play continuously and play ocasional thunder sounds.
I have tried the following but my rain sound stops when thunder sound is playing. I have tried using channels but I don't know how to choose which channel to sound is playing from or if two channels can be played at once.
var.rain_sound.play()
if random.randint(0,80) == 10:
thunder = var.thunder_sound
thunder.play()
Thank you for your help
Pygame's find_channel function makes it very easy to play audio on an unused channel:
sound1 = pygame.mixer.Sound("sound.wav")
pygame.mixer.find_channel().play(sound1)
Note that by default, find_channel will return None if there are no free channels available. By passing it True, you can instead return the channel that's been playing audio the longest:
sound1 = pygame.mixer.Sound("sound.wav")
pygame.mixer.find_channel(True).play(sound1)
You may also be interested in the set_num_channels function, which lets you set the maximum number of audio channels:
pygame.mixer.set_num_channels(20)
You can only play one Sound at a time per channel, but you can play multiple channels at once. If you don't name the channels, pygame selects an unused channel to play a Sound; by default, pygame has 8 channels around. You can specify a channel by creating a Channel object. As for looping a sound indefinitely, you can do that by playing a sound with the argument loops = -1. You can find the documentation for these classes and methods at http://www.pygame.org/docs/ref/mixer.html
I would also suggest using the built-in module time, particularly the sleep() function that pauses execution for a specified time in seconds. This is because pygame.mixer functions that play a Sound return ASAP long before the sound finishes playing, and when you try to play a 2nd Sound on the same channel, the 1st Sound is stopped to play the 2nd. So, to guarantee that your thunder Sound is played to completion, it's best to pause execution while it's playing. I put the sleep() line outside the if-statement because inside the if-statement, the sleep() line won't pause execution if the thunder Sound isn't played, so the loop would just loop very quickly to the next thunder Sound, outputting far more frequently than "occasional."
import pygame
import random
import time
import var
# initialize pygame.mixer
pygame.mixer.init(frequency = 44100, size = -16, channels = 1, buffer = 2**12)
# init() channels refers to mono vs stereo, not playback Channel object
# create separate Channel objects for simultaneous playback
channel1 = pygame.mixer.Channel(0) # argument must be int
channel2 = pygame.mixer.Channel(1)
# plays loop of rain sound indefinitely until stopping playback on Channel,
# interruption by another Sound on same Channel, or quitting pygame
channel1.play(var.rain_sound, loops = -1)
# plays occasional thunder sounds
duration = var.thunder_sound.get_length() # duration of thunder in seconds
while True: # infinite while-loop
# play thunder sound if random condition met
if random.randint(0,80) == 10:
channel2.play(var.thunder_sound)
# pause while-loop for duration of thunder
time.sleep(duration)
Related
I have tried to repeat a song(mp3) using pygame module. The code is as follows from the site https://www.studytonight.com/tkinter/music-player-application-using-tkinter
When calling this function, it comes out only one time, of course it's natural.
def playsong(self):
# Displaying Selected Song title
self.track.set(self.playlist.get(ACTIVE))
# Displaying Status
self.status.set("-Playing")
# Loading Selected Song
pygame.mixer.music.load(self.playlist.get(ACTIVE))
# Playing Selected Song
pygame.mixer.music.play()
To repeat this sound file I added loop like this, but it plays just one time.
How can I repeat many times over and over this? Should I call the function playsound(self) many times I want to repeat?
def playsong(self):
for i in range(3):
# Displaying Selected Song title
self.track.set(self.playlist.get(ACTIVE))
# Displaying Status
self.status.set("-Playing")
# Loading Selected Song
pygame.mixer.music.load(self.playlist.get(ACTIVE))
# Playing Selected Song
pygame.mixer.music.play()
The first optional argument (loops) to pygame.mixer.music.play() tells the function how often to repeate the music.
For instance pass 1 to play the music twice (repeat once):
pygame.mixer.music.play(1)
Pass -1 to play the music infinitely:
pygame.mixer.music.play(-1)
See pygame.mixer.music.play():
play(loops=0, start=0.0, fade_ms = 0) -> None
This will play the loaded music stream. If the music is already playing it will be restarted.
loops is an optional integer argument, which is 0 by default, it tells how many times to repeat the music. The music repeats indefinately if this argument is set to -1.
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?
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.
This can be easily done with playsound.
playsound.playsound('alert.mp3', False)
Setting the second argument to False makes the function run asynchronously.
I'm trying to play multiple sounds simultaneously in Pygame. I have a background music and I want a rain sound to play continuously and play ocasional thunder sounds.
I have tried the following but my rain sound stops when thunder sound is playing. I have tried using channels but I don't know how to choose which channel to sound is playing from or if two channels can be played at once.
var.rain_sound.play()
if random.randint(0,80) == 10:
thunder = var.thunder_sound
thunder.play()
Thank you for your help
Pygame's find_channel function makes it very easy to play audio on an unused channel:
sound1 = pygame.mixer.Sound("sound.wav")
pygame.mixer.find_channel().play(sound1)
Note that by default, find_channel will return None if there are no free channels available. By passing it True, you can instead return the channel that's been playing audio the longest:
sound1 = pygame.mixer.Sound("sound.wav")
pygame.mixer.find_channel(True).play(sound1)
You may also be interested in the set_num_channels function, which lets you set the maximum number of audio channels:
pygame.mixer.set_num_channels(20)
You can only play one Sound at a time per channel, but you can play multiple channels at once. If you don't name the channels, pygame selects an unused channel to play a Sound; by default, pygame has 8 channels around. You can specify a channel by creating a Channel object. As for looping a sound indefinitely, you can do that by playing a sound with the argument loops = -1. You can find the documentation for these classes and methods at http://www.pygame.org/docs/ref/mixer.html
I would also suggest using the built-in module time, particularly the sleep() function that pauses execution for a specified time in seconds. This is because pygame.mixer functions that play a Sound return ASAP long before the sound finishes playing, and when you try to play a 2nd Sound on the same channel, the 1st Sound is stopped to play the 2nd. So, to guarantee that your thunder Sound is played to completion, it's best to pause execution while it's playing. I put the sleep() line outside the if-statement because inside the if-statement, the sleep() line won't pause execution if the thunder Sound isn't played, so the loop would just loop very quickly to the next thunder Sound, outputting far more frequently than "occasional."
import pygame
import random
import time
import var
# initialize pygame.mixer
pygame.mixer.init(frequency = 44100, size = -16, channels = 1, buffer = 2**12)
# init() channels refers to mono vs stereo, not playback Channel object
# create separate Channel objects for simultaneous playback
channel1 = pygame.mixer.Channel(0) # argument must be int
channel2 = pygame.mixer.Channel(1)
# plays loop of rain sound indefinitely until stopping playback on Channel,
# interruption by another Sound on same Channel, or quitting pygame
channel1.play(var.rain_sound, loops = -1)
# plays occasional thunder sounds
duration = var.thunder_sound.get_length() # duration of thunder in seconds
while True: # infinite while-loop
# play thunder sound if random condition met
if random.randint(0,80) == 10:
channel2.play(var.thunder_sound)
# pause while-loop for duration of thunder
time.sleep(duration)
In pygame is there a way to tell if a sound has finished playing? I read the set_endevent and get_endevent documentation but I can't make sense of it or find an example.
I'm trying to specifically do the following:
play a sound
While the sound is playing keep iterating on a loop
When the sound has finished playing, move on.
I did check the other questions that were asked - didnt find anything specifically targeting python / pygame.
Thanks!
The trick is that you get an object of type pygame.mixer.Channel when you call the play() method of a sound (pygame.mixer.Sound). You can test if the sound is finished playing using the channel's get_busy() method.
A simple example:
import pygame.mixer, pygame.time
mixer = pygame.mixer
mixer.init()
tada = mixer.Sound('tada.wav')
channel = tada.play()
while channel.get_busy():
pygame.time.wait(100) # ms
print "Playing..."
print "Finished."
The examples assumes you have a sound file called 'tada.wav'.
You can use some code like this:
import pygame
pygame.mixer.music.load('your_sound_file.mid')
pygame.mixer.music.play(-1, 0.0)
while pygame.mixer.music.get_busy() == True:
continue
And, it doesn't have to be a .mid file. It can be any file type pygame understands.
To use set_endevent you have to do something like this:
# You can have several User Events, so make a separate Id for each one
END_MUSIC_EVENT = pygame.USEREVENT + 0 # ID for music Event
pygame.mixer.music.set_endevent(END_MUSIC_EVENT)
running = True
# Main loop
while running:
events = pygame.event.get()
if events:
for event in events:
...
if event.type == END_MUSIC_EVENT and event.code == 0:
print "Music ended"
...
pygame.mixer.pre_init(44100, 16, 2, 4096) # setup mixer to avoid sound lag
pygame.init()
startmusic="resources\snd\SGOpeningtheme.ogg"
pygame.mixer.set_num_channels(64) # set a large number of channels so all the game sounds will play WITHOUT stopping another
pygame.mixer.music.load(startmusic)
pygame.mixer.music.set_volume(0.3)
pygame.mixer.music.play(-1) # the music will loop endlessly
once the above code is executed the music will continue to play until you stop it or fade out as per the pygame site info.
You can loop to your heart content and the music will continue. If you need the music to play twice, instead of -1 put 1. Then the music will PLAY once and REPEAT ONCE.
You can check if the stream is playing by doing pygame.mixer.music.get_busy().
If it returns True it is. False means it has finished.
How can I use the pyglet API for sound to play subsets of a sound file e.g. from 1 second in to 3.5seconds of a 6 second sound clip?
I can load a sound file and play it, and can seek to the start of the interval desired, but am wondering how to stop playback at the point indicated?
It doesn't appear that pyglet has support for setting a stop time. Your options are:
Poll the current time and stop playback when you've reached your desired endpoint. This may not be precise enough for you.
Or, use a sound file library to extract the portion you want into a temporary sound file, then use pyglet to play that sound file in its entirety. Python has built-in support for .wav files (the "wave" module), or you could shell out to a command-line tool like "sox".
This approach seems to work: rather than poll the current time manually to stop playback, use the pyglet clock scheduler to run a stop callback once after a given interval. This is precise enough for my use case ;-)
player = None
def stop_callback(dt):
if player != None:
player.stop()
def play_sound_interval(mp3File, start=None, end=None):
sound = pyglet.resource.media(mp3File)
global player
player = pyglet.media.ManagedSoundPlayer()
player.queue(sound)
if start != None:
player.seek(start)
if end != None and start != None:
pyglet.clock.schedule_once(stop_callback, end-start)
elif end != None and start == None:
pyglet.clock.schedule_once(stop_callback, end)
player.play()