Skip to content

feat: read-aloud (TTS) via media3, with voice picker + offline fallback#2186

Open
mjhfunctionalashtanga wants to merge 2 commits into
jocmp:mainfrom
mjhfunctionalashtanga:read-aloud
Open

feat: read-aloud (TTS) via media3, with voice picker + offline fallback#2186
mjhfunctionalashtanga wants to merge 2 commits into
jocmp:mainfrom
mjhfunctionalashtanga:read-aloud

Conversation

@mjhfunctionalashtanga

Copy link
Copy Markdown

What

Text-to-speech read-aloud for articles, played through the app's existing media3 pipeline (so it inherits the MediaSession, notification/lock-screen controls, and screen-off playback — same stack as podcast enclosures):

  • Article toolbar action → reads the article aloud. HTML → plain text → chunked → TextToSpeech.synthesizeToFile() → WAV → media3.
  • Progressive: plays chunk 0 while the rest synthesize.
  • Transport: ⏪10 · play/pause · ⏩10 · stop, plus speed (0.75×–2×) and pitch selectors (persisted). Also ungates the ±10s skip for regular podcast playback.
  • Auto-picks the best voice — on Google TTS every English voice reports the same "quality", so it prefers a neural (network) voice and skips notInstalled ones. This matters because Android's system TTS settings can't select the neural voices at all.
  • In-app voice picker in the read-aloud menu (Automatic + installed voices, neural first), enumerated lazily only while the menu is open.
  • Offline auto-fallback: if a network voice fails to synthesize offline, it switches once to the on-device voice and retries.

Note on #1298

I know read-aloud was marked Not planned in #1298 — no worries at all if that's still the call, and feel free to close this. I built it out for my own e-ink use and figured a complete, working implementation (reusing the existing media3 stack rather than adding a parallel audio path, and off-by-nothing since it's just a new toolbar action) might be worth a second look, or at least useful reference for anyone who wants it. Totally your call on scope/maintenance appetite.

Split out from a companion PR (volume-key paging) so each stands alone.

🤖 Generated with Claude Code

mjhfunctionalashtanga and others added 2 commits July 7, 2026 11:45
…speed/pitch, ±10s skip, progressive load) + podcast FF/rewind + warm tan notification artwork

Adds a read-aloud feature to the Capy Reader fork: synthesizes article text to
WAV chunks and plays them through the app's existing media3 MediaSession, so it
inherits background/screen-off playback and lock-screen controls. Toolbar
transport (⏪10 · ▶/⏸ · ⏩10 · speed/pitch), progressive loading, and the same
±10s skip added to the podcast enclosure player. Default warm-tan artwork so the
media notification accent reads on e-ink instead of the system blue.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QncYY8gj1nQ3EX5TcGPBTD
…fallback

- Auto-select the best available voice at read time. Google TTS reports
  uniform "quality" for English voices, so prefer a neural (network) voice
  and skip notInstalled ones. This works without touching Android's system
  TTS settings, which don't expose the neural voices at all.
- Add a Voice picker to the read-aloud menu (Automatic + installed English
  voices, neural first), enumerated lazily via a short-lived TextToSpeech
  only while the menu is open.
- Offline auto-fallback: if a network voice fails to synthesize (e.g. read
  offline), switch once to the on-device voice and retry so the article
  still reads without a connection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant