Add looping gameplay music

This commit is contained in:
2026-05-07 20:03:20 +02:00
parent 5233294b26
commit a908b50019
4 changed files with 92 additions and 0 deletions
+77
View File
@@ -5,6 +5,10 @@ from ctypes import *
import sdl2
import sdl2.ext
try:
import sdl2.sdlmixer as sdlmixer
except ImportError:
sdlmixer = None
from sdl2.ext.compat import byteify
from sdl2 import SDL_AudioSpec
from PIL import Image
@@ -124,6 +128,73 @@ class GameWindow:
self.audio_devs["base"] = sdl2.SDL_OpenAudioDevice(None, 0, audio_spec, None, 0)
self.audio_devs["effects"] = sdl2.SDL_OpenAudioDevice(None, 0, audio_spec, None, 0)
self.audio_devs["music"] = sdl2.SDL_OpenAudioDevice(None, 0, audio_spec, None, 0)
self.music_enabled = False
self.music_volume = 128
self.music_track = None
self.music_path = None
if sdlmixer is None:
return
mixer_flags = getattr(sdlmixer, "MIX_INIT_MP3", 0)
init_result = sdlmixer.Mix_Init(mixer_flags)
if mixer_flags and init_result & mixer_flags != mixer_flags:
print("[audio] SDL_mixer MP3 support not available")
return
if sdlmixer.Mix_OpenAudio(44100, sdl2.AUDIO_S16SYS, sdlmixer.MIX_DEFAULT_CHANNELS, 2048) != 0:
print(f"[audio] failed to open music mixer: {sdl2.SDL_GetError()}")
return
self.music_enabled = True
sdlmixer.Mix_VolumeMusic(self.music_volume)
def set_volume(self, volume_percent):
clamped = max(0, min(int(volume_percent), 100))
self.music_volume = int(round(clamped * 128 / 100))
if self.music_enabled and sdlmixer is not None:
sdlmixer.Mix_VolumeMusic(self.music_volume)
def play_music(self, music_file, loop=True):
if not self.audio or not self.music_enabled or sdlmixer is None:
return False
music_path = str(resolve_bundle_path(os.path.join("assets", "music", music_file)))
if self.music_path != music_path:
self._free_music_track()
self.music_track = sdlmixer.Mix_LoadMUS(byteify(music_path, "utf-8"))
if not self.music_track:
print(f"[audio] failed to load music: {music_path}")
return False
self.music_path = music_path
if sdlmixer.Mix_PausedMusic():
sdlmixer.Mix_ResumeMusic()
return True
if sdlmixer.Mix_PlayingMusic():
return True
loops = -1 if loop else 0
if sdlmixer.Mix_PlayMusic(self.music_track, loops) != 0:
print(f"[audio] failed to play music: {music_path}")
return False
return True
def pause_music(self):
if self.music_enabled and sdlmixer is not None and sdlmixer.Mix_PlayingMusic() and not sdlmixer.Mix_PausedMusic():
sdlmixer.Mix_PauseMusic()
def stop_music(self):
if self.music_enabled and sdlmixer is not None and (sdlmixer.Mix_PlayingMusic() or sdlmixer.Mix_PausedMusic()):
sdlmixer.Mix_HaltMusic()
def _free_music_track(self):
self.stop_music()
if self.music_enabled and sdlmixer is not None and self.music_track is not None:
sdlmixer.Mix_FreeMusic(self.music_track)
self.music_track = None
self.music_path = None
# ======================
# TEXTURE & IMAGE METHODS
@@ -437,6 +508,7 @@ class GameWindow:
def stop_sound(self):
"""Stop all audio playback"""
self.stop_music()
for dev in self.audio_devs.values():
sdl2.SDL_PauseAudioDevice(dev, 1)
sdl2.SDL_ClearQueuedAudio(dev)
@@ -722,6 +794,11 @@ class GameWindow:
def close(self):
"""Close the game window and cleanup"""
self._free_music_track()
if self.music_enabled and sdlmixer is not None:
sdlmixer.Mix_CloseAudio()
sdlmixer.Mix_Quit()
self.music_enabled = False
if self.game_controller:
sdl2.SDL_GameControllerClose(self.game_controller)
self.game_controller = None