Skip to content

Add Romanian TTS support via a new MMS engine - #916

Open
at-aepeak-com wants to merge 5 commits into
jamiepine:mainfrom
at-aepeak-com:feat/mms-romanian
Open

Add Romanian TTS support via a new MMS engine#916
at-aepeak-com wants to merge 5 commits into
jamiepine:mainfrom
at-aepeak-com:feat/mms-romanian

Conversation

@at-aepeak-com

@at-aepeak-com at-aepeak-com commented Jul 19, 2026

Copy link
Copy Markdown

Closes #578

What

New mms TTS engine wrapping facebook/mms-tts-ron (Meta MMS, VITS architecture), adding Romanian as Voicebox's 24th language. Single preset voice, no cloning — follows the existing Kokoro preset-engine pattern (kokoro_backend.py was the structural template).

Zero new dependencies — uses the already-pinned transformers. ~145 MB model, CPU-realtime, 16 kHz output, works on all platforms (no CUDA/platform gating needed).

Notable: Romanian diacritics handling

The MMS Romanian tokenizer's vocab is mixed: it contains comma-below ș (U+0219) but cedilla ţ (U+0163), and silently drops the other two variants (ş U+015F, ț U+021B) — the tokenizer strips characters not in its vocab with no error. Real-world Romanian text freely mixes both spellings, so without handling this, letters silently vanish from the audio.

The backend NFC-normalizes input and maps each diacritic to the form the checkpoint was trained on (ş→ș, ț→ţ, uppercase included). An e2e test proves both spellings produce identical token ids with zero dropped characters.

Extensible by design

MMS_HF_REPOS is a module-level {language: checkpoint} map — further MMS languages are one-line additions (e.g. Croatian mms-tts-hrv, requested in #549). Only Romanian ships in this PR to keep it reviewable.

Changes

  • backend/backends/mms_backend.py (new) — MMSTTSBackend implementing the TTSBackend protocol
  • Registration: TTS_ENGINES, ModelConfig, factory branch in backends/__init__.py; ro/mms added to validation regexes in models.py; preset-voice branches in routes/profiles.py + services/profiles.py; PyInstaller hidden-import in build_binary.py
  • Frontend: ro in ALL_LANGUAGES, mms in ENGINE_LANGUAGES, engine wired through the selector, profile forms, model management, and format/display maps
  • Docs: README counts, preset-voices.mdx, PROJECT_STATUS.md, CHANGELOG

Testing

  • 31 unit tests (diacritic normalization both directions, NFC/NFD, registration, regexes, preset endpoint)
  • 4 e2e tests gated behind VOICEBOX_MMS_E2E=1 (real model download + generation: 16 kHz float32, no NaNs; token-identity proof for both diacritic spellings; seeded determinism)
  • Full existing suite: green, zero regressions
  • bunx biome check / ruff check clean on touched files (no new findings)
  • Verified end-to-end in the web app: profile creation via Create Voice → Built-in → MMS Romanian, model download with progress, generation from the floating box, and language-dropdown gating (Romanian-only when the MMS engine is active; other engines unchanged)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added MMS (Meta) TTS as an eighth voice-generation engine.
    • Added Romanian (ro) support with an MMS Romanian preset voice.
    • Added Romanian diacritic normalization to improve text handling for synthesis.
    • Updated engine/model selection, voice profiles, model management, and playback to support mms.
  • Documentation

    • Updated feature claims, engine/language listings, and preset-voice documentation to include MMS Romanian.
  • Tests

    • Added a dedicated MMS backend test suite (including Romanian diacritics and optional end-to-end generation).

aepeak and others added 4 commits July 19, 2026 08:47
New 'mms' engine wrapping facebook/mms-tts-ron (Meta MMS, VITS
architecture) — pure PyTorch via the already-pinned transformers
dependency, zero new packages. Single preset voice, no cloning,
modeled on the Kokoro preset-voice pattern.

The mms-tts-ron vocab mixes Romanian diacritic conventions (comma-below
s U+0219 but cedilla t U+0163), and the character-level VitsTokenizer
silently drops out-of-vocab characters, so input text is NFC-normalized
and both real-world variants are mapped onto the trained forms before
tokenizing.

Registered in the model config registry, engine factory, request-model
regexes (language 'ro', engine 'mms'), preset-voice endpoints, and the
PyInstaller hidden imports.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Romanian added to ALL_LANGUAGES, 'mms' wired through every engine
enumeration: engine union types, zod schema, engine picker, display
names, preset-engine sets (profile form/list/card, floating generate
box), model management filter, and captures play-as union. MMS is
treated as a preset-voice engine like Kokoro and Qwen CustomVoice.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Unit coverage for the Romanian diacritics normalization (cedilla vs
comma-below variants, NFC composition, lossless handling of unknown
chars), engine registration surfaces, and request-model regexes.

End-to-end generation tests (model download ~150MB) are opt-in via
VOICEBOX_MMS_E2E=1: Romanian text with both diacritic conventions
produces valid 16kHz float32 audio, both spellings tokenize
identically, seeded generation is deterministic, and fully
out-of-vocab text falls back to silence.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
README engine/language counts (7 to 8 engines, 23 to 24 languages) and
tables, preset-voices docs page with the MMS section, PROJECT_STATUS
engine tracking, and a CHANGELOG entry under Unreleased.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 57eaae4a-4f67-419c-8bb6-975e0b39bba5

📥 Commits

Reviewing files that changed from the base of the PR and between b587d84 and 6a82698.

📒 Files selected for processing (4)
  • README.md
  • app/src/components/CapturesTab/CapturesTab.tsx
  • backend/backends/mms_backend.py
  • backend/models.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/models.py
  • README.md
  • backend/backends/mms_backend.py

📝 Walkthrough

Walkthrough

Adds Romanian MMS-TTS as a new preset engine with backend inference, diacritic normalization, model and voice registration, frontend selection, request validation, tests, packaging, and documentation updates.

Changes

MMS Romanian TTS

Layer / File(s) Summary
MMS backend and engine registration
backend/backends/..., backend/build_binary.py
Adds the MMS engine registry entry, Romanian checkpoint configuration, lazy model lifecycle, preset voice behavior, normalized text generation, and standalone-binary packaging.
Romanian contracts and preset voice plumbing
backend/models.py, backend/routes/profiles.py, backend/services/profiles.py, app/src/lib/constants/languages.ts, backend/tests/test_mms_backend.py
Accepts Romanian and MMS values across request models, exposes MMS preset voices, and tests normalization, registration, validation, lifecycle, and opt-in generation.
Frontend engine and profile integration
app/src/components/..., app/src/lib/...
Adds MMS to engine selectors, generation routing, profile controls, display names, model management, capture playback, and API types.
Documentation and release references
README.md, CHANGELOG.md, docs/...
Documents MMS as the eighth TTS engine and Romanian preset voice option.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant EngineModelSelector
  participant useGenerationForm
  participant BackendFactory
  participant MMSTTSBackend
  User->>EngineModelSelector: choose MMS Romanian
  EngineModelSelector->>useGenerationForm: set engine to mms
  useGenerationForm->>BackendFactory: submit mms-tts-ron request
  BackendFactory->>MMSTTSBackend: create or reuse MMS backend
  MMSTTSBackend-->>User: return Romanian waveform
Loading

Possibly related PRs

  • jamiepine/voicebox#325: Adds a related preset-engine integration pattern across frontend profiles and backend preset voice plumbing.
  • jamiepine/voicebox#328: Introduces related engine registration, dispatch, and frontend wiring patterns.
  • jamiepine/voicebox#257: Extends the shared engine registration and generation type plumbing.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding Romanian TTS support through a new MMS engine.
Linked Issues check ✅ Passed The PR implements Romanian language support requested in #578 by adding an MMS backend, validation, and UI/documentation wiring.
Out of Scope Changes check ✅ Passed The changes stay focused on Romanian/MMS TTS support, with only supporting updates to validation, docs, UI, backend, and tests.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/components/CapturesTab/CapturesTab.tsx (1)

266-278: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Handle MMS’s Romanian-only language constraint.

When a user plays a capture through an MMS profile, this code prefers capture.language. A non-Romanian capture therefore sends engine: 'mms' with (for example) en, even though MMS only supports Romanian. Reject this combination in the UI or explicitly validate against the selected profile language before calling generateSpeech.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/components/CapturesTab/CapturesTab.tsx` around lines 266 - 278,
Update the capture playback flow around the engine selection and generateSpeech
call to prevent MMS from being used with any non-Romanian language. Validate the
resolved language against the selected voice profile before calling
generateSpeech, and reject the request through the existing UI error path rather
than sending an invalid engine/language combination.
🧹 Nitpick comments (1)
backend/backends/mms_backend.py (1)

14-15: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Docstring overstates how easy adding a language is.

The module doc says adding a language is "a one-line addition to MMS_HF_REPOS," but _get_model_path and _load_model_sync always resolve to MMS_HF_REPOS[MMS_DEFAULT_LANGUAGE] rather than any language actually requested — a new MMS_HF_REPOS entry wouldn't be reachable without further code changes to thread the language through model loading.

✏️ Clarify the docstring (or parameterize by language)
-Adding a language is a one-line addition to ``MMS_HF_REPOS`` plus a voice
-entry in ``MMS_VOICES`` and a ``ModelConfig`` registration.
+Adding a language currently requires updating ``MMS_HF_REPOS``, ``MMS_VOICES``,
+a ``ModelConfig`` registration, *and* threading the target language through
+``_get_model_path``/``_load_model_sync`` (which are hardcoded to
+``MMS_DEFAULT_LANGUAGE`` today).

Also applies to: 98-99, 111-124

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/backends/mms_backend.py` around lines 14 - 15, Correct the module
docstring to stop claiming that adding a language only requires one MMS_HF_REPOS
entry, and describe the additional model-loading changes currently required.
Update the documentation near _get_model_path and _load_model_sync consistently,
unless you instead parameterize those functions to resolve MMS_HF_REPOS using
the requested language and preserve the existing default-language behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/models.py`:
- Line 21: Extract the duplicated language-code and engine regex patterns into
shared constants in the models module, then replace the inline patterns in
VoiceProfileCreate, GenerationRequest, MCPClientBindingResponse,
MCPClientBindingUpsert, and SpeakRequest with those constants. Preserve the
current accepted values, including ro and mms, so all validation surfaces remain
consistent.

In `@README.md`:
- Around line 75-77: Update the README preset-voices bullet to mention the
Romanian MMS preset voice alongside Kokoro and Qwen CustomVoice, while
preserving the existing voice-cloning and preset-voice descriptions.

---

Outside diff comments:
In `@app/src/components/CapturesTab/CapturesTab.tsx`:
- Around line 266-278: Update the capture playback flow around the engine
selection and generateSpeech call to prevent MMS from being used with any
non-Romanian language. Validate the resolved language against the selected voice
profile before calling generateSpeech, and reject the request through the
existing UI error path rather than sending an invalid engine/language
combination.

---

Nitpick comments:
In `@backend/backends/mms_backend.py`:
- Around line 14-15: Correct the module docstring to stop claiming that adding a
language only requires one MMS_HF_REPOS entry, and describe the additional
model-loading changes currently required. Update the documentation near
_get_model_path and _load_model_sync consistently, unless you instead
parameterize those functions to resolve MMS_HF_REPOS using the requested
language and preserve the existing default-language behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5204a75c-b666-41bc-b29d-661591e604f4

📥 Commits

Reviewing files that changed from the base of the PR and between f2cf2a7 and b587d84.

📒 Files selected for processing (22)
  • CHANGELOG.md
  • README.md
  • app/src/components/CapturesTab/CapturesTab.tsx
  • app/src/components/Generation/EngineModelSelector.tsx
  • app/src/components/Generation/FloatingGenerateBox.tsx
  • app/src/components/ServerSettings/ModelManagement.tsx
  • app/src/components/VoiceProfiles/ProfileCard.tsx
  • app/src/components/VoiceProfiles/ProfileForm.tsx
  • app/src/components/VoiceProfiles/ProfileList.tsx
  • app/src/lib/api/types.ts
  • app/src/lib/constants/languages.ts
  • app/src/lib/hooks/useGenerationForm.ts
  • app/src/lib/utils/format.ts
  • backend/backends/__init__.py
  • backend/backends/mms_backend.py
  • backend/build_binary.py
  • backend/models.py
  • backend/routes/profiles.py
  • backend/services/profiles.py
  • backend/tests/test_mms_backend.py
  • docs/PROJECT_STATUS.md
  • docs/content/docs/overview/preset-voices.mdx

Comment thread backend/models.py Outdated
Comment thread README.md
- CapturesTab: only honor a capture's language when the target engine
  supports it (MMS is Romanian-only; also hardens Kokoro playback),
  falling back to the profile's language otherwise
- models.py: extract shared TTS_LANGUAGE_PATTERN / TTS_ENGINE_PATTERN
  constants to replace seven duplicated inline regexes
- mms_backend: correct module docstring — adding a language also needs
  the checkpoint resolution threaded through model loading
- README: mention MMS (Romanian) in the preset-voices bullet

Co-Authored-By: Claude Opus 4.8 <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.

Support for Romanian language -> ro-ro

2 participants