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
+31 -13
View File
@@ -53,22 +53,40 @@ def initialize_gui(gameTitle):
# Enable key repeat for volume controls # Enable key repeat for volume controls
pygame.key.set_repeat(500, 100) pygame.key.set_repeat(500, 100)
# Load sound files # Load sound files recursively including subdirectories
soundData = {}
try: try:
from os import listdir import os
from os.path import isfile, join
soundFiles = [f for f in listdir("sounds/") soundDir = "sounds/"
if isfile(join("sounds/", f)) # Walk through directory tree
and (f.split('.')[1].lower() in ["ogg", "wav"])] 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: except Exception as e:
print("No sounds found.") print("Error loading sounds:", e)
Speech.get_instance().speak("No sounds found.", False) Speech.get_instance().speak("Error loading sounds.", False)
soundFiles = []
# Create dictionary of sound objects
soundData = {} soundData = {}
for f in soundFiles:
soundData[f.split('.')[0]] = pygame.mixer.Sound("sounds/" + f)
# Play intro sound if available # Play intro sound if available
from .sound import cut_scene from .sound import cut_scene
+111 -27
View File
@@ -256,9 +256,15 @@ def learn_sounds(sounds):
"""Interactive menu for learning game sounds. """Interactive menu for learning game sounds.
Allows users to: Allows users to:
- Navigate through available sounds - Navigate through available sounds with up/down arrows
- Play selected sounds - Navigate between sound categories (folders) using Page Up/Page Down or Left/Right arrows
- Return to menu with escape key - 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: Args:
sounds (dict): Dictionary of available sound objects sounds (dict): Dictionary of available sound objects
@@ -269,56 +275,134 @@ def learn_sounds(sounds):
# Get speech instance # Get speech instance
speech = Speech.get_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 # Organize sounds by directory
soundFiles = [f for f in listdir("sounds/") soundsByDir = {}
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("_"))]
# Sort the sound files alphabetically # Process each sound key in the dictionary
soundFiles.sort() for soundKey in sounds.keys():
# Skip if key has any excluded prefix
if any(soundKey.lower().startswith(prefix.lower()) for prefix in excludedPrefixes):
continue
# Total number of sound files # Split key into path parts
totalSounds = len(soundFiles) parts = soundKey.split('/')
# Track last spoken index to avoid repetition # 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)
# Sort each directory's sounds
for directory in soundsByDir:
soundsByDir[directory].sort()
# 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 lastSpoken = -1
directoryChanged = True # Flag to track if directory just changed
# Flag to track when to exit the loop # Flag to track when to exit the loop
returnToMenu = False returnToMenu = False
while not returnToMenu: while not returnToMenu:
if currentIndex != lastSpoken: # Announce current sound
# Speak the sound name followed by its position in the list if currentSoundIndex != lastSpoken:
speech.speak(f"{soundFiles[currentIndex][:-4]}, {currentIndex + 1} of {totalSounds}") totalSounds = len(currentSoundKeys)
lastSpoken = currentIndex 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() event = pygame.event.wait()
if event.type == pygame.KEYDOWN: if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE: if event.key == pygame.K_ESCAPE:
returnToMenu = True 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() 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() 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: try:
soundName = soundFiles[currentIndex][:-4] soundName = currentSoundKeys[currentSoundIndex]
pygame.mixer.stop() pygame.mixer.stop()
sounds[soundName].play() sounds[soundName].play()
except: except Exception as e:
lastSpoken = -1 print(f"Error playing sound: {e}")
speech.speak("Could not play sound.") speech.speak("Could not play sound.")
event = pygame.event.clear() event = pygame.event.clear()
pygame.event.pump() # Process pygame's internal events
time.sleep(0.001) time.sleep(0.001)
return "menu" return "menu"
+31 -35
View File
@@ -8,6 +8,7 @@ Provides functionality for:
- Volume controls - Volume controls
""" """
import os
import pygame import pygame
import random import random
import re import re
@@ -44,46 +45,41 @@ class Sound:
self.load_sounds() self.load_sounds()
def load_sounds(self): def load_sounds(self):
"""Load all sound files from the sound directory.""" """Load all sound files from the sound directory and its subdirectories.
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 Searches recursively through subdirectories and loads all sound files with
for f in soundFiles: .ogg or .wav extensions. Sound names are stored as relative paths from the
self.sounds[f.split('.')[0]] = pygame.mixer.Sound(join(self.soundDir, f)) sound directory, with directory separators replaced by forward slashes.
"""
try:
# 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: except Exception as e:
print(f"Error loading sounds: {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): def play_sound(self, soundName, volume=1.0):
"""Play a sound with current volume settings applied. """Play a sound with current volume settings applied.