Skip to content

Add arktts (Audio8-TTS-Preview-0.6b): DualAR TTS with zero-shot voice cloning - #867

Merged
lucasnewman merged 3 commits into
Blaizzy:mainfrom
xocialize:add-arktts
Jul 31, 2026
Merged

Add arktts (Audio8-TTS-Preview-0.6b): DualAR TTS with zero-shot voice cloning#867
lucasnewman merged 3 commits into
Blaizzy:mainfrom
xocialize:add-arktts

Conversation

@xocialize

Copy link
Copy Markdown
Contributor

Adds the arktts model family — Audio8/Audio8-TTS-Preview-0.6b (Apache-2.0), a 0.6B multilingual TTS model with zero-shot voice cloning and a bundled 44.1 kHz codec. Architecture is DualAR (Fish S2 Pro-style): a 24-layer slow AR transformer emits one semantic token per frame; a 4-layer fast AR emits the frame's 10 codec codebooks conditioned on the slow hidden state.

What's included

  • mlx_audio/tts/models/arktts/ — model (arktts.py), codec (codec.py, DAC-style encoder/decoder + split semantic/residual RVQ + windowed transformer pre/post modules), mirroring the upstream remote-code structure file-for-file
  • Reference-exact sampling: semantic-range logit filter, legacy top-k/top-p order (filter before temperature), exponential-race sampling, RAS repetition rescue
  • sanitize() consumes either raw upstream checkpoints (folds both parametrized and legacy weight norm, transposes conv layouts, reshapes Snake alphas) or the pre-converted mlx repo (idempotent)
  • mlx_audio/tts/tests/test_arktts.py — sanitize contract + tiny-model forward/generation tests (no weights needed)

Converted weights

mlx-community/Audio8-TTS-Preview-0.6b-bf16 (bf16 LM + fp32 codec), validated by fresh-download → mlx_audio.tts.utils.load → voice-clone generation.

Parity vs PyTorch reference (fp32, CPU stream)

  • unit/block outputs: max-abs 1e-6…1e-4
  • reference-audio codec encode: 100% code-exact
  • greedy generation: 100% token-exact over a 102-frame validation utterance
  • decoded waveform: max-abs 7.5e-6
  • bf16 GPU smoke: healthy audio (−18 dBFS RMS), RTF ~0.8–0.9 on M-series

Usage

from mlx_audio.tts.utils import load
model = load("mlx-community/Audio8-TTS-Preview-0.6b-bf16")
for r in model.generate(text="Hello from MLX.", ref_audio="ref.wav", ref_text="Reference transcript."):
    ...

🤖 Generated with Claude Code

xocialize and others added 2 commits July 30, 2026 09:03
… cloning

Adds the arktts model family: a DualAR transformer (24-layer slow AR emitting
one semantic token per frame + 4-layer fast AR emitting 10 codec codebooks),
with the bundled 44.1 kHz codec (DAC-style encoder/decoder, split semantic +
residual RVQ, windowed transformer pre/post modules) and the reference prompt
format for zero-shot voice cloning.

Parity vs the PyTorch reference (fp32, CPU stream): unit/block outputs within
1e-4, reference-audio encode 100% code-exact, greedy generation 100%
token-exact over a 102-frame validation utterance, decoded waveform max-abs
7.5e-6. Converted weights: mlx-community/Audio8-TTS-Preview-0.6b-bf16.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Formatting only (line wrapping) — the repo's pre-commit style hook reformatted
arktts.py, codec.py, and test_arktts.py. No semantic changes: the 7 unit tests
still pass and the PyTorch parity harness was re-run to confirm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
]
self.fast_norm = ArkttsRMSNorm(config.fast_dim, config.norm_eps)
self.fast_output = nn.Linear(config.fast_dim, config.codebook_size, bias=False)
self._freqs_cis = _precompute_rope(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

These two cache arrays need to be materialized with e.g. mx.eval((self._freqs_cis, self._fast_freqs_cis)) at init time so the model doesn't crash moving across streams/threads.

@lucasnewman lucasnewman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@xocialize Very nice, thank you! Can you fix the cache array issue I mentioned and add a README for the model, and then we can merge?

Review feedback from @lucasnewman:

1. The two rope tables are plain attributes, not module parameters, so nothing
   else forces them. Left lazy, their graph would first be evaluated inside
   whichever stream/thread ran the first forward, which crashes when the model is
   moved across streams. Now mx.eval'd at init. Verified by constructing on the
   GPU stream and running a forward on the CPU stream.

2. Added mlx_audio/tts/models/arktts/README.md following the house style
   (Voxtral/confucius4): supported model, Python + CLI usage, languages, options,
   architecture, conversion notes, parity numbers.

   The CLI block documents only flags that exist — mlx_audio.tts.generate has no
   --ref-audio/--ref-text, so cloning is noted as Python-API only rather than
   showing a command that would fail.

7/7 arktts tests green; black/isort clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@xocialize

Copy link
Copy Markdown
Contributor Author

Thanks for the review! Both addressed:

1. Cache arrays. Good catch — the two rope tables are plain attributes rather than module parameters, so nothing else was forcing them. They're now mx.eval'd at the end of __init__. Verified the failure mode you described is gone by constructing on the GPU stream and running a forward on the CPU stream, which is what would previously have evaluated the graph in the wrong context.

2. README added at mlx_audio/tts/models/arktts/README.md, following the Voxtral/confucius4 shape: supported model, Python + CLI usage, languages, options, architecture, conversion notes, parity numbers.

One small thing worth flagging: I'd initially written the CLI example with --ref-audio/--ref-text, then checked and found mlx_audio.tts.generate has no such flags. The CLI block now shows only the default-voice path and notes that cloning is Python-API only — same as confucius4 documents it. If exposing reference audio through the CLI would be useful I'm happy to add it in a follow-up, but it seemed out of scope here.

@xocialize
xocialize requested a review from lucasnewman July 31, 2026 05:11

@lucasnewman lucasnewman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks!

@lucasnewman
lucasnewman merged commit bca8c45 into Blaizzy:main Jul 31, 2026
12 checks passed
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.

2 participants