Compare commits

...

4 Commits

Author SHA1 Message Date
Storm Dragon 3b01662d98 Directory names were not speaking. Hopefully fixed it. 2025-03-16 17:45:49 -04:00
Storm Dragon 5e926fa7eb More updates to learn_sounds. I think I have a good system for multiple sound directories now. 2025-03-16 17:34:22 -04:00
Storm Dragon e272da1177 learn_sounds needed updating too. Hopefully this attempt works. 2025-03-16 17:13:02 -04:00
Storm Dragon 1bb9e18ea2 Sounds can now load from subdirectories. 2025-03-16 17:02:18 -04:00
3 changed files with 176 additions and 78 deletions
+33 -15
View File
@@ -53,22 +53,40 @@ def initialize_gui(gameTitle):
# Enable key repeat for volume controls
pygame.key.set_repeat(500, 100)
# Load sound files
try:
from os import listdir
from os.path import isfile, join
soundFiles = [f for f in listdir("sounds/")
if isfile(join("sounds/", f))
and (f.split('.')[1].lower() in ["ogg", "wav"])]
except Exception as e:
print("No sounds found.")
Speech.get_instance().speak("No sounds found.", False)
soundFiles = []
# Create dictionary of sound objects
# Load sound files recursively including subdirectories
soundData = {}
for f in soundFiles:
soundData[f.split('.')[0]] = pygame.mixer.Sound("sounds/" + f)
try:
import os
soundDir = "sounds/"
# Walk through directory tree
for dirPath, dirNames, fileNames in os.walk(soundDir):
# Get relative path from soundDir
relPath = os.path.relpath(dirPath, soundDir)
# Process each file
for fileName in fileNames:
# Check if file is a valid sound file
if fileName.lower().endswith(('.ogg', '.wav')):
# Full path to the sound file
fullPath = os.path.join(dirPath, fileName)
# Create sound key (remove extension)
baseName = os.path.splitext(fileName)[0]
# If in root sounds dir, just use basename
if relPath == '.':
soundKey = baseName
else:
# Otherwise use relative path + basename, normalized with forward slashes
soundKey = os.path.join(relPath, baseName).replace('\\', '/')
# Load the sound
soundData[soundKey] = pygame.mixer.Sound(fullPath)
except Exception as e:
print("Error loading sounds:", e)
Speech.get_instance().speak("Error loading sounds.", False)
soundData = {}
# Play intro sound if available
from .sound import cut_scene
+111 -27
View File
@@ -256,9 +256,15 @@ def learn_sounds(sounds):
"""Interactive menu for learning game sounds.
Allows users to:
- Navigate through available sounds
- Play selected sounds
- Return to menu with escape key
- Navigate through available sounds with up/down arrows
- Navigate between sound categories (folders) using Page Up/Page Down or Left/Right arrows
- Play selected sounds with Enter
- Return to menu with Escape
Excluded sounds:
- Files in folders named 'ambience' (at any level)
- Files in any directory starting with '.'
- Files starting with 'game-intro', 'music_menu', or '_'
Args:
sounds (dict): Dictionary of available sound objects
@@ -269,56 +275,134 @@ def learn_sounds(sounds):
# Get speech instance
speech = Speech.get_instance()
currentIndex = 0
# Define exclusion criteria
excludedPrefixes = ["game-intro", "music_menu", "_"]
excludedDirs = ["ambience", "."]
# Get list of available sounds, excluding special sounds
soundFiles = [f for f in listdir("sounds/")
if isfile(join("sounds/", f))
and (f.split('.')[1].lower() in ["ogg", "wav"])
and (f.split('.')[0].lower() not in ["game-intro", "music_menu"])
and (not f.lower().startswith("_"))]
# Organize sounds by directory
soundsByDir = {}
# Sort the sound files alphabetically
soundFiles.sort()
# Process each sound key in the dictionary
for soundKey in sounds.keys():
# Skip if key has any excluded prefix
if any(soundKey.lower().startswith(prefix.lower()) for prefix in excludedPrefixes):
continue
# Split key into path parts
parts = soundKey.split('/')
# Skip if any part of the path is an excluded directory
if any(part.lower() == dirName.lower() or part.startswith('.') for part in parts for dirName in excludedDirs):
continue
# Determine the directory
if '/' in soundKey:
directory = soundKey.split('/')[0]
else:
directory = 'root' # Root directory sounds
# Add to sounds by directory
if directory not in soundsByDir:
soundsByDir[directory] = []
soundsByDir[directory].append(soundKey)
# Total number of sound files
totalSounds = len(soundFiles)
# Sort each directory's sounds
for directory in soundsByDir:
soundsByDir[directory].sort()
# Track last spoken index to avoid repetition
# If no sounds found, inform the user and return
if not soundsByDir:
speech.speak("No sounds available to learn.")
return "menu"
# Get list of directories in sorted order
directories = sorted(soundsByDir.keys())
# Start with first directory
currentDirIndex = 0
currentDir = directories[currentDirIndex]
currentSoundKeys = soundsByDir[currentDir]
currentSoundIndex = 0
# Display appropriate message based on number of directories
if len(directories) > 1:
messagebox(f"Starting with {currentDir if currentDir != 'root' else 'root directory'} sounds. Use left and right arrows or page up and page down to navigate categories.")
# Track last spoken to avoid repetition
lastSpoken = -1
directoryChanged = True # Flag to track if directory just changed
# Flag to track when to exit the loop
returnToMenu = False
while not returnToMenu:
if currentIndex != lastSpoken:
# Speak the sound name followed by its position in the list
speech.speak(f"{soundFiles[currentIndex][:-4]}, {currentIndex + 1} of {totalSounds}")
lastSpoken = currentIndex
# Announce current sound
if currentSoundIndex != lastSpoken:
totalSounds = len(currentSoundKeys)
soundName = currentSoundKeys[currentSoundIndex]
# Remove directory prefix if present
if '/' in soundName:
displayName = '/'.join(soundName.split('/')[1:])
else:
displayName = soundName
# If directory just changed, include directory name in announcement
if directoryChanged:
dirDescription = "Root directory" if currentDir == 'root' else currentDir
announcement = f"{dirDescription}: {displayName}, {currentSoundIndex + 1} of {totalSounds}"
directoryChanged = False # Reset flag after announcement
else:
announcement = f"{displayName}, {currentSoundIndex + 1} of {totalSounds}"
speech.speak(announcement)
lastSpoken = currentSoundIndex
event = pygame.event.wait()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
returnToMenu = True
if event.key in [pygame.K_DOWN, pygame.K_s] and currentIndex < len(soundFiles) - 1:
# Sound navigation
elif event.key in [pygame.K_DOWN, pygame.K_s] and currentSoundIndex < len(currentSoundKeys) - 1:
pygame.mixer.stop()
currentIndex += 1
currentSoundIndex += 1
if event.key in [pygame.K_UP, pygame.K_w] and currentIndex > 0:
elif event.key in [pygame.K_UP, pygame.K_w] and currentSoundIndex > 0:
pygame.mixer.stop()
currentIndex -= 1
currentSoundIndex -= 1
if event.key == pygame.K_RETURN:
# Directory navigation
elif event.key in [pygame.K_PAGEDOWN, pygame.K_RIGHT] and currentDirIndex < len(directories) - 1:
pygame.mixer.stop()
currentDirIndex += 1
currentDir = directories[currentDirIndex]
currentSoundKeys = soundsByDir[currentDir]
currentSoundIndex = 0
directoryChanged = True # Set flag on directory change
lastSpoken = -1 # Force announcement
elif event.key in [pygame.K_PAGEUP, pygame.K_LEFT] and currentDirIndex > 0:
pygame.mixer.stop()
currentDirIndex -= 1
currentDir = directories[currentDirIndex]
currentSoundKeys = soundsByDir[currentDir]
currentSoundIndex = 0
directoryChanged = True # Set flag on directory change
lastSpoken = -1 # Force announcement
# Play sound
elif event.key == pygame.K_RETURN:
try:
soundName = soundFiles[currentIndex][:-4]
soundName = currentSoundKeys[currentSoundIndex]
pygame.mixer.stop()
sounds[soundName].play()
except:
lastSpoken = -1
except Exception as e:
print(f"Error playing sound: {e}")
speech.speak("Could not play sound.")
event = pygame.event.clear()
pygame.event.pump() # Process pygame's internal events
time.sleep(0.001)
return "menu"
+32 -36
View File
@@ -8,6 +8,7 @@ Provides functionality for:
- Volume controls
"""
import os
import pygame
import random
import re
@@ -44,46 +45,41 @@ class Sound:
self.load_sounds()
def load_sounds(self):
"""Load all sound files from the sound directory."""
"""Load all sound files from the sound directory and its subdirectories.
Searches recursively through subdirectories and loads all sound files with
.ogg or .wav extensions. Sound names are stored as relative paths from the
sound directory, with directory separators replaced by forward slashes.
"""
try:
soundFiles = [f for f in listdir(self.soundDir)
if isfile(join(self.soundDir, f))
and (f.split('.')[1].lower() in ["ogg", "wav"])]
# Create dictionary of sound objects
for f in soundFiles:
self.sounds[f.split('.')[0]] = pygame.mixer.Sound(join(self.soundDir, f))
# Walk through directory tree
for dirPath, dirNames, fileNames in os.walk(self.soundDir):
# Get relative path from soundDir
relPath = os.path.relpath(dirPath, self.soundDir)
# Process each file
for fileName in fileNames:
# Check if file is a valid sound file
if fileName.lower().endswith(('.ogg', '.wav')):
# Full path to the sound file
fullPath = os.path.join(dirPath, fileName)
# Create sound key (remove extension)
baseName = os.path.splitext(fileName)[0]
# If in root sounds dir, just use basename
if relPath == '.':
soundKey = baseName
else:
# Otherwise use relative path + basename, normalized with forward slashes
soundKey = os.path.join(relPath, baseName).replace('\\', '/')
# Load the sound
self.sounds[soundKey] = pygame.mixer.Sound(fullPath)
except Exception as e:
print(f"Error loading sounds: {e}")
def play_intro(self):
"""Play the game intro sound if available."""
if 'game-intro' in self.sounds:
self.cut_scene('game-intro')
def get_sounds(self):
"""Get the dictionary of loaded sound objects.
Returns:
dict: Dictionary of loaded sound objects
"""
return self.sounds
def play_bgm(self, musicFile):
"""Play background music with proper volume settings.
Args:
musicFile (str): Path to the music file to play
"""
try:
pygame.mixer.music.stop()
pygame.mixer.music.load(musicFile)
pygame.mixer.music.set_volume(self.volumeService.get_bgm_volume())
pygame.mixer.music.play(-1) # Loop indefinitely
except Exception as e:
pass
def play_sound(self, soundName, volume=1.0):
"""Play a sound with current volume settings applied.