freezmo/app
Martin Dvorak 8e1cd0855d Podcast voices: natural provider TTS, and system-voice hygiene
Two reports, one root: the platform's speech engine.

"Loud cracking at the end of every sentence" is the browser/OS TTS popping
between its internal sentence chunks (an idle cancel makes it worse). Hygiene
for that path: never cancel when nothing is pending, normalise the spoken text
(control characters, ellipses, dashes), and a 140ms beat between sections so
speech never runs into the engine's tail.

The real fix is a second voice source that does not use the platform engine:
- lib/providers carries TTS profiles (OpenAI gpt-4o-mini-tts, Groq
  playai-tts, any OpenAI-compatible /audio/speech behind the custom provider),
  male voices first.
- /api/tts proxies them with the same guards and the key still in headers.
- the picker gains System / Natural, with the provider's voice list.
- lib/podcast-audio plays one clip per section through a hidden <audio>
  element, measures the clip's real duration and gives each word a slice of it
  proportional to its spoken length - the highlight follows the audio exactly,
  and pause/seek/chapters become real audio operations. Clips cache per
  session, the next section generates while the current plays, and a voice
  change disposes the element and its blobs. Speed applies live (setRate)
  without dropping the cache.

podcast-test stubs /api/tts with a real silent WAV and pins the path: the
voice list, the request body, exactly one playing element, and the highlight
riding currentTime. 43 checks. tests/lib.mjs launches Chromium with the
autoplay policy relaxed (a real device has the listener's gesture).
Version 0.7.6, SW v98.
2026-10-16 16:34:46 +00:00
..
api Podcast voices: natural provider TTS, and system-voice hygiene 2026-10-16 16:34:46 +00:00
chat Chat mode, phase 1: grounded streaming chat over your notes 2026-10-11 18:32:11 +00:00
cookies Privacy policy, terms of service, cookie policy + a cookie notice 2026-10-12 21:26:55 +00:00
create App versioning + the create page's missing top padding 2026-10-12 20:04:26 +00:00
deck/[id] Accessibility pass, report control for homework exercises, clearer rail actions 2026-10-12 21:15:56 +00:00
exam Smart Review: instant loading feedback, no more dead-feeling tap 2026-10-11 14:35:16 +00:00
homework Navigation restructure: five-tab bottom bar, no phone drawer 2026-10-10 16:53:55 +00:00
offline Add an English/Czech interface language switch 2026-10-08 13:56:32 +00:00
podcast Podcast mode: notes read aloud with a lyric-style word highlight 2026-10-16 11:42:15 +00:00
privacy Privacy policy, terms of service, cookie policy + a cookie notice 2026-10-12 21:26:55 +00:00
review Smart Review: instant loading feedback, no more dead-feeling tap 2026-10-11 14:35:16 +00:00
share Navigation restructure: five-tab bottom bar, no phone drawer 2026-10-10 16:53:55 +00:00
share-target Native-feel pass: PWA integration, system back, offline, haptics, undo 2026-10-10 12:16:03 +00:00
stats Smart Review: instant loading feedback, no more dead-feeling tap 2026-10-11 14:35:16 +00:00
terms Privacy policy, terms of service, cookie policy + a cookie notice 2026-10-12 21:26:55 +00:00
favicon.ico Use the new app icon everywhere 2026-10-08 00:38:54 +00:00
globals.css Podcast mode: notes read aloud with a lyric-style word highlight 2026-10-16 11:42:15 +00:00
layout.tsx Frutiger Aero: the theme now plays its soundtrack 2026-10-15 22:47:44 +00:00
manifest.ts Accessibility pass, report control for homework exercises, clearer rail actions 2026-10-12 21:15:56 +00:00
not-found.tsx Custom Frutiger Aero 404 2026-10-14 15:27:47 +00:00
page.tsx Podcast mode: notes read aloud with a lyric-style word highlight 2026-10-16 11:42:15 +00:00