Openal turned out to be a bust. Attempt to improve sound playback reliability.
This commit is contained in:
77
sound.py
77
sound.py
@@ -48,12 +48,33 @@ def find_available_channel():
|
|||||||
channel.stop()
|
channel.stop()
|
||||||
return channel
|
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
|
# This ensures sounds always play rather than being silently dropped
|
||||||
channel = pygame.mixer.Channel(0)
|
channel = pygame.mixer.Channel(1)
|
||||||
channel.stop()
|
channel.stop()
|
||||||
return channel
|
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():
|
def get_channel_usage():
|
||||||
"""Return channel usage info for debugging."""
|
"""Return channel usage info for debugging."""
|
||||||
total = pygame.mixer.get_num_channels()
|
total = pygame.mixer.get_num_channels()
|
||||||
@@ -98,7 +119,7 @@ class Sound:
|
|||||||
pygame.mixer.pre_init(44100, -16, 2, 4096)
|
pygame.mixer.pre_init(44100, -16, 2, 4096)
|
||||||
pygame.mixer.init()
|
pygame.mixer.init()
|
||||||
pygame.mixer.set_num_channels(48)
|
pygame.mixer.set_num_channels(48)
|
||||||
pygame.mixer.set_reserved(0)
|
pygame.mixer.set_reserved(1) # Reserve channel 0 for cutscenes
|
||||||
|
|
||||||
self.load_sounds()
|
self.load_sounds()
|
||||||
|
|
||||||
@@ -175,6 +196,7 @@ class Sound:
|
|||||||
|
|
||||||
# Check if sound exists
|
# Check if sound exists
|
||||||
if soundName not in self.sounds:
|
if soundName not in self.sounds:
|
||||||
|
print(f"Sound not found: {soundName}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Handle cut scene mode
|
# Handle cut scene mode
|
||||||
@@ -186,12 +208,11 @@ class Sound:
|
|||||||
pygame.event.clear()
|
pygame.event.clear()
|
||||||
pygame.mixer.stop()
|
pygame.mixer.stop()
|
||||||
|
|
||||||
# Play the sound with guaranteed channel allocation
|
# Play the sound with retry logic
|
||||||
channel = self.sounds[soundName].play(-1 if loop else 0)
|
channel, success = play_sound_with_retry(self.sounds[soundName], loop)
|
||||||
if not channel:
|
if not success:
|
||||||
# If no channel available, force allocation
|
print(f"Failed to play sound: {soundName}")
|
||||||
channel = find_available_channel()
|
return None
|
||||||
channel.play(self.sounds[soundName], -1 if loop else 0)
|
|
||||||
|
|
||||||
# Apply appropriate volume settings
|
# Apply appropriate volume settings
|
||||||
sfx_volume = self.volumeService.get_sfx_volume()
|
sfx_volume = self.volumeService.get_sfx_volume()
|
||||||
@@ -308,12 +329,11 @@ class Sound:
|
|||||||
if not soundName:
|
if not soundName:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Play the sound with guaranteed channel allocation
|
# Play the sound with retry logic
|
||||||
channel = self.sounds[soundName].play()
|
channel, success = play_sound_with_retry(self.sounds[soundName], False)
|
||||||
if not channel:
|
if not success:
|
||||||
# If no channel available, force allocation
|
print(f"Failed to play falling sound: {soundName}")
|
||||||
channel = find_available_channel()
|
return None
|
||||||
channel.play(self.sounds[soundName])
|
|
||||||
|
|
||||||
if channel:
|
if channel:
|
||||||
channel.set_volume(
|
channel.set_volume(
|
||||||
@@ -330,7 +350,7 @@ class Sound:
|
|||||||
pygame.mixer.music.set_volume(self.volumeService.get_bgm_volume())
|
pygame.mixer.music.set_volume(self.volumeService.get_bgm_volume())
|
||||||
pygame.mixer.music.play(-1)
|
pygame.mixer.music.play(-1)
|
||||||
except Exception as e:
|
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):
|
def adjust_master_volume(self, change):
|
||||||
"""Adjust the master volume for all sounds."""
|
"""Adjust the master volume for all sounds."""
|
||||||
@@ -432,7 +452,8 @@ def play_bgm(musicFile):
|
|||||||
pygame.mixer.music.load(musicFile)
|
pygame.mixer.music.load(musicFile)
|
||||||
pygame.mixer.music.set_volume(volumeService.get_bgm_volume())
|
pygame.mixer.music.set_volume(volumeService.get_bgm_volume())
|
||||||
pygame.mixer.music.play(-1)
|
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):
|
def adjust_master_volume(change):
|
||||||
"""Adjust the master volume."""
|
"""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
|
# Case 4: Sound name with dictionary
|
||||||
elif isinstance(sounds, dict) and isinstance(sound_or_name, str) and sound_or_name in sounds:
|
elif isinstance(sounds, dict) and isinstance(sound_or_name, str) and sound_or_name in sounds:
|
||||||
# Play the sound with guaranteed channel allocation
|
# Play the sound with retry logic
|
||||||
channel = sounds[sound_or_name].play(-1 if loop else 0)
|
channel, success = play_sound_with_retry(sounds[sound_or_name], loop)
|
||||||
if not channel:
|
if not success:
|
||||||
# If no channel available, force allocation
|
print(f"Failed to play sound: {sound_or_name}")
|
||||||
channel = find_available_channel()
|
return None
|
||||||
channel.play(sounds[sound_or_name], -1 if loop else 0)
|
|
||||||
|
|
||||||
# Apply volume settings
|
# Apply volume settings
|
||||||
sfx_vol = volumeService.get_sfx_volume()
|
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:
|
if not matched_sound:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Play the sound with guaranteed channel allocation
|
# Play the sound with retry logic
|
||||||
channel = sounds[matched_sound].play()
|
channel, success = play_sound_with_retry(sounds[matched_sound], False)
|
||||||
if not channel:
|
if not success:
|
||||||
# If no channel available, force allocation
|
print(f"Failed to play falling sound: {matched_sound}")
|
||||||
channel = find_available_channel()
|
return None
|
||||||
channel.play(sounds[matched_sound])
|
|
||||||
|
|
||||||
if channel:
|
if channel:
|
||||||
channel.set_volume(
|
channel.set_volume(
|
||||||
|
Reference in New Issue
Block a user