Maya-2 Native on MLX (Apple Silicon)

An unofficial MLX port of the inference code for maya-research/maya2-native, the open-weight Indic + English text-to-speech model by Maya Research. All credit for the model goes to them. This repo contains code only: it loads the official weights from maya-research/maya2-native as they are, with no conversion step and no re-upload.

The official release needs Linux and an NVIDIA GPU (CUDA graphs, Triton kernels). This port runs it natively on a Mac's GPU, one stream at a time. Every component was checked against the original PyTorch model (see Parity).

Listen

Generated on an M4 Max with this port (default exact mode):

Language Voice Sample
English Siddhant
Hindi Ritvik
Tamil Abhay
Telugu Nivedita

Install

Needs macOS on Apple Silicon and Python 3.10–3.12.

python -m venv .venv && source .venv/bin/activate
pip install "maya2-mlx @ git+https://huggingface.co/aruntemme/maya2-native-mlx"

This also installs the official maya3 package from GitHub, pinned to the commit the parity checks were run against. The release's text normaliser, prompt packer, voices and guards are reused from it. The weights (~5 GB), the Mimi codec and the ECAPA speaker encoder download from Hugging Face on first use.

Use

import soundfile as sf
from maya2_mlx.tts import Maya2MLX

tts = Maya2MLX.from_pretrained()          # exact port: bf16 backbone, fp16 depth decoder
wav = tts.generate("வணக்கம், எப்படி இருக்கீங்க?", lang="ta", voice="Kayal", normalize=True)
sf.write("speech.wav", wav, 24000)

# stream 80 ms int16 PCM frames as they are generated
for chunk in tts.stream("Your order has shipped.", lang="en", voice="Siddhant", normalize=True):
    ...

# clone a voice across languages (get the speaker's permission first)
tts.register_voice("my_voice", ref_audio="reference.wav", ref_text="<exact transcript>", lang="en")
wav = tts.generate("नमस्ते, आप कैसे हैं?", lang="hi", voice="my_voice", normalize=True)
  • Same API as the official Python interface: generate() and stream() take the same keyword arguments as maya3.Maya3TTS: seed, temperature, top_k, top_p, repetition_penalty, max_frames, ref_audio / ref_text / ref_lang, return_info.
  • Voices: all 20 named voices from the release are available.
  • Cloning: the reference clip is encoded once with the PyTorch Mimi encoder and ECAPA on CPU. Generation itself runs on MLX.
  • Fast mode: Maya2MLX.from_pretrained(backbone_bits=8, depth_dtype="bf16"), an opt-in trade-off measured below.

Speed (M4 Max, 36 GB)

Time to make 1 s of audio Time to first audio Peak memory
Official PyTorch engine on MPS (with patches/maya2-mps.patch) 0.94 s 120 ms –
This port, exact (default) 0.41 s 60 ms 6.1 GB
This port, fast (8-bit backbone + bf16 depth) 0.30 s 50 ms 4.5 GB

Load plus warmup takes about 3.5 s.

Intelligibility

Character error rate when Whisper large-v3-turbo transcribes the output (one sample per clip; lower is better):

en hi ta te bn kn mr clone en→hi
Official PyTorch engine 0.00 0.15 0.00 0.13 0.20 0.13 0.27 0.07
This port, exact 0.00 0.03 0.00 0.07 0.17 0.09 0.14 0.07
This port, fast 0.00 0.06 0.04 0.07 0.22 0.20 0.11 0.07

With a single sample per language, read these as "the same quality". Most remaining errors are Whisper's spelling of Indic scripts.

Parity with the PyTorch model

Each component was compared with the original code on identical inputs, using real model states rather than random ones. The raw output is in parity/results.txt.

Component Result
Prompt embeddings 97–99% bit-identical; the rest differ by 1 bf16 ulp
Backbone (Qwen3-1.7B) hidden state error at or below PyTorch's own bf16 noise; same cb0 argmax in all cases; cb0 KL ≤ 2e-3
Depth decoder, 31 levels logit error vs fp32 1.4e-3 (PyTorch fp16 itself: 1.3e-3); greedy codes 95–97% equal to the engine's
Mimi streaming decoder 122 dB SNR vs the engine's decoder (fp32 rounding)
Fast-mode costs 8-bit backbone: cb0 KL 4.5e-3; bf16 depth: 8× the fp16 logit error; 4-bit backbone rejected (KL 0.32)

The parity scripts need the original PyTorch engine to run on a Mac. Its CUDA-graph decoders need the small patch in patches/maya2-mps.patch, which also lets the official engine run on Apple's MPS backend. Apply it to a checkout of MayaResearch/maya2-native at the pinned commit, install that checkout, then run:

PYTHONPATH=parity:. python parity/check_backbone.py   # also check_depth.py, check_mimi.py, check_variants.py

How it maps to the original

File Mirrors
maya2_mlx/backbone.py Qwen3 decoder with a preallocated KV cache (serve/fast_backbone.py)
maya2_mlx/depth.py 31-level depth decoder in one compiled graph per frame (serve/fast_depth.py)
maya2_mlx/mimi.py streaming Mimi decoder built from the HF module tree (serve/fast_codec.py)
maya2_mlx/prompt.py prompt packing via the release's own collate, plus compose_inputs
maya2_mlx/tts.py the engine's per-seat loop: controlled stop, sampling, loop guard, voices

Limits

  • One stream at a time. The CUDA engine's continuous batching across many concurrent streams isn't ported.
  • No HTTP / WebSocket server. The release's maya3.server drives the PyTorch engine.
  • Seeds aren't portable. A seed reproduces audio within this port, but not the CUDA engine's audio, because the random number generators differ. The sampling distributions are the same.
  • Where the time goes. About two thirds of each 80 ms frame goes to the depth decoder's small-matrix kernels across 31 dependent levels. The bf16 backbone step is at its memory-bandwidth floor.

License and credit

The code is Apache-2.0, like the original. It's derived from MayaResearch/maya2-native, and the model weights, voices and evaluation are Maya Research's. Please cite their release: maya-research/maya2-native.

This port is not affiliated with or endorsed by Maya Research.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support