Stabilize screen audio playback/reduce race conditions
Some checks are pending
build-docker / Build Image (push) Waiting to run

This commit is contained in:
mrkmntal 2026-04-12 17:42:18 -04:00
commit b0d9c95bf1
3 changed files with 87 additions and 18 deletions

View file

@ -385,6 +385,10 @@ const screenAudioMap = {
};
let screenAudioPlayer = null;
let audioStartTime = 0;
let audioStopTimeout = null;
const AUDIO_MIN_PLAY_TIME = 500; // 500ms minimum play time
let currentScreenId = null;
// Helper function to check if screen audio is enabled (always reads from localStorage)
const isScreenAudioEnabled = () => {
@ -394,17 +398,35 @@ const isScreenAudioEnabled = () => {
// Play screen audio
const playScreenAudio = async (screenId) => {
console.log(`[AUDIO] playScreenAudio called for ${screenId}`);
// Always check localStorage to ensure setting is current
if (!isScreenAudioEnabled()) return;
if (!isScreenAudioEnabled()) {
console.log(`[AUDIO] Aborting - screen audio disabled`);
return;
}
const fileName = screenAudioMap[screenId];
if (!fileName) return;
if (!fileName) {
console.log(`[AUDIO] No audio file mapped for ${screenId}`);
return;
}
// Cancel any pending stop timeout from previous audio
if (audioStopTimeout) {
console.log(`[AUDIO] Cancelling pending stop timeout for new audio`);
clearTimeout(audioStopTimeout);
audioStopTimeout = null;
}
// Stop any existing screen audio first
stopScreenAudio();
// Don't play if alert tone is active
if (alertToneActive || alertTonePending) return;
if (alertToneActive || alertTonePending) {
console.log(`[AUDIO] Aborting - alert tone active/pending`);
return;
}
// Duck background music to 10%
if (player && !player.paused) {
@ -414,17 +436,22 @@ const playScreenAudio = async (screenId) => {
// Create and play audio
screenAudioPlayer = new Audio(withBasePath(`alert/${fileName}`));
screenAudioPlayer.type = 'audio/mpeg';
currentScreenId = screenId;
screenAudioPlayer.addEventListener('ended', () => {
console.log(`[AUDIO] 'ended' event fired for ${currentScreenId}`);
screenAudioPlayer = null;
currentScreenId = null;
// Only restore if alert isn't playing
if (!alertToneActive && !alertTonePending) {
restoreMediaAfterAlert();
}
});
screenAudioPlayer.addEventListener('error', () => {
screenAudioPlayer.addEventListener('error', (e) => {
console.log(`[AUDIO] 'error' event fired for ${currentScreenId}:`, e.message || e);
screenAudioPlayer = null;
currentScreenId = null;
if (!alertToneActive && !alertTonePending) {
restoreMediaAfterAlert();
}
@ -432,21 +459,69 @@ const playScreenAudio = async (screenId) => {
try {
await screenAudioPlayer.play();
audioStartTime = Date.now();
console.log(`[AUDIO] Audio started for ${screenId} at ${audioStartTime}`);
} catch (e) {
console.log(`[AUDIO] Failed to play for ${screenId}:`, e.message);
screenAudioPlayer = null;
currentScreenId = null;
if (!alertToneActive && !alertTonePending) {
restoreMediaAfterAlert();
}
}
};
// Stop screen audio immediately
const stopScreenAudio = () => {
// Actually stop the audio (internal helper)
const actuallyStopAudio = () => {
console.log(`[AUDIO] Actually stopping audio for ${currentScreenId}`);
if (screenAudioPlayer) {
screenAudioPlayer.pause();
screenAudioPlayer.currentTime = 0;
screenAudioPlayer = null;
}
currentScreenId = null;
audioStartTime = 0;
audioStopTimeout = null;
};
// Stop screen audio (with minimum play time protection)
const stopScreenAudio = () => {
if (!screenAudioPlayer) {
console.log(`[AUDIO] stopScreenAudio called but no audio playing`);
return;
}
const elapsed = Date.now() - audioStartTime;
console.log(`[AUDIO] stopScreenAudio called for ${currentScreenId}, elapsed: ${elapsed}ms`);
// If we haven't played for the minimum time, delay the stop
if (elapsed < AUDIO_MIN_PLAY_TIME) {
const delay = AUDIO_MIN_PLAY_TIME - elapsed;
console.log(`[AUDIO] Delaying stop for ${delay}ms (minimum play time: ${AUDIO_MIN_PLAY_TIME}ms)`);
// Clear any existing timeout first
if (audioStopTimeout) {
clearTimeout(audioStopTimeout);
}
audioStopTimeout = setTimeout(() => {
// Check if this audio is still the current one (might have been replaced)
if (screenAudioPlayer && Date.now() - audioStartTime >= AUDIO_MIN_PLAY_TIME) {
console.log(`[AUDIO] Executing delayed stop for ${currentScreenId}`);
actuallyStopAudio();
// Restore music volume after delayed stop
if (!alertToneActive && !alertTonePending) {
restoreMediaAfterAlert();
}
} else {
console.log(`[AUDIO] Delayed stop cancelled - audio already changed or stopped`);
}
}, delay);
} else {
// Minimum play time met, stop immediately
console.log(`[AUDIO] Minimum play time met, stopping immediately`);
actuallyStopAudio();
}
};
const playerCanPlay = async () => {