From 1a6ad651395a9cb4cd7ca4ee63fc9c536ed7243f Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 18 Sep 2025 19:30:24 -0400 Subject: [PATCH] Openal turned out to be a bust. Attempt to improve sound playback reliability. --- sound.py | 77 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/sound.py b/sound.py index 1548eff..8e0639f 100644 --- a/sound.py +++ b/sound.py @@ -48,12 +48,33 @@ def find_available_channel(): channel.stop() return channel - # If no silent channels, stop the first busy channel as last resort + # If no silent channels, stop channel 1 as last resort (avoid channel 0 - reserved for cutscenes) # This ensures sounds always play rather than being silently dropped - channel = pygame.mixer.Channel(0) + channel = pygame.mixer.Channel(1) channel.stop() return channel +def play_sound_with_retry(sound, loop=False, max_retries=3): + """Play a sound with retry logic, returns (channel, success).""" + for attempt in range(max_retries): + # Try normal playback first + channel = sound.play(-1 if loop else 0) + if channel: + return channel, True + + # If failed, force allocation and try again + try: + channel = find_available_channel() + channel.play(sound, -1 if loop else 0) + if channel.get_busy(): # Verify it's actually playing + return channel, True + except Exception as e: + if attempt == max_retries - 1: # Last attempt + print(f"Sound playback failed after {max_retries} attempts: {e}") + return None, False + + return None, False + def get_channel_usage(): """Return channel usage info for debugging.""" total = pygame.mixer.get_num_channels() @@ -98,7 +119,7 @@ class Sound: pygame.mixer.pre_init(44100, -16, 2, 4096) pygame.mixer.init() pygame.mixer.set_num_channels(48) - pygame.mixer.set_reserved(0) + pygame.mixer.set_reserved(1) # Reserve channel 0 for cutscenes self.load_sounds() @@ -175,6 +196,7 @@ class Sound: # Check if sound exists if soundName not in self.sounds: + print(f"Sound not found: {soundName}") return None # Handle cut scene mode @@ -186,12 +208,11 @@ class Sound: pygame.event.clear() pygame.mixer.stop() - # Play the sound with guaranteed channel allocation - channel = self.sounds[soundName].play(-1 if loop else 0) - if not channel: - # If no channel available, force allocation - channel = find_available_channel() - channel.play(self.sounds[soundName], -1 if loop else 0) + # Play the sound with retry logic + channel, success = play_sound_with_retry(self.sounds[soundName], loop) + if not success: + print(f"Failed to play sound: {soundName}") + return None # Apply appropriate volume settings sfx_volume = self.volumeService.get_sfx_volume() @@ -308,12 +329,11 @@ class Sound: if not soundName: return None - # Play the sound with guaranteed channel allocation - channel = self.sounds[soundName].play() - if not channel: - # If no channel available, force allocation - channel = find_available_channel() - channel.play(self.sounds[soundName]) + # Play the sound with retry logic + channel, success = play_sound_with_retry(self.sounds[soundName], False) + if not success: + print(f"Failed to play falling sound: {soundName}") + return None if channel: channel.set_volume( @@ -330,7 +350,7 @@ class Sound: pygame.mixer.music.set_volume(self.volumeService.get_bgm_volume()) pygame.mixer.music.play(-1) except Exception as e: - print(f"Error playing background music: {e}") + print(f"Failed to load background music {musicFile}: {e}") def adjust_master_volume(self, change): """Adjust the master volume for all sounds.""" @@ -432,7 +452,8 @@ def play_bgm(musicFile): pygame.mixer.music.load(musicFile) pygame.mixer.music.set_volume(volumeService.get_bgm_volume()) pygame.mixer.music.play(-1) - except: pass + except Exception as e: + print(f"Failed to load background music {musicFile}: {e}") def adjust_master_volume(change): """Adjust the master volume.""" @@ -498,12 +519,11 @@ def play_sound(sound_or_name, volume=1.0, loop=False, playerPos=None, objPos=Non # Case 4: Sound name with dictionary elif isinstance(sounds, dict) and isinstance(sound_or_name, str) and sound_or_name in sounds: - # Play the sound with guaranteed channel allocation - channel = sounds[sound_or_name].play(-1 if loop else 0) - if not channel: - # If no channel available, force allocation - channel = find_available_channel() - channel.play(sounds[sound_or_name], -1 if loop else 0) + # Play the sound with retry logic + channel, success = play_sound_with_retry(sounds[sound_or_name], loop) + if not success: + print(f"Failed to play sound: {sound_or_name}") + return None # Apply volume settings sfx_vol = volumeService.get_sfx_volume() @@ -597,12 +617,11 @@ def play_random_falling(sounds, soundName, playerX, objectX, startY, currentY=0, if not matched_sound: return None - # Play the sound with guaranteed channel allocation - channel = sounds[matched_sound].play() - if not channel: - # If no channel available, force allocation - channel = find_available_channel() - channel.play(sounds[matched_sound]) + # Play the sound with retry logic + channel, success = play_sound_with_retry(sounds[matched_sound], False) + if not success: + print(f"Failed to play falling sound: {matched_sound}") + return None if channel: channel.set_volume(