import { NOTIFICATION_SOUNDS, type NotificationType } from "./types"; class SoundPlayer { private audioCache: Map = new Map(); private enabled: boolean = true; private globalVolume: number = 1.0; constructor() { // Preload all essential sounds this.preloadSounds(); } private preloadSounds(): void { Object.entries(NOTIFICATION_SOUNDS).forEach(([type, sound]) => { const audio = new Audio(`/sounds/${sound.filename}`); audio.preload = "auto"; audio.volume = (sound.volume || 0.7) * this.globalVolume; this.audioCache.set(type as NotificationType, audio); }); } async play(type: NotificationType): Promise { if (!this.enabled) return; try { const audio = this.audioCache.get(type); if (!audio) { console.warn(`No audio found for notification type: ${type}`); return; } // Clone the audio to allow overlapping sounds const audioClone = audio.cloneNode() as HTMLAudioElement; audioClone.volume = audio.volume; await audioClone.play(); } catch (error) { console.error("Failed to play notification sound:", error); } } setEnabled(enabled: boolean): void { this.enabled = enabled; } setGlobalVolume(volume: number): void { this.globalVolume = Math.max(0, Math.min(1, volume)); // Update all cached audio volumes this.audioCache.forEach((audio, type) => { const sound = NOTIFICATION_SOUNDS[type]; audio.volume = (sound.volume || 0.7) * this.globalVolume; }); } isEnabled(): boolean { return this.enabled; } } // Export singleton instance export const soundPlayer = new SoundPlayer();