This was the one open prerequisite for any target-language audio lane: the survey in spec 26 says the model covers Chinese, but that is a model-card claim, and our whole voice catalogue is English references. Nothing had been measured about what happens when an English reference is asked to speak Chinese. scripts/probe_cross_language_tts.py now also takes an optional second English reference and makes the identity question differential rather than a transcription question - a transcript has no timbre. Two English references reading the same text should stay as far apart when they read Chinese as when they read English; if they do, the reference is carrying identity into the Chinese output rather than being ignored. The measurement is made with ECAPA-TDNN cosine similarity. The result, on the real Deal Hunter narrator and chief-of-the-guard references, is in logs/cross_language_tts_probe/probe-report.json: - the English reference speaks Chinese. Whisper hears zh on every Chinese-text clip and en on every English-text clip, recovering 12 of the source's 22 distinct characters. - the text's script decides the language, not the `language` parameter. Chinese text with language=english still comes back as Chinese, so a caller that trusted the parameter to select a language would get the wrong phonology without an error. - the reference is carried: different voices are 0.383 apart reading English and 0.369 apart reading Chinese. What the measurement does not establish is stated in the spec rather than glossed: ECAPA is an English VoxCeleb model, so its Chinese embeddings are out of domain and the same-speaker/different-speaker separation collapses in Chinese. It can show that two clips differ; it cannot certify that two clips are the same speaker across a language boundary. How much of the identity survives is still a listening question, and the clips are saved for it. Spec 26 gains the measurement and a sixth open question - whether the identity survives for all 61 speakers, not just the two measured. Spec 27 gains the parameterisation record (UmbraTrans 3f05489) and a measurement of what the Chinese edition actually contains: 4,305 delivery rows, of which the 90 not-voiced rows are 44 normal collapses and 46 drift-dropped paragraphs covering 33 paragraphs and 356 source words, about 2.4 minutes of speech. Two of the dropped paragraphs are upstream translation defects the aligner cannot fix - ch-15-p0100 and ch-25-p0089 carry text that belongs to a different sentence - so fixing them means re-translating. Suite: 913 passed, 8 failed (the same pre-existing pronunciation and voice_profile_manager accent failures), 6 skipped.
UmbraAudio
Quality-first multi-pass audiobook generation from English manuscripts.
Features
- 5-pass pipeline: Analysis → Voice Design → Mapping → Test → Generate
- Multi-engine TTS: Chatterbox (primary), Qwen3-TTS (secondary)
- LLM-enhanced: BookNLP for NLP, LLM for profiling/emotion/pronunciation
- Web review: FastAPI + HTMX at checkpoints for human approval
Quick Start
python -m umbraaudio.cli init <manuscript.epub> --name 'My Book'
python -m umbraaudio.cli analyze
python -m umbraaudio.cli voices
python -m umbraaudio.cli review
Requirements
- Python 3.11+
- NVIDIA GPU with CUDA support (RTX 3060 or better recommended)
- ffmpeg
Development
pip install -e ".[dev]"
ruff check umbraaudio/
pytest tests/ -v
Commit cadence: commit at least once every Sunday. Never leave a week (let alone a month) of work uncommitted — a single accidental
git checkouton 2026-09-20 wiped out a month of changes toumbraaudio/web/routes.py. SeeAGENTS.md→ Commit Cadence.
scripts/commit_check.sh enforces the nudge: it exits non-zero once the tree has been dirty
longer than a week. The LaunchAgent com.umbraaudio.commitcheck runs it every Sunday at
18:00 and posts a macOS notification when there is uncommitted work.
scripts/commit_check.sh # warn if dirty > 7 days
scripts/commit_check.sh --days 3 # tighter threshold
scripts/commit_check.sh --any # warn on any uncommitted change
Rebuilding from source
The repo tracks source only; large runtime data is reconstructed as needed.
-
Models: weights are not committed (see
.gitignore). Download them:bash scripts/download_models.sh # ~9 GB, targets ./models -
Qwen3-TTS is a git submodule pinned to upstream commit
022e286:git submodule update --init --recursiveThe
tts_server/web-demo backend includes a local UI customization toqwen_tts/cli/demo.py. It is preserved as a patch — reapply it after checking out the submodule:cd Qwen3-TTS && git apply ../scripts/qwen3_tts_demo_umbra_patch.patch -
Environment: copy/keep
.env(contains secrets) and setUMBRA_TTS__*model paths undermodels/.
Book projects (e.g. DealHunterShip_...) are committed separately under their
own project directories and are not part of this program repository.