Problem:
pygame.mixer.music crash while changing song
pygame versions with problems:
1.7.1 : got from pygame site maybe two years ago, the binary dependencies at same time , the binaries readme tells it is SDL - 1.2.7 , with SDL_mixer - 1.2.3, built with MSVC 6. Lots of pygame games run with no problems.
1.8.1.rc3 : test 1 both with the telus build and the 'automated build', tests 2 and 3 only tested against telus build
Other info:
winXP + sp2, python 2.4.3, integrated audio nvidia nforce2, audio drivers version 5.10.2917.0
Tested on more than a single machine, but same hardware.
Problem - bugdemos history:
1. I am one of the magicor (http://magicor.sourceforge.net) developers, working in windows; with pygame 1.7.1 no real problems with the game, but trying the pygame 1.8.1.rc3 crashes begin.
I tracked the problem to the moment pygame.mixer.music is instructed to change the current song.
The crash is a GPF, not even a 'pygame parachute', and the OS tell the fault is at sdl_mixer.dll , vs 1.2.8.0 ( provided with the windows installer for pygame 1.8.1.rc3 )
The specific fail moment is when in the levelselect menu the user gives an 'esc', wich normally will change to the main menu.
I tryied with both the telus build and the 'automated build', same problem.
1.8.1.rc3 installed after unistall 1.7.1 and deleting the remaining site-packages\pygame dir
2. Then I wrote mtest2.py (atached) to demo the problem in short code; it responds to keypresess changing or restarting the song played.
( to try it you must get two songs from the magicor repo, data\music\menu.xm and data\levels\egypt\egyptian-trance.xm )
All right, at first seemed to worked as expected: no fail in 1.7.1, fail in 1.8.1.rc3.
With more runs, they start to appear crashes in 1.7.1, albeit mostly 'pygame parachute's.
Failures not so consistent. Maybe sensitive to the timming of keypresses, sometimes closing a console and starting other seems to help to get failures.
Restarting ( keypress 'r' ) seems more prone to crash than toggle song ( keypress 't' ) in pygame 1.7.1, the other way with 1.8.1.rc3
the song 'egyptian-trance.xm' seems to crash more than menu.xm ( in repeat mode )
Two or tree toggles in 1.8.1.rc3 usually suffice to crash, 1.7.1 seems les prone to crash.
3. To have a better testbed for posible workarounds, I wrote a test that colects some stats.
trunner2.py (the test runner) and ,mtest4.py ( the test ) - both atached.
trunner2 will run k-times mtest4.py as a subprocess, counting the crashes.
mtest4.py is an automated mtest2.py, it attempts to do a programable count of song changes, be it by restarting the same sound or changing to the other. If not crashed, will add a line to a log file. ( Note: that works well with 1.7.1, but in 1.8.1.rc3 you must close manually the OS messagebox that informs about the gpf )
Finally trunner will report the stats.
That was better, but I suspect the test is introducing some artifacts. Specifically, the failure ratio for 1.7.1 seems too high ( 22/25 , 24/25 for song 'egyptian-trance.xm , 12/25 for 'menu.xm', change_mode as restart same song ). And seems that if run k fails then run k+1 probably will fail. Suspecting an OS cleanup after crash can interfere, I added a litle wait between test runs, thats the time_sleep in trunner2.py . With it, failures goes down to 1/25 , 0/25 , more reasonable in the light that magicor doenst crash with 1.7.1
Some results:
---
Trunner2 results wwith pygame 1.7.1
run1:
initial song: 1
change mode is toggle: 1
time_sleep: None
runs: 25
changes per runs: 10
success: 3
failed: 22
run2:
initial song: 1
change mode is toggle: 1
time_sleep: 0.5
runs: 25
changes per runs: 10
success: 25
failed: 0
initial song: 1
change mode is toggle: 0
time_sleep: 0.5
runs: 25
changes per runs: 10
success: 25
failed: 0
initial song: 1
change mode is toggle: 0
time_sleep: None
runs: 25
changes per runs: 10
success: 3
failed: 22
------------
Trunner2 results wwith pygame 1.8.1.rc3
initial song: 1
change mode is toggle: 1
time_sleep: None
runs: 25
changes per runs: 10
success: 0
failed: 25
initial song: 1
change mode is toggle: 1
time_sleep: 0.5
runs: 25
changes per runs: 10
success: 0
failed: 25
initial song: 1
change mode is toggle: 0
time_sleep: 0.5
runs: 25
changes per runs: 10
success: 25
failed: 0
-------------
Allright, I will try to build some workaround and report.
If there is some sugestion from the pygame builders I will be glad to test.
Be patient with the mail, Im working out of town and checking mail at a cybercafe once a day.
--
claxo
import subprocess, time # params test run maxruns = 25 ch_per_run = 10 # bToggle = 0 initial_song = 1 time_sleep = None # use None for no sleep beetwen runs cntruns = 0 cntfailed = 0 #clean stats f = open('tstats.txt','w') f.write('Start.\n') f.close() sl = ['python.exe','mtest4.py','%d'%ch_per_run,'%d'%bToggle,'%d'%initial_song ] for i in xrange(maxruns): cntruns +=1 ret = subprocess.call(sl) if time_sleep: time.sleep(time_sleep) f = open('tstats.txt','r') cntsucces = len(f.readlines())-1 f.close() print 'initial song:',initial_song print 'change mode is toggle:',bToggle print 'time_sleep:',time_sleep print 'runs:', maxruns print 'changes per runs:',ch_per_run print 'success:',cntsucces print 'failed:', maxruns-cntsucces #mejorar las estadisticas contando nre de changes exitosos, incluyendo los de #runs incompletas.
import pygame import pygame.mixer import sys, time import subprocess #self.music = music #? conflict with pygame.mixer.music ? # en/dis ##key1 : togle song ##key2 : reload same song ##key3 : quit def play_music( song, musicVol ): print 'About to start song:',song pygame.mixer.music.set_volume(musicVol*0.01) pygame.mixer.music.load( song ) pygame.mixer.music.play(-1) # looped def stop_music(): if pygame.mixer.get_init(): pygame.mixer.music.stop() ch_per_run = 0 try: ch_per_run = int(sys.argv[1]) bToggle = int(sys.argv[2]) selected = int(sys.argv[3]) except: pass if ch_per_run<1: print """ Usage: \tmtest3.py n b s Where: \tn > 1 Number of changes to try \tb = 0-1 1->toggle songs, 0->restart song \ts = 0-1 starting song, 0->menu.xm , 1->egyptian-trance.xm """ sys.exit(1) pygame.display.init() pygame.mixer.init(44100, -16, True, 4096) pygame.mixer.set_num_channels(8) soundVol = 100 musicVol = 100 songs = ['menu.xm', 'egyptian-trance.xm'] chtime = time.time()+1.0 play_music( songs[selected], musicVol) if bToggle: selected = not selected pygame.display.set_mode((300,300)) screen = pygame.display.get_surface() screen.fill((0,0,255)) clock = pygame.time.Clock() cntChanges = 0 bContinue = True while bContinue: # pygame.event.pump() for ev in pygame.event.get(): if (ev.type == pygame.KEYDOWN): if ev.key == pygame.K_ESCAPE: bContinue = False if time.time()>chtime: chtime = time.time() + 1.0 play_music( songs[selected], musicVol) if bToggle: selected = not selected cntChanges += 1 if cntChanges > ch_per_run: bContinue = False pygame.display.update() clock.tick(25) pygame.quit() f = open('tstats.txt','a') f.write('run completed\n') f.close() sys.exit(7) # En r hizo pygame_parachute: # Fatal Python error: (pygame parachute) Segmentation Fault #tambien en t, pero no siempre. # agregando un stop_music antes de music.load() se fue la falla en pygame 1.7.1 # cambiando music por pygame.mixer.music en 1.7.1 parece que no cuelga. # pero en 1.8.1.rc3 si cuelga. # En 1.8.1.rc1 no es totalmente repetible; ademas a veces lo hace con togglr, no # necesariamente con restart. Pero no hace parachute; es directamente gpf # Adicionalmente podriamos probar si quitando el music.play() [ que parece # redundante ] hay cuelgue o no -> no era redundante; el nuevo file no empieza a tocar. # probando sin play y sin stop
import pygame import pygame.mixer #from pygame.mixer import music #self.music = music #? conflict with pygame.mixer.music ? # en/dis ##key1 : togle song ##key2 : reload same song ##key3 : quit def play_music( song, musicVol ): #stop_music() #@ cures the crash print 'About to start song:',song pygame.mixer.music.set_volume(musicVol*0.01) pygame.mixer.music.load( song ) pygame.mixer.music.play(-1) # looped def stop_music(): if pygame.mixer.get_init(): pygame.mixer.music.stop() pygame.display.init() pygame.mixer.init(44100, -16, True, 4096) pygame.mixer.set_num_channels(8) soundVol = 100 musicVol = 100 songs = ['menu.xm', 'egyptian-trance.xm'] selected = 0 print """ \nUse: \t t: toggle song \t r: restart song \t esc: quit """ pygame.display.set_mode((300,300)) screen = pygame.display.get_surface() screen.fill((0,0,255)) clock = pygame.time.Clock() bContinue = True while bContinue: # pygame.event.pump() for ev in pygame.event.get(): if (ev.type == pygame.KEYDOWN): if ev.key == pygame.K_ESCAPE: bContinue = False elif ev.key == pygame.K_t: selected = not selected play_music( songs[selected], musicVol) elif ev.key == pygame.K_r: play_music( songs[selected], musicVol) else: pass pygame.display.update() clock.tick(25) pygame.quit() # En r hizo pygame_parachute: # Fatal Python error: (pygame parachute) Segmentation Fault #tambien en t, pero no siempre. # agregando un stop_music antes de music.load() se fue la falla en pygame 1.7.1 # cambiando music por pygame.mixer.music en 1.7.1 parece que no cuelga. # pero en 1.8.1.rc3 si cuelga. # En 1.8.1.rc1 no es totalmente repetible; ademas a veces lo hace con togglr, no # necesariamente con restart. Pero no hace parachute; es directamente gpf # Adicionalmente podriamos probar si quitando el music.play() [ que parece # redundante ] hay cuelgue o no -> no era redundante; el nuevo file no empieza a tocar. # probando sin play y sin stop