Skip to content

Releases: maxwhipw/marmalade-tts-android

Marmalade TTS 1.0.0

Choose a tag to compare

@github-actions github-actions released this 11 Aug 02:09

Marmalade TTS 1.0.0, the first stable release.

Voices and engines

  • Kitten Nano built in: a fast, natural English voice that works fully offline from first launch. No downloads, no setup.
  • Kokoro: the best all-round voices. Speaks English (US and UK), Spanish, French, Italian, Hindi, Brazilian Portuguese, Japanese, and Mandarin, and detects the language automatically.
  • Pocket TTS: the most expressive, characterful English voices.
  • Optional cloud voices with your own API keys, off by default and clearly separated from on-device engines.

Read anywhere

  • A full system TTS engine: e-book readers, browsers, and screen readers can all speak through Marmalade.
  • Accessibility first: TalkBack can use Marmalade as its voice, the built-in voice needs no download to get there, and the app itself is built to be navigated with a screen reader.
  • Share text or an article to Marmalade to hear it read aloud.
  • Per-app voices: give each app its own voice.

Make it yours

  • 22 effect presets, from radio to cathedral to trailer, plus a full effects editor.
  • Voice aliases for quick switching between favorite setups.
  • Emoji in your text shape the voice's expression (supported engines).

Private by default

  • On-device speech never leaves your phone. No ads, no accounts, no analytics.

marmalade-tts-1.0.0-fdroid.apk is the sideload/F-Droid build (cert SHA-256 556ebcb67b5ba498707c345a72ffe59ea156dca544800bf966d40989272118d0). The -play artifacts are for Google Play.

v0.1.14 — fix double-height top app bar

Choose a tag to compare

@maxwhipw maxwhipw released this 23 May 04:04

Bug fix: the top app bar on every tab + detail screen was rendering at ~2× its expected height. Same defect that existed in the marmalade-android sister app; mirrored their canonical fix.

What was wrong

Compose Material 3 nested Scaffold + nested TopAppBar both apply WindowInsets.statusBars by default. The outer Scaffold in AppRoot consumed the inset to push the NavHost below the status bar. Each per-screen Scaffold then re-applied the same inset to its CenterAlignedTopAppBar's internal chrome — producing ~24-28dp of empty space above the title text.

The fix

Opt every per-screen Scaffold + TopAppBar out of insets, letting the outer Scaffold be the single owner:

Scaffold(
    contentWindowInsets = WindowInsets(0),
    topBar = { CenterAlignedTopAppBar(..., windowInsets = WindowInsets(0)) },
) { … }

Applied across all 7 screens with a TopAppBar. The diagnostic agent traced the canonical pattern in marmalade-android (SettingsMainScreen.kt:40-47) and mirrored it here. Both apps use the same idiom now.

OnboardingScreen is unchanged — it runs outside AppRoot's Scaffold (as the pre-flight gate), so the double-inset bug never applied.

Tests

197 tests, all green.

Install

adb install -r marmalade-tts-android-0.1.14-debug.apk

v0.1.13 — onboarding prompts to set system TTS engine

Choose a tag to compare

@maxwhipw maxwhipw released this 23 May 03:16

Onboarding gains a fifth (and final) step: "Set Marmalade as your system TTS engine."

The previous wizard left users thinking everything was wired after installing an engine and creating an alias — but Android still requires you to manually pick Marmalade as the default TTS engine in system Settings before any other app's TTS request actually routes here. v0.1.13 makes that step impossible to miss.

What's new

  • New onboarding step with a primary button "Open system TTS settings" that fires a 3-tier intent cascade so we land as close to the right page as the OEM allows (stock Android opens TTS settings directly; Samsung One UI / MIUI / etc. fall back through locale settings → top-level Settings).
  • Secondary affordance "Finish — I'll do this later" for users who want to dismiss the prompt.
  • Also added to Settings → "Set as system TTS engine" (new "System integration" section) so the launcher is reachable any time, not just during onboarding.

Did the engine registration change?

No — the manifest has been registering MarmaladeTtsService as a TTS engine since v0.1.0. The OS-level discovery has worked all along. What was missing was telling users to look in Settings + giving them a one-tap shortcut to get there. If Marmalade still doesn't appear in your phone's TTS engine list after running onboarding, please file an issue — that's a real bug we'd want to dig into.

Install

adb install -r marmalade-tts-android-0.1.13-debug.apk

If you've already onboarded on v0.1.12: open Settings → Set as system TTS engine to land on the right page.

v0.1.12 — per-app voice routing

Choose a tag to compare

@maxwhipw maxwhipw released this 23 May 03:03

The big one: Marmalade now picks a different voice per calling app.

When an app calls Android's system TTS without specifying a voice, the request carries a callerUid. Marmalade resolves it to a package name and looks up your user-configured "this app → this alias" mapping. Falls back to your primary alias (from v0.1.11) when no rule matches. Caller-specified voices still win — this is a fallback, not an override.

How to use it

  1. Settings → Per-app voices
  2. Tap the FAB to add a mapping
  3. Pick an app from the list, pick an alias, save
  4. Next time that app speaks, your chosen alias is used

Use cases: Pocket Casts in your "calm" voice, Telegram in your "narrator" voice, accessibility readers in something high-contrast.

Precedence

  1. Caller-specified voice (from the TTS request) — unchanged, app's choice wins
  2. Per-app mapping
  3. Primary alias
  4. Engine default voice (current fallback when nothing else matches)

Under the hood

  • New AppAliasMapping entity + DAO; DB migrates v3→v4 (additive — CREATE TABLE only, voice_meta + voice_alias untouched)
  • New TtsRouter (@Singleton) used by both system-TTS and foreground-synthesis paths
  • App picker filters PackageManager.getInstalledApplications(...) to apps with a launch intent (drops system services)

Install

adb install -r marmalade-tts-android-0.1.12-debug.apk

Existing v0.1.11 installs upgrade in place; the new app_alias_mapping table is created by the migration. Your primary alias becomes the universal default; per-app mappings layer on top.

v0.1.11 — engines cards + per-engine settings + primary alias + gated onboarding

Choose a tag to compare

@maxwhipw maxwhipw released this 22 May 23:14

Two structural changes that prepare the app for per-app routing (next release).

Engines tab → cards + per-engine settings page

Each engine is now an OutlinedCard with Install / Uninstall / Engine settings buttons. Tap "Engine settings" → land on a per-engine page with status, in-page install affordance, Text Preprocessing rules (moved here from global Settings — it was a per-engine concept all along), description, and license summary.

The global Settings page is now slimmer: Appearance / Engine behavior / Voice aliases / About.

Primary alias + gated onboarding

There's now a designated primary alias — the default voice/effect/speed bundle Marmalade uses when an app asks it to speak without specifying anything. This is the foundation for per-app routing (where you'll be able to say "use Bella for Pocket Casts, Jasper for Tasker, …" — coming in v0.1.12).

  • Primary indicator on the alias list: filled-star icon, primary-tinted, "Primary" chip beside the name. Tap a dimmed star on any other row to promote that alias.
  • Onboarding wizard now has a fourth step: Create primary alias. Two paths through it — full inline editor (name / engine / voice / speed / effect) or "Use defaults" which auto-creates a "default" alias with the recommended engine's default voice. Both mark the new alias as primary and complete onboarding.
  • Existing aliases self-heal: if a user is dropped into a state with aliases but no primary set, the first alias gets promoted automatically.

Tests

181 → 197 (+16). All green.

Install

adb install -r marmalade-tts-android-0.1.11-debug.apk

Existing v0.1.10 installs: the new "Engine settings" buttons appear on the Engines tab. If you have aliases but no primary was set (impossible on fresh installs — this is for sideloaded-data edge cases), launching the app self-heals by promoting the first alias to primary.

v0.1.10 — install indicator + light/dark + Marmalade default + grouped voices

Choose a tag to compare

@maxwhipw maxwhipw released this 22 May 22:53

Four small UX fixes, all visible on first launch:

  1. Engines screen install indicator. After download hit 100%, the row used to sit with a "…" character with no progress bar while EngineInstaller did the tar.bz2 extract — felt frozen, especially on Kokoro's 99 MB bundle (5-20s extract). Now you get a spinner in the action slot AND an indeterminate progress strip below with the label "Installing · unpacking model files…".

  2. Light / Dark / System override. Theme used to follow the OS exclusively. Settings → Appearance now has a Mode chip group (System / Light / Dark) above the color preset chip group. Decoupled: pick "Marmalade always dark" or "Forest System" or whatever combo.

  3. Default theme is now Marmalade. New installs used to default to Material You (System preset); now they default to the orange Marmalade palette. The brand identity is the brand identity.

  4. Voice picker groups by engine. With Kokoro + Kitten both installable, the flat list was confusing. The picker now has a section header per engine — Kokoro first, then Kitten — so it's obvious which voices belong to which engine. Effective engine-switching is just picking a voice from a different section.

Install

adb install -r marmalade-tts-android-0.1.10-debug.apk

The mode/preset preferences are new; existing v0.1.9 installs will default-back to Marmalade + System mode (which matches their previous behavior in light mode — dark-mode users will see no change to their experience).

v0.1.9 — Kokoro engine + per-engine text preprocessing

Choose a tag to compare

@maxwhipw maxwhipw released this 22 May 22:29

Two paired features:

Kokoro TTS (new recommended engine)

Higher-quality English TTS — 11 voices (American + British, male + female). Sounds meaningfully more natural than Kitten v0.8 at the cost of a ~3× larger install (99 MB compressed / 151 MB on-disk vs Kitten's 31 / 44 MB).

  • Apache-2.0 Kokoro model + GPL-3.0 espeak-ng phonemizer (same disclosure as Kitten)
  • Engines-repo v4 release — byte-identical mirror of Sherpa-ONNX upstream
  • New default: onboarding pre-selects Kokoro; Kitten is still installable as a smaller alternative
  • Default voice: kokoro:af_bella (American female)

Per-engine text preprocessing (editable in Settings)

Ported the marmalade-tts CLI's preprocessing system to Android. 15 rules — currency normalization ("$3.50" → "3 dollars and 50 cents"), number-to-words, ordinals, abbreviations, HTML decode, URLs, emails, percentages, time, date, math, ampersand, hashtag — each toggleable per-engine in Settings → Text preprocessing.

  • Year-band protection (1900–2099 stays as digits in normal flow)
  • Pure-Kotlin spell-out (no new deps)
  • Per-engine profiles — Kitten and Kokoro have curated defaults; users can override per rule, per engine
  • "Reset to defaults" button per engine

Tests

122 → 181 (+59). All green.

Install

adb install -r marmalade-tts-android-0.1.9-debug.apk

Upgrading from v0.1.8: existing Kitten install stays valid. To try Kokoro, go to Engines tab and install. To curate preprocessing, go to Settings → Text preprocessing.

v0.1.8 — theme + bottom nav + Settings page

Choose a tag to compare

@maxwhipw maxwhipw released this 22 May 22:04

Visual overhaul. Pulls the theme + nav idiom from the sister marmalade-android (OpenClaw chat client) so the two apps feel like family.

What's new

Theme. 5 presets — System (Material You dynamic), Marmalade (the hand-tuned orange), Midnight, Forest, Berry. Light/dark auto-follow system.

Bottom navigation. Speak / Voices / Engines / Settings as proper tabs. Tab switching doesn't grow the back stack. The Voice aliases screen lives off Settings.

Settings page. Single page, four sections — Appearance (theme picker), Engine behavior (keep-loaded toggle), Voice aliases entry, About.

The "keep engine loaded" toggle stores the preference today; runtime release-on-toggle is a follow-up.

Install

adb install -r marmalade-tts-android-0.1.8-debug.apk

The bottom bar replaces the old top-bar overflow menu — if you used Engines or Voice aliases on v0.1.7 those moved. Engines is now its own tab; Voice aliases lives on Settings.

v0.1.7 — APK 131 MB → 66 MB (drop emulator ABIs)

Choose a tag to compare

@maxwhipw maxwhipw released this 22 May 21:46

Build hygiene: stop shipping native libs for ABIs no real Android device runs.

ABI Real-device use Status
arm64-v8a Modern phones (99% of devices) ✅ kept
armeabi-v7a Older 32-bit ARM phones ✅ kept
x86 Emulator-only ❌ dropped (saved 35 MB)
x86_64 Emulator-only ❌ dropped (saved 24 MB)

APK: 131 MB → 66 MB. No functional change. Anyone wanting to run the app in an x86 emulator can do a from-source build.

(The x86 ABI was particularly wasteful — sherpa-onnx ships a separate 31 MB libonnxruntime.so for x86 because the AAR's "static-link" naming is misleading for that one ABI.)

v0.1.6 — Sherpa-ONNX 1.13.2 (Kitten v0.8 runtime support)

Choose a tag to compare

@maxwhipw maxwhipw released this 22 May 09:49

v0.1.5 had the right v0.8 model files on disk, but synthesis crashed because the vendored Sherpa-ONNX runtime AAR (1.12.32) was too old — KittenTTS v0.8 runtime support landed in sherpa-onnx v1.13.2 (May 13, 2026). v0.8's model ships extra ONNX metadata that older Sherpa-ONNX doesn't read; 1.12.32 tried v0.1's pipeline on a v0.8 model and JNI-faulted.

Upgraded to 1.13.2 (also the current latest, so we're not floored at a minimum-required version). APK grows ~2 MB.

Install

adb install -r marmalade-tts-android-0.1.6-debug.apk

The engine bundle on disk from v0.1.5 is already correct. Just launch and Speak — synthesis should actually run now.

Engine version chain so far

  • Sherpa-ONNX AAR: 1.13.2 (was 1.12.32)
  • Kitten model: kitten-nano-en-v0_8-int8 (was v0_1-fp16)
  • Engines repo release: v3
  • Speaker IDs: male-first per upstream (was female-first / gender-swapped)
  • verifyLayout: matches model*.onnx glob (was hard-coded model.fp16.onnx)