Playing music with Pygame unreliable - python

I'm trying to write a simple program to play music files with Pygame. My script is below.
import pygame
import sys
import time
FRAMERATE = 30
if len(sys.argv) < 2:
sys.exit(2)
filename = sys.argv[1]
clock = pygame.time.Clock()
pygame.init()
pygame.mixer.init(frequency=44100)
pygame.mixer.music.load(filename)
print "%s loaded!" % filename
pygame.mixer.music.play(1)
while pygame.mixer.music.get_busy():
clock.tick(FRAMERATE)
But I'm having some puzzling problems. The "[File name] loaded!" message always prints, but sometimes it never enters the loop and exits immediately. If I check on the status of pygame.mixer.music.get_busy(), it appears to be false immediately after the pygame.mixer.music.play(1) command. This happens erratically; I just tried running the program with no changes to the code, having it work once and encounter this problem once right afterward. Does anyone know what could be causing these seemingly random playback problems?

I imagine this is because the actual music playback is happening on another thread, so it sometimes hasn't finished starting at the point you first call get_busy().
If that's the case, it seems like a bug in either pygame or SDL_mixer (which pygame uses.)
As an alternative way of checking for music completion, you can get pygame to give you an event when the music finishes and check for that. Like this:
pygame.mixer.music.set_endevent(pygame.USEREVENT)
quit = False
while not quit:
clock.tick(FRAMERATE)
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit = True
if event.type == pygame.USEREVENT:
print "Music ended"
quit = True

Related

why does pygame.display.update stop working after being called a set number of times?

When running I am blit'ing some text onto my surface (WIN) and I want this to be shown in the game so I call pygame.display.update to update the display, this works perfectly fine until the loop has iterated around 75 times and after this the display stops updating. Code below
def dialogue(x,y,text):
global clock
global run
typing=True
tempstring=""
counter=0
switcher=0
for z in range(0,(len(text))):
clock.tick(15)
tempstring=text[0:z+1]
counter+=1
print(counter)
print("temp is "+tempstring)
writing=TITLE_FONT.render(tempstring, 1, (255,255,255),(0,0,0))
if x==-1 and y>-1:
pygame.display.update(WIN.blit(writing, (640-(writing.get_width()//2),y-(writing.get_height()//2))))
elif x>-1 and y==-1:
pygame.display.update(WIN.blit(writing, (x-(writing.get_width()//2),360-(writing.get_height()//2))))
elif x==-1 and y==-1:
pygame.display.update(WIN.blit(writing, (640-(writing.get_width()//2),360-(writing.get_height()//2))))
else:
pygame.display.update(WIN.blit(writing, (x-(writing.get_width()//2),y-(writing.get_height()//2))))
print(typing)
while True:
event = pygame.event.wait()
if event.type == pygame.QUIT:
run=False
pygame.quit()
break
if event.type == pygame.KEYDOWN:
break
I have tried just using pygame.display.update() at the end of the line of if statements too but this also fails.
On some systems PyGame doesn't work correctly if you don't get pygame.event from system - system may think that program hangs and it may even close it.
You may need to use pygame.even.get() (or similar functions) inside your loop.
In doc pygame.event you can find (but it is hidden in long description)
To prevent lost events, especially input events which signal a quit command, your program must handle events every frame (with pygame.event.get(), pygame.event.pump(), pygame.event.wait(), pygame.event.peek() or pygame.event.clear()) and process them.
Not handling events may cause your system to decide your program has locked up.
To keep pygame in sync with the system, you will need to call pygame.event.pump() internally process pygame event handlers to keep everything current.

When I close PyGame window the window closes but python becomes non responsive

I wrote a game with pygame. The game works fine but when I quit and close the window, while the window does close Python itself does not quit and instead shows (not responding) and I am forced to, well, force quit.
I start the code by importing several modules
import random, pygame, sys
from pygame.locals import*
There is only one place in the code that directly commands the program to shut down
def terminate():
pygame.quit()
sys.exit()
To quit the program always runs the terminate() function.
I tried adding pygame.display.quit() above pygame.quit() and substituting raise SystemExit for sys.exit()
What could cause python to close the window but not shut down itself?
I personnay don't imoprt sys to exit. It simply isn't effective for me to type sys.exit() and you can simply call exit() to exit the program.
So you can close the window like that:
# main loop
running = True
while running:
for event in pygame.event.get():
if event.type == QUIT: # if the user closes the window
running = False
# after exiting mail loop
pygame.quit()
exit()
[EDIT] According to this answer, you should prefer using quit() instead of exit():
However, like quit, exit is considered bad to use in production code and should be reserved for use in the interpreter. This is because it too relies on the site module.
I don't get any problems now that I use quit().

In pygame (or even in python in general), how would I create a loop that keeps executing while a sound is playing?

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.

Issue with sys.exit() in pygame

I am learning to use Pygame, and when I use sys.exit(), I run into a problem. Here is the code:
import pygame, sys,os
from pygame.locals import *
pygame.init()
window = pygame.display.set_mode((468, 60))
pygame.display.set_caption('Game')
screen = pygame.display.get_surface()
file_name = os.path.join("data","image.bmp")
surface = pygame.image.load(file_name)
screen.blit(surface, (0,0))
pygame.display.flip()
def input(events):
for event in events:
if event.type == QUIT:
sys.exit(0)
else:
print event
while True:
input(pygame.event.get())
It's really just the code from the pygame tutorial. The problem occurs when I actually try to exit, regardless of what event I try to use to sys.exit().
Traceback (most recent call last):
File "C:/Python27/Lib/site-packages/pygame/examples/test.py", line 25, in <module>
input(pygame.event.get())
File "C:/Python27/Lib/site-packages/pygame/examples/test.py", line 20, in input
sys.exit(0)
SystemExit: 0
... And then the program doesn't exit. What am I doing wrong here? Because I did notice that this code was for an antiquated version of Python.
sys.exit()
alone is a bit unholy with pygame.. the proper way to exit a pygame app is to first break out of the mainloop then quit pygame then quit the program. ie.
while running == True:
# catch events
if event_type == quit:
running = False # breaks out of the loop
pygame.quit() # quits pygame
sys.exit()
also it seems to me that you aren't catching the event properly.. it should be
if event.type == pygame.QUIT:
you can read more about events in pygame here.
sys.exit just throws an exception (a SystemExit exception). This has two unusual effects:
It only exits the current thread in a multithreaded application
The exception could have been caught.
I solved this problem and the right code is below:
running = True
while running == True:
for event in pygame.event.get():
if event.type == QUIT:
running = False # Exiting the while loop
screen.blit(background, (0,0))
pygame.display.update()
pygame.quit() # Call the quit() method outside the while loop to end the application.
I have read in some sources there is a conflict between the mainloop() in Tkinter which runs the Python shell and Pygame.init() which the sys.exit() command follows.
The suggestion was to run the game from the command line to get round the problem rather than load the game using run (F5) from the shell.
A good side effect of this was that in my space invaders game, which does a lot of variable updating: 35 times a second, was that the animation ran correctly whereas from the shell it ran poorly and was jerky.
If i use the following code:
if event.type == QUIT:
pygame.quit()
sys.exit()
the game exits correctly but leaves an error message in the shell which has no effect on the game and is largely redundant. It is just a little ugly. This does not happen from the command line.
Summary: try running the game from the command line to avoid Tkinter problems
If you still have the issue, (after breaking the loop) try using sys.exit(0) instead of sys.exit(). Hope it'll help. It worked for me. It seems pygame expects the 'status' argument (i.e. 0 here) to be passed in explicitly.
See the below example:
isRunning = True
while isRunning:
# Catch events
if event.type == pygame.QUIT:
isRunning = False # Breaks the loop
pygame.quit()
sys.exit(0)

Why is my basic PyGame module so slow?

I've planning on writing a code in Pygame and I was just getting started with the basics and found that the executing code was really slow. When I press a key it takes a while for it to print it in the terminal (there doesn't seem to be any pattern to it).
I'm running Python 2.6, I downgraded after coming across this problem. With further testing I've found that the whole system slows down. Has anyone come across this or got a solution so it runs faster or/and prevents the system from slowing down?
OS - Ubuntu
Hardware - Macbook Pro
import pygame
import pygame.locals
pygame.mixer.init()
screen = pygame.display.set_mode((640, 480))
pygame.display.set_caption("bla")
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill(pygame.Color("green"))
screen.blit(background, (0, 0))
looping = True
while looping:
for event in pygame.event.get():
if event.type == pygame.QUIT:
looping = False
elif event.type == pygame.KEYDOWN:
keyName = pygame.key.name(event.key)
print "key pressed:", keyName
if event.key == pygame.K_SPACE:
print "Loading Music"
pygame.mixer.music.load("born.mp3")
elif event.key == pygame.K_ESCAPE:
looping = False
pygame.display.flip()
If there's any further information I can provide I would be happy to help.
pyGame is based on SDL which is internally based on threads.
When you have threading, print messages are basically a no-no. Because often times because of the scheduler slices (which are large in SDL), the print messages get delayed. Its not that pygame is slow (it is some situations, but, not in this one), its just that the print statement is in a seperate event thread.
Try doing this in pygame, it'll run pretty well.

Categories