fix: Prevent multiple audio streams and overlapping audio

CRITICAL AUDIO FIXES:

1. DUPLICATE AUDIO PREVENTION:
 Fixed skipTask() triggering both 'mocking' + 'denial' punishment audio
 Added skipAudioPlayed flag to prevent consequence audio when skip audio is playing
 Modified displayCurrentTask() to check flag before playing consequence audio

2. AUDIO OVERLAP ELIMINATION:
 Made audio stopping in playAudio() synchronous instead of calling stopCategory()
 Forcibly stop existing audio immediately: pause() + currentTime=0 + src=''
 Enhanced stopAllImmediate() to use same aggressive stopping method
 Removed fadeOut delays in completeTask() and skipTask() (now immediate stop)

3. TIMING IMPROVEMENTS:
 completeTask() now stops audio immediately (0ms) before playing reward audio
 skipTask() stops task audio immediately (0ms) before punishment audio
 Added 100ms delay before reward audio to ensure previous audio fully stopped

4. TECHNICAL DETAILS:
- Audio elements now get src='' to fully release resources
- Synchronous audio stopping prevents race conditions
- Clear fade timeouts to prevent delayed audio operations
- Aggressive stopping in both individual playAudio() and global stopAllImmediate()

EXPECTED RESULT:
- Only ONE audio stream plays at a time
- No overlapping punishment/task/reward audio
- Clean audio transitions without interruption errors
- Proper audio stopping when games end
This commit is contained in:
dilgenfritz 2025-10-28 08:12:16 -05:00
parent 66bee4821b
commit f250d015b8
2 changed files with 43 additions and 12 deletions

View File

@ -4455,8 +4455,14 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
// Play task audio based on type // Play task audio based on type
if (this.gameState.isConsequenceTask) { if (this.gameState.isConsequenceTask) {
// Play punishment audio for consequence tasks // Play punishment audio for consequence tasks, but not if we just played skip audio
this.audioManager.playPunishmentAudio('denial', { fadeIn: 1000 }); if (!this.gameState.skipAudioPlayed) {
this.audioManager.playPunishmentAudio('denial', { fadeIn: 1000 });
} else {
// Clear the flag for next time
this.gameState.skipAudioPlayed = false;
console.log('Skipping consequence audio - skip punishment audio already playing');
}
} else { } else {
// Determine audio intensity based on task difficulty // Determine audio intensity based on task difficulty
const difficulty = this.gameState.currentTask.difficulty || 'Medium'; const difficulty = this.gameState.currentTask.difficulty || 'Medium';
@ -4623,12 +4629,15 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
completeTask() { completeTask() {
if (!this.gameState.isRunning || this.gameState.isPaused) return; if (!this.gameState.isRunning || this.gameState.isPaused) return;
// Stop current task audio // Stop current task audio immediately (no fade to prevent overlap)
this.audioManager.stopCategory('tasks', 500); console.log('Task completed - stopping all audio immediately');
this.audioManager.stopCategory('punishments', 500); this.audioManager.stopCategory('tasks', 0);
this.audioManager.stopCategory('punishments', 0);
// Play reward audio // Brief delay before playing reward audio to ensure previous audio has stopped
this.audioManager.playRewardAudio({ fadeIn: 300 }); setTimeout(() => {
this.audioManager.playRewardAudio({ fadeIn: 300 });
}, 100);
// Mark task as used and award points // Mark task as used and award points
if (this.gameState.isConsequenceTask) { if (this.gameState.isConsequenceTask) {
@ -4717,8 +4726,9 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
return; return;
} }
// Stop current task audio // Stop current task audio immediately (no fade to prevent overlap)
this.audioManager.stopCategory('tasks', 500); console.log('Task skipped - stopping task audio immediately');
this.audioManager.stopCategory('tasks', 0);
// Play immediate punishment audio // Play immediate punishment audio
this.audioManager.playPunishmentAudio('mocking', { fadeIn: 200 }); this.audioManager.playPunishmentAudio('mocking', { fadeIn: 200 });
@ -4742,6 +4752,7 @@ ${usagePercent > 85 ? '⚠️ Storage getting full - consider deleting some imag
// Load a consequence task // Load a consequence task
this.gameState.isConsequenceTask = true; this.gameState.isConsequenceTask = true;
this.gameState.skipAudioPlayed = true; // Flag to prevent duplicate audio
this.loadNextTask(); this.loadNextTask();
this.updateStats(); this.updateStats();

View File

@ -275,8 +275,15 @@ class AudioManager {
console.log(`Selected audio file: ${audioFile.path}`); console.log(`Selected audio file: ${audioFile.path}`);
// Stop any currently playing audio in this category // Stop any currently playing audio in this category immediately and synchronously
this.stopCategory(category); if (this.categories[category].currentAudio) {
const existingAudio = this.categories[category].currentAudio;
existingAudio.pause();
existingAudio.currentTime = 0;
existingAudio.src = ''; // Clear source to fully stop
this.categories[category].currentAudio = null;
console.log(`Forcibly stopped existing audio in category: ${category}`);
}
// Create and configure audio element // Create and configure audio element
const audio = new Audio(audioFile.path); const audio = new Audio(audioFile.path);
@ -368,7 +375,20 @@ class AudioManager {
stopAllImmediate() { stopAllImmediate() {
console.log('Stopping all audio immediately...'); console.log('Stopping all audio immediately...');
for (const category in this.categories) { for (const category in this.categories) {
this.stopCategory(category, 0); // No fade, immediate stop if (this.categories[category].currentAudio) {
const audio = this.categories[category].currentAudio;
audio.pause();
audio.currentTime = 0;
audio.src = ''; // Clear source to fully stop
this.categories[category].currentAudio = null;
console.log(`Forcibly stopped audio in category: ${category}`);
}
// Clear any pending fade timeouts
if (this.categories[category].fadeTimeout) {
clearTimeout(this.categories[category].fadeTimeout);
this.categories[category].fadeTimeout = null;
}
} }
} }