Files
readest/apps
Huang Xin b87c735c1e fix(tts): keep native System TTS reading past unspeakable chunks offline (#4613, #4408) (#4716)
Android System TTS (and iOS) read-aloud could stop offline and refuse to
continue — #4613 "stops at the end of the chapter, won't advance" and #4408
"stops at random intervals" — after which the play/headphone controls felt
wedged.

Root cause: `TTSController.#speak` only auto-advances when the last event code
is `end`. The native client surfaces an offline engine failure as a terminal
`error` code (Android `UtteranceProgressListener.onError`). This typically
happens on a specific utterance the offline engine can't synthesize — e.g. an
unsupported character — characteristically the first utterance after a chapter
boundary, even with a local/offline voice (online, engines often fall back to
network synthesis, which is why it only breaks offline). On `error` the
controller never called `forward()` and left `state` stuck at `playing`, so
playback dead-ended and the controls couldn't recover. Edge/Web clients throw
instead (handled by `error()`), so only the native client hit this.

Fix (native-scoped, no change to the Edge/Web path): when the active client is
the native client and an utterance ends with a terminal `error` (still playing,
not aborted, not one-time), skip that chunk and advance just as a normal `end`
would — re-speaking the same unsynthesizable text would only fail again. A
consecutive-error cap stops playback gracefully if the engine can't speak
anything, so a wholly-unusable engine doesn't silently race to the end of the
book and the state machine always leaves `playing`.

Tests: tts-controller covers skip-on-error advancing past a bad chunk, and the
consecutive-error cap stopping gracefully (bounded, not wedged in playing).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 04:50:37 +02:00
..