[Rod Stephens Books]
Index Books Python Examples About Rod Contact
[Mastodon] [Bluesky]
[Build Your Own Ray Tracer With Python]

[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

Title: Control sounds with Pygame in Python

[These buttons play various sounds controlled with Pygame in Python]

Lately I've been working on a book that uses Pygame's sound system quite a bit. The basic approach is to load a sound and call its play method to play it. That method returns a mixer object that you can use to stop the sound or see if it has finished playing.

That all works, but there are a few odd side effects. One particularly annoying issue is, if you play the same sound quickly, you get a weird and irritating echo-like sound. Because the actions that launch sounds in a Pygame program typically happen at 20 or 30 frames per second, that's a real problem.

To deal with that and to generally keep track of what sounds are playing, I wrote a few sound management methods.

load_sounds

The following load_sounds method loads the program's sound files all at once.

def load_sounds(self): '''Load the game sounds.''' sound_files= ['crash.wav', 'saucer.wav', 'tada.wav', 'wah_wah.wav'] self.sounds = {name.split('.')[0]: pygame.mixer.Sound(name) for name in sound_files} # sounds_playing will hold channels of playing sounds. self.sounds_playing = {}

This code first defines a list of sound file names and then uses a dictionary comprehension to loop through those names. It splits the name at the dot to remove the file's extension. It uses the stripped down name as a key and a new pygame.mixer.Sound object as the value in the resulting dictionary, which it saves in self.sounds.

For example, the value self.sounds['crash'] is an object that represents the sound file crash.wav.

The method finishes by creating an empty sounds_playing dictionary. The following methods use that dictionary to manage playing sounds that are playing.

play_sound

The following play_sound method starts a sound playing.

def play_sound(self, sound_name): '''Play this sound.''' self.stop_sound(sound_name) self.sounds_playing[sound_name] = self.sounds[sound_name].play()

The method first calls stop_sound to stop the sound if it is already playing. An alternative strategy would be to do nothing if the sound is already playing. In the game programs I'm working on now, that's probably a better approach, but this one works well for this example. You'll need to decide which approach is better for your application.

Having stopped the sound if it is playing, the code calls the Sound object's play method. That method returns a mixer object, which the code saves in the sounds_playing dictionary using the sound's name as the key.

sound_is_playing

The following sound_is_playing method returns True if a particular sound is currently playing.

def sound_is_playing(self, sound_name): '''Return True if this sound is currently playing.''' return sound_name in self.sounds_playing

This method simply checks to see if the sound's name is in the sounds_playing dictionary. If the name is present, the sound is playing so the method returns True.

stop_sound

The following stop_sound method stops a sound if it is playing.

def stop_sound(self, sound_name): '''Stop this sound if it is playing.''' if self.sound_is_playing(sound_name): self.sounds_playing[sound_name].stop() self.sounds_playing.pop(sound_name)

This code first calls sound_is_playing to see if the sound is playing. If the sound is currently playing, the method uses the sounds_playing dictionary to get the sound's mixer and calls the mixer's stop method. It then removes the sound's entry from the dictionary.

stop_all_sounds

The following stop_all_sounds method stops all playing sounds.

def stop_all_sounds(self): '''Stop all playing sounds.''' for sound_name in list(self.sounds.keys()): self.stop_sound(sound_name)

This method calls the dictionary's keys method to get a list of the dictionary's keys, which are the names of the currently playing sounds. It passes those keys into the list constructor to convert the iterator into a list.

The reason it does that is it's always risky to modify an object while you're iterating it. In this example, we would be removing items from a dictionary while we're looping through its keys, so its list of keys is changing as we go. (More precisely, the keys method returns a dict_keys object, which is a dynamic view object that changes as the dictionary's contents change, which can be a potential headache.)

This seems to work in this example, but as I said, it's a risky proposition. To avoid possible confusion, the code passes the keys iterator into the list constructor to make a list holding the keys. That list doesn't change as the dictionary does, so problem averted!

The code loops through the list of sound names and calls the stop_sound for each.

Conclusion

I've tried other methods for playing sounds in Python, and Pygame's sound system has given me the least trouble. The methods described here make it pretty easy to load, start, and stop sounds files. Download the example to experiment with it and to see additional details.

Acknowledgements

I often use sounds from freesound.org because there are a lot of them and they include a lot of sounds that are useful for games. (Plus they're free!) Here are the ones included in this example.

FileAttributionModifications
crash.wav Flying, then crash SFX.mp3 by YTR76 -- https://freesound.org/s/620502/ -- License: Creative Commons 0 Shortened
saucer.wav Flying saucer by ruslanas.com -- https://freesound.org/s/269541/ -- License: Attribution 4.0
tada.wav Fanfare - Rpg by colorsCrimsonTears -- https://freesound.org/s/566203/ -- License: Creative Commons 0
wah_wah.wav Sad Trombone.wav by Benboncan -- https://freesound.org/s/73581/ -- License: Attribution 4.0 Shortened

If you use these sounds or others from freesound.org, please consider making a small donation to the sound authors.

© 2025 Rocky Mountain Computer Consulting, Inc. All rights reserved.