// Audio playback using pre-generated files + server-side TTS API. // All functions silently no-op when audio playback fails. function playFile(path: string) { try { const audio = new Audio(path); audio.play().catch(() => {}); } catch {} } export function playStartChime() { playFile("/audio/start-chime.wav"); } export function playTick() { playFile("/audio/tick.wav"); } export function playEndChime() { playFile("/audio/end-chime.wav"); } export function playMidGuidance(lang: string) { const file = lang === "zh" ? "mid-guidance-zh.mp3" : "mid-guidance-en.mp3"; playFile(`/audio/${file}`); } export function playEndGuidance(lang: string) { const file = lang === "zh" ? "end-guidance-zh.mp3" : "end-guidance-en.mp3"; playFile(`/audio/${file}`); } // Dynamic TTS via server-side Edge TTS (neural voices) API. // Falls back silently on error (network issue, edge-tts not installed, etc.). export async function speakText(text: string, lang: string = "zh"): Promise { try { const url = `/api/audio/tts?text=${encodeURIComponent(text)}&lang=${lang}&rate=-40%25`; const response = await fetch(url); if (!response.ok) return; const blob = await response.blob(); const blobUrl = URL.createObjectURL(blob); await new Promise((resolve) => { const audio = new Audio(blobUrl); audio.onended = () => { URL.revokeObjectURL(blobUrl); resolve(); }; audio.onerror = () => { URL.revokeObjectURL(blobUrl); resolve(); }; audio.play().catch(() => { URL.revokeObjectURL(blobUrl); resolve(); }); }); } catch {} }