Spaces:
Sleeping
Sleeping
| 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(); | |
| } | |
| } | |
| }; | |