From d364ce28ef5eed8c86817f1cefe1e3a498fe9e26 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Fri, 19 Sep 2025 00:44:25 -0400 Subject: [PATCH] Another experimental sound change. --- sound.py | 49 +++++++++++++++++++++++++++++++++++++------------ speech.py | 8 ++++++-- utils.py | 8 ++++++-- 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/sound.py b/sound.py index 6548d63..01d119c 100644 --- a/sound.py +++ b/sound.py @@ -19,20 +19,44 @@ from .services import VolumeService # Global instance for backward compatibility volumeService = VolumeService.get_instance() +# Global channel manager for explicit channel assignment +_next_channel_index = 1 + +def get_available_channel(): + """Get next available channel using explicit assignment instead of pygame's auto-selection.""" + global _next_channel_index + + # Try to find an idle channel starting from next_channel_index + for _ in range(47): # Try all available channels (1-47, skip 0 reserved for cutscenes) + channel = pygame.mixer.Channel(_next_channel_index) + if not channel.get_busy(): + result = _next_channel_index + _next_channel_index = (_next_channel_index % 47) + 1 # Cycle 1-47 + return pygame.mixer.Channel(result) + _next_channel_index = (_next_channel_index % 47) + 1 + + # If all channels busy, force stop the current one and use it + channel = pygame.mixer.Channel(_next_channel_index) + channel.stop() + pygame.event.pump() # Force immediate cleanup + result = _next_channel_index + _next_channel_index = (_next_channel_index % 47) + 1 + return pygame.mixer.Channel(result) + def play_sound_with_retry(sound, loop=False, max_retries=3): - """Play a sound with retry logic, returns (channel, success).""" + """Play a sound with explicit channel assignment instead of pygame auto-selection.""" for attempt in range(max_retries): - # pygame automatically finds channels and forces allocation if needed - channel = sound.play(-1 if loop else 0) - if channel: + try: + # Use explicit channel assignment instead of pygame's automatic selection + channel = get_available_channel() + channel.play(sound, -1 if loop else 0) return channel, True - - # If still failed after retries - if attempt == max_retries - 1: - print(f"Sound playback failed after {max_retries} attempts") - return None, False + except Exception as e: + if attempt == max_retries - 1: + print(f"Sound playback failed after {max_retries} attempts: {e}") + return None, False return None, False @@ -487,9 +511,10 @@ def play_sound(sound_or_name, volume=1.0, loop=False, playerPos=None, objPos=Non # Case 3: Direct pygame.Sound elif isinstance(sound_or_name, pygame.mixer.Sound): - channel = sound_or_name.play(-1 if loop else 0) - if channel: - channel.set_volume(volume * volumeService.get_sfx_volume()) + # Use explicit channel assignment instead of pygame auto-selection + channel = get_available_channel() + channel.play(sound_or_name, -1 if loop else 0) + channel.set_volume(volume * volumeService.get_sfx_volume()) return channel # Case 4: Sound name with dictionary diff --git a/speech.py b/speech.py index cd8910a..281f46d 100644 --- a/speech.py +++ b/speech.py @@ -289,14 +289,18 @@ def _play_dialog_sound(entry, dialog_config, sounds): if hasattr(sounds, 'sounds') and sound_to_play in sounds.sounds: # Sound class instance (like from libstormgames Sound class) sound_obj = sounds.sounds[sound_to_play] - channel = sound_obj.play() + from .sound import get_available_channel + channel = get_available_channel() + channel.play(sound_obj) sound_duration = sound_obj.get_length() if sound_duration > 0: pygame.time.wait(int(sound_duration * 1000)) elif isinstance(sounds, dict) and sound_to_play in sounds: # Dictionary of pygame sound objects (like from initialize_gui) sound_obj = sounds[sound_to_play] - channel = sound_obj.play() + from .sound import get_available_channel + channel = get_available_channel() + channel.play(sound_obj) sound_duration = sound_obj.get_length() if sound_duration > 0: pygame.time.wait(int(sound_duration * 1000)) diff --git a/utils.py b/utils.py index d293087..983739d 100644 --- a/utils.py +++ b/utils.py @@ -476,7 +476,9 @@ def x_powerbar(): leftVolume = (50 - position) / 100 rightVolume = (position + 50) / 100 tone = generate_tone(frequency) - channel = tone.play() + from .sound import get_available_channel + channel = get_available_channel() + channel.play(tone) channel.set_volume(leftVolume, rightVolume) # Visual representation @@ -519,7 +521,9 @@ def y_powerbar(): while True: frequency = 220 + (power * 5) # Adjust these values to change the pitch range tone = generate_tone(frequency) - channel = tone.play() + from .sound import get_available_channel + channel = get_available_channel() + channel.play(tone) # Visual representation screen.fill((0, 0, 0))