~/satyajit

Nar TTS: the two rewards it built and refuses to switch on

mdjsonmcp

2026-08-23 · 7 min · tts · grpo · reinforcement-learning

Why read this

Nichelast 15%

Reads a small TTS repo for its evaluation discipline: two rewards built and held at zero so the judge is never the model it trained against.

  • Original analysis
  • A lasting reference

Speech & audioNeeds datacenter GPUsPractitioner tool

How this was scored
Is it new?
1 of 3: An incremental tweak
Can I trust it?
1 of 3: Spot-checks a few numbers
Can I run it?
1 of 3: API-only, gated or restrictive licence
Will I understand it?
1 of 3: Partial mechanism
Can I act on it?
1 of 3: General advice
Will it last?
2 of 3: A reference for a year or more
Does it affect many?
0 of 3: A niche paper or one-off repo
Only here?
2 of 3: A teardown or measurement few others did

Score 39 of 100, ranked 535 of 577 rated articles. Each question is answered 0–3 by hand, and a 3 is rare. How articles are scored

Nar TTS combines a user-selected causal language model with Mimi speech tokens. It does quality-gated inference, expressive controls, supervised training and GRPO, without model-specific config files. That description would fit a hundred repositories.

What makes it worth reading is a paragraph in docs/quality.md:

The emotion and non-verbal event reward implementations are ready, but their weights remain zero. Do not enable them until an independently validated classifier has been selected using synthetic Turkish speech. Using the same SER model as both the reward and the success metric encourages reward hacking.

Two rewards, built, wired in, and deliberately held at zero — with the reason written down. That discipline shows up three separate times in this repository, and it is not something most projects with far more resources maintain.

Repokadirnar/nar-tts · Python 3.10, CUDA 12.x
Designany causal LM + Mimi speech tokens · no model-specific config files
Trainingpretrain → optional SFT → optional GRPO, FSDP via accelerate, LoRA optional
GRPO8 generations per group across 8 GPUs · six active rewards, two held at zero
Inference2 candidates, 4 on gate failure · Whisper verification · JSON quality report
Controls6 emotions × 3 deliveries × 8 non-verbal events, as text markup
CreditsOrpheus TTS, Qwen3, Mimi

Never judge with the model you trained against

The active reward mix is sensible and unsurprising: 60% intelligibility from Qwen3-ASR CER plus ground-truth NLL, 15% speaker similarity from WavLM-Large and ECAPA, then duration, technical signal quality, prosody and speaker drift. They sum to exactly 1.00, and each is normalized inside its own prompt group before the weighted sum — necessary, because CER, cosine similarity and a clipping diagnostic share no scale.

The two rows at zero are the article. The project's own success criteria include emotion accuracy and event F1, both scored by a classifier. Put that classifier into the reward and the model optimizes its agreement with the judge rather than the thing the judge was standing in for — and the evaluation, which uses the same judge, reports that it worked.

The same instinct appears twice more:

And a sentence I would put on a poster: "technical_quality is a signal diagnostic, not a MOS or naturalness model. An emotion-classifier score alone is not evidence of product quality."

The quality gate is best-of-N with the bill itemized

Default inference generates two candidates, verifies them, and expands to four only when the gate fails. The gate checks CER, speaker similarity, duration, clipping, silence and repetition — with the last one being the characteristic failure of autoregressive TTS, and the one a listener notices immediately.

Adaptive best-of-N reaches exactly the same success rate as always generating four, because the second pair is produced precisely when it is needed. It just only pays for it on the requests that need it: at a gate pass rate of 80%, that is 2.08 generations instead of 4, and it degrades gracefully rather than failing as the pass rate drops.

The part that makes this a system rather than a trick is what it writes out: the winning WAV, every candidate, and a machine-readable JSON report including a real-time factor. The docs are explicit that best-of-N and verification cost extra compute and that real_time_factor is how you compare speed against quality on your own data. A best-of-N scheme whose cost you can measure is a feature; one whose cost is invisible is a surprise on the invoice.

There is also a content-addressed Mimi token cache for repeated reference audio, true batched generation with a KV cache, and sentence splitting for long text with acoustic context carried from the previous chunk plus crossfading. That last one is the detail that separates a demo from something you would run on a paragraph.

Controls that do not require retraining

Nar exposes three independent controls — six emotions, three deliveries, eight non-verbal events — and renders them as text markup that does not modify the tokenizer.

That is a deliberate architectural choice, and the repository explains the cost of the alternative in a section called changes that require retraining: if the codec, the speech-token layout, or the special control tokens change, all speech data must be re-encoded and the model retrained. Alternative codecs, a new decoder and true frame-level streaming are all listed there too, as separate model generations rather than upgrades. Keeping expressive control in the text stream keeps it on the near side of that line.

The taxonomy is more careful than most, too: speech_laugh means the text is spoken with laughter while laugh is a separate laughter event, and crying_speech and sob are annotated separately. Most expressive TTS interfaces collapse those into one label and then wonder why the control is unreliable.

And then, in the same document: "The current checkpoint has not learned this markup, so it cannot produce crying speech or laughter on its own. These capabilities require an expressive SFT checkpoint." The interface is shipped; the capability is a labelled-data problem, and the repo says so rather than letting the API imply otherwise.

The data loop

The pipeline in docs/quality.md is worth drawing because it closes.

Where the thin parts are

There are no numbers. The success criteria are well specified — Turkish, English and Japanese CER and WER, speaker similarity and long-form drift, p50/p95 RTF, VRAM, time to first chunk, clipping/silence/repetition/truncation rates, blinded human A/B — and none of them are reported. This is a repository that documents how it would evaluate a release without having published one.

The GRPO weights are asserted, not ablated. 0.60 / 0.15 / 0.08 / 0.07 / 0.05 / 0.05 is a plausible allocation and nothing shows what happens if intelligibility drops to 0.4, or what the speaker-drift term is worth on long-form output. For a repository this careful about not fooling itself, the weights are the one place that reads as taste rather than measurement.

"Any causal LM" is a strong claim with one worked example. The design goal is no model-specific config files — you run inspect-tokenizer, copy text_eos_token_id and pad_token_id into three YAMLs, and go. Qwen3 is credited. Whether a Mimi speech-token head grafts equally well onto a differently-shaped model is the claim the architecture rests on, and it is untested in public.

The Turkish focus is a feature and a constraint. Examples are Turkish, normalization covers Turkish and English, and the reward-hacking warning specifically says to validate the emotion classifier on synthetic Turkish speech. Good TTS tooling for languages outside the usual five is genuinely undersupplied. It also means the evaluation set the whole quality system is designed around does not exist publicly yet.

Why a repository with no benchmarks is worth an article

Nar TTS has no results table, no model weights, and no demo I can point at. What it has is a set of decisions written down in the order they were made, each with its reason attached — and several of those decisions are ones larger projects get wrong.

Do not use the same model as reward and metric. Do not evaluate a release with the ASR you trained against. Do not claim a control surface works when the checkpoint has not learned it. Do not put controls in special tokens if you cannot afford to re-encode your corpus. Do not report a signal diagnostic as a naturalness score.

None of those are novel. All of them are the kind of thing that gets skipped under deadline, and the resulting model looks great on the metric it was trained to satisfy. A repository that writes them down as rules — and then holds two finished rewards at zero to obey one — is worth more attention than its star count suggests.

Cite this article

For attribution, please use the following reference or BibTeX:

Satyajit Ghana, "Nar TTS: the two rewards it built and refuses to switch on", ai.thesatyajit.com, August 2026.

bibtex
@misc{ghana2026nartts,
  author = {Satyajit Ghana},
  title  = {Nar TTS: the two rewards it built and refuses to switch on},
  url    = {https://ai.thesatyajit.com/articles/nar-tts},
  year   = {2026}
}
share