// Plays a soundboard entry to the local default audio output. Used when no // call pipeline is active (the in-call path routes via the LiveKit // publishing pipeline so peers hear; this path is local-only). Fetches the // blob via getSoundBlob and creates a short-lived object URL for the audio // element. import { getSoundBlob, type SoundboardEntry } from './soundboardStorage'; const activeAudios = new Set(); export async function playSoundboardLocal(entry: SoundboardEntry): Promise { const blob = await getSoundBlob(entry.id); if (!blob) return; const src = URL.createObjectURL(blob); const el = new Audio(src); // Use the per-entry gain as local volume. SoundboardEntry exposes `gain` // (0..1) which mirrors the value used in the in-call pipeline. el.volume = Math.max(0, Math.min(1, entry.gain)); activeAudios.add(el); const cleanup = () => { activeAudios.delete(el); URL.revokeObjectURL(src); }; el.addEventListener('ended', cleanup); el.addEventListener('error', cleanup); try { await el.play(); } catch (err) { cleanup(); console.warn('soundboardLocalPlay failed', err); } } export function stopSoundboardLocal(): void { for (const el of activeAudios) { try { el.pause(); el.currentTime = 0; } catch { /* ignore */ } } activeAudios.clear(); }