NeuTTS Air - Moroccan Darija
neuphonic/neutts-air fine-tuned on Moroccan Darija speech tokens from
Tilas/MoulSot-Tokens-v1.
Speech is represented as NeuCodec tokens (single codebook, 65,536 codes, 50 tokens/s,
0.8 kbps). Generated tokens must be decoded with neuphonic/neucodec.
Usage
Darija text is phonemised with espeak-ng's ar voice through a custom phonemizer that
maps Arabic punctuation to Latin equivalents (espeak silently drops ؟ and ،) and
strips harakat. The same phonemizer must be used at inference, or the model sees a
different phoneme distribution than it was trained on.
import src.darija_g2p # registers DarijaPhonemizer as CUSTOM_PHONEMIZERS["ar"]
from neutts import NeuTTS
tts = NeuTTS(backbone_repo="Tilas/neutts-air-darija-v1", language="ar")
wav = tts.infer("شنو كتدير هنا؟", ref_codes=ref_codes, ref_text=ref_text)
language="ar" must be passed explicitly: this repo is not in BACKBONE_LANGUAGE_MAP,
and _load_phonemizer raises rather than guessing.
Training
| Base model | neuphonic/neutts-air (747.9M trainable params) |
| Training samples | 71,919 |
| Eval samples | 1,000 |
| Speech | ~94.8 h after filtering (101.7 h before) |
| Max sequence length | 896 tokens |
| Effective batch | 32 |
| Learning rate | 4e-05 (cosine, 0.03 warmup) |
| Epochs | 2.0 |
| Precision | bf16 |
| GPU | NVIDIA GeForce RTX 4090 |
| Final eval loss | 7.1369 |
Supervision covers only the speech span; the phoneme prompt and padding are masked with
-100. Sequences are dynamically padded with length-grouped batching.
Data
Audio and transcripts come from
atlasia/MoulSot-Full,
a Moroccan Darija speech corpus collected by AtlasIA
from 51 YouTube channels and released under Apache-2.0. This model uses the
100-gt-2.5 config — 101.9 hours of transcribed speech.
That audio was encoded to NeuCodec tokens and published as
Tilas/MoulSot-Tokens-v1,
which is what this fine-tune consumes.
Data filtering
72,919 of 79,641 source rows were kept.
Rejections: {"too_short": 2676, "contains_digits": 2103, "no_arabic_letters": 1797, "too_long_with_phonemes": 129, "too_long": 17}
Clips under 1.0 s were dropped to match min_new_tokens=50 at inference. Clips
containing digits were dropped because espeak verbalises numerals into Modern Standard
Arabic while the audio contains the Darija form.
Limitations
- Phonemisation is a baseline, not a Darija G2P. espeak-ng's
arvoice applies MSA morphophonology and reads short vowels from diacritics that undiacritised Darija does not carry, producing MSA-flavoured output and vowel-less consonant clusters (نبداو→mbdˈaːw,فالسبعة→fassbʕt). It is deterministic and collision-free on the pairs tested, so the model can learn a consistent mapping, but the IPA is frequently wrong. A dedicated Darija phonemiser is the obvious next improvement. - French loanwords get English-flavoured IPA under
language_switch="remove-flags". - Transcripts are Gemini-2.5-Pro generated, not human-verified, and Darija has no standardised orthography.
- Source audio is YouTube, so acoustic conditions vary and background noise is learnable. Voice quality at inference depends heavily on the reference clip.
- The eval split is a random sample of training channels, so eval loss is optimistic. It is a training-health signal, not a quality benchmark.
Provenance
Tokens were produced by neuphonic/neucodec with the encode path frozen, at
batch_size=1 and with no pre-padding. Both are required for token stability, since
Wav2Vec2-BERT applies per-utterance feature normalisation and encode_code passes no
attention mask.