Experimental sound improvements. I'm about to say to hell with it and try using openal.
This commit is contained in:
92
sound.py
92
sound.py
@@ -19,6 +19,72 @@ from .services import VolumeService
|
||||
# Global instance for backward compatibility
|
||||
volumeService = VolumeService.get_instance()
|
||||
|
||||
def find_silent_channels():
|
||||
"""Find channels that are playing but with zero volume (effectively silent)."""
|
||||
silent_channels = []
|
||||
for n in range(pygame.mixer.get_num_channels()):
|
||||
channel = pygame.mixer.Channel(n)
|
||||
if channel.get_busy():
|
||||
# Get current volume setting (single value for both channels)
|
||||
volume = channel.get_volume()
|
||||
# Consider silent if volume is effectively zero
|
||||
if volume <= 0.001:
|
||||
silent_channels.append(channel)
|
||||
return silent_channels
|
||||
|
||||
def find_available_channel():
|
||||
"""Find an available channel, prioritizing silent channels over stopping active ones."""
|
||||
# Try to find an idle channel first
|
||||
for n in range(pygame.mixer.get_num_channels()):
|
||||
channel = pygame.mixer.Channel(n)
|
||||
if not channel.get_busy():
|
||||
return channel
|
||||
|
||||
# If no idle channels, look for silent ones to reclaim
|
||||
silent_channels = find_silent_channels()
|
||||
if silent_channels:
|
||||
# Stop the first silent channel and return it
|
||||
channel = silent_channels[0]
|
||||
channel.stop()
|
||||
return channel
|
||||
|
||||
# If no silent channels, stop the first busy channel as last resort
|
||||
# This ensures sounds always play rather than being silently dropped
|
||||
channel = pygame.mixer.Channel(0)
|
||||
channel.stop()
|
||||
return channel
|
||||
|
||||
def get_channel_usage():
|
||||
"""Return channel usage info for debugging."""
|
||||
total = pygame.mixer.get_num_channels()
|
||||
busy = sum(1 for n in range(total) if pygame.mixer.Channel(n).get_busy())
|
||||
silent = len(find_silent_channels())
|
||||
return f"Channels: {busy}/{total} busy ({silent} silent)"
|
||||
|
||||
def print_channel_debug():
|
||||
"""Print detailed channel usage for debugging."""
|
||||
total = pygame.mixer.get_num_channels()
|
||||
print(f"Channel usage: {get_channel_usage()}")
|
||||
|
||||
busy_channels = []
|
||||
silent_channels = []
|
||||
|
||||
for n in range(total):
|
||||
channel = pygame.mixer.Channel(n)
|
||||
if channel.get_busy():
|
||||
volume = channel.get_volume()
|
||||
if volume <= 0.001:
|
||||
silent_channels.append(n)
|
||||
else:
|
||||
busy_channels.append(n)
|
||||
|
||||
if busy_channels:
|
||||
print(f"Active channels: {busy_channels}")
|
||||
if silent_channels:
|
||||
print(f"Silent channels: {silent_channels}")
|
||||
if not busy_channels and not silent_channels:
|
||||
print("No busy channels")
|
||||
|
||||
class Sound:
|
||||
"""Handles sound loading and playback."""
|
||||
|
||||
@@ -120,10 +186,12 @@ class Sound:
|
||||
pygame.event.clear()
|
||||
pygame.mixer.stop()
|
||||
|
||||
# Play the sound
|
||||
# Play the sound with guaranteed channel allocation
|
||||
channel = self.sounds[soundName].play(-1 if loop else 0)
|
||||
if not channel:
|
||||
return None
|
||||
# If no channel available, force allocation
|
||||
channel = find_available_channel()
|
||||
channel.play(self.sounds[soundName], -1 if loop else 0)
|
||||
|
||||
# Apply appropriate volume settings
|
||||
sfx_volume = self.volumeService.get_sfx_volume()
|
||||
@@ -240,8 +308,13 @@ class Sound:
|
||||
if not soundName:
|
||||
return None
|
||||
|
||||
# Play the sound
|
||||
# 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])
|
||||
|
||||
if channel:
|
||||
channel.set_volume(
|
||||
finalLeft * self.volumeService.sfxVolume,
|
||||
@@ -425,10 +498,12 @@ 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
|
||||
# Play the sound with guaranteed channel allocation
|
||||
channel = sounds[sound_or_name].play(-1 if loop else 0)
|
||||
if not channel:
|
||||
return None
|
||||
# If no channel available, force allocation
|
||||
channel = find_available_channel()
|
||||
channel.play(sounds[sound_or_name], -1 if loop else 0)
|
||||
|
||||
# Apply volume settings
|
||||
sfx_vol = volumeService.get_sfx_volume()
|
||||
@@ -522,8 +597,13 @@ def play_random_falling(sounds, soundName, playerX, objectX, startY, currentY=0,
|
||||
if not matched_sound:
|
||||
return None
|
||||
|
||||
# Play the sound
|
||||
# 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])
|
||||
|
||||
if channel:
|
||||
channel.set_volume(
|
||||
finalLeft * volumeService.sfxVolume,
|
||||
|
Reference in New Issue
Block a user