Files
InnerOS/src/lib/audio.ts
T
jackyu66gitandClaude Opus 4.7 6530b20172 feat: guided light meditation + Edge TTS neural voice audio
- Add continuous guided light-body-scan meditation (16 segments, 97% coverage)
- Edge TTS API endpoint with Xiaoxiao neural voice at -40% rate
- Pre-generated chime WAVs and guidance MP3s in public/audio/
- Mute toggle in practice page, sequenced non-overlapping audio
- Fix middleware to serve /audio and /api/audio/tts without auth
- All LLM output localized to Chinese; fix blank home/settings pages
- DeepSeek API compat: json_object, increased max_tokens
- Analytics, cron reminders, onboarding flow fixes

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-17 20:08:29 +08:00

51 lines
1.6 KiB
TypeScript

// 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<void> {
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<void>((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 {}
}