class AudioQueueManager { private queue: string[] = []; private isPlaying: boolean = false; private currentAudio: HTMLAudioElement | null = null; private _instance: AudioQueueManager | null = null; constructor() {} public static getInstance(): AudioQueueManager { if (!(window as any).audioQueueInstance) { (window as any).audioQueueInstance = new AudioQueueManager(); } return (window as any).audioQueueInstance; } public play(path: string) { // Avoid adding duplicates if the same sound is already at the end of the queue // This helps with the "double trigger" issue if the UI sends it twice rapidly if (this.queue.length > 0 && this.queue[this.queue.length - 1] === path) { return; } // Also check if currently playing is the same sound and we are just starting (optional robustness) console.log(`[AudioQueue] Added to queue: ${path}`); this.queue.push(path); this.processQueue(); } private processQueue() { if (this.isPlaying || this.queue.length === 0) { return; } const nextPath = this.queue.shift(); if (!nextPath) return; this.isPlaying = true; // Prefix with /audio/ if not present and it's a relative filename const fullPath = nextPath.startsWith('/') || nextPath.startsWith('http') ? nextPath : `/audio/${nextPath}`; this.currentAudio = new Audio(fullPath); this.currentAudio.onended = () => { console.log(`[AudioQueue] Finished: ${nextPath}`); this.isPlaying = false; this.currentAudio = null; this.processQueue(); // Play next }; this.currentAudio.onerror = (e) => { console.error(`[AudioQueue] Error playing ${nextPath}`, e); this.isPlaying = false; this.currentAudio = null; this.processQueue(); // Skip and play next }; this.currentAudio.play().catch(e => { console.error(`[AudioQueue] Autoplay failed for ${nextPath}`, e); this.isPlaying = false; this.processQueue(); }); } public stopAll() { if (this.currentAudio) { this.currentAudio.pause(); this.currentAudio = null; } this.queue = []; this.isPlaying = false; } } export const AudioQueue = { play: (path: string) => { if (typeof window !== 'undefined') { AudioQueueManager.getInstance().play(path); } }, stopAll: () => { if (typeof window !== 'undefined') { AudioQueueManager.getInstance().stopAll(); } } };