Magpie-TTS Multilingual
NVIDIA Magpie-TTS Multilingual 357M in Swift auf Apple Silicon — ein autoregressives Multi-Codebook-TTS-Modell über NeMos 22.05 kHz Nano-Codec. Neun Sprachen (Englisch, Spanisch, Deutsch, Französisch, Italienisch, Vietnamesisch, Hindi, Mandarin, Japanisch) mit fünf vordefinierten Sprecher-Identitäten. Quantisiert als INT4 (~247 MB) oder INT8 (~411 MB). Streaming-fähig, ~120 ms First-Packet-Latenz.
Magpie ist die richtige Wahl, wenn du ein einziges kleines Bundle brauchst, das neun Sprachen mit einer konsistenten Stimme spricht. Die fünf vordefinierten Sprecher bleiben über alle Sprachen hinweg identitätsstabil — nützlich für mehrsprachige Assistenten, Lern-Apps oder Hörbuch-Narration mit Code-Switching. Für Zero-Shot-Voice-Cloning verwende CosyVoice3, Qwen3-TTS Base oder VoxCPM2.
Architektur
Magpie ist eine 4-Bundle-MLX-Pipeline: Text-Encoder → Cross-Attention-Decoder → LocalTransformer-Codebook-Head → Causal HiFi-GAN-Audio-Codec. Die Bundles teilen Decoder-Gewichte zwischen den Prefill- und Step-Einstiegspunkten, um die CoreML-Layout-Kompatibilität mit FluidInference upstream zu bewahren.
| Stufe | Modul | Details |
|---|---|---|
| 1. Tokenisierung | MagpieTokenizer | Sprachspezifisches G2P (IPA-Wörterbuch / byT5-Bytes / Pinyin / Katakana), gemeinsames 2360-Token-Vokabular mit Pro-Tokenizer-Offsets, immer angehängtes EOS |
| 2. Text-Encoder | MagpieTextEncoder | 6 kausale Transformer-Layer, d=768, k=3 Conv-FFN |
| 3. Decoder-Prefill | MagpieDecoder | 12 kausale Layer mit Cross-Attention. Initialisiert den KV-Cache mit dem 110-Frame-Kontext des vordefinierten Sprechers + BOS. |
| 4. LocalTransformer | MagpieLocalTransformer | 1-Layer-Codebook-AR-Head, d=256. Sampled die 8 Codebooks pro Frame sequenziell anhand des Decoder-Hiddens. |
| 5. Decoder-Step | MagpieDecoder | Ein AR-Schritt pro Frame bis EOS oder das 500-Frame-Limit (~23 s). |
| 6. NanoCodec | MagpieNanoCodec | FSQ-Inverse → kausales HiFi-GAN → 22.05 kHz Mono-Waveform. |
Sprachen und G2P
Alle neun Sprachen durchlaufen Qwen3-ASR im testMultilingualRoundTrip des SDKs. Jede hat eine angepasste Pipeline:
| Sprache | Code | G2P-Pipeline |
|---|---|---|
| Englisch | en | CMU-IPA-Wörterbuch (125k Einträge, mitgeliefert) |
| Spanisch | es | Spanisches IPA-Wörterbuch (mitgeliefert) |
| Deutsch | de | Deutsches IPA-Wörterbuch (mitgeliefert) |
| Französisch | fr | byT5 UTF-8-Byte-Encoder |
| Italienisch | it | byT5 UTF-8-Byte-Encoder |
| Vietnamesisch | vi | byT5 UTF-8-Byte-Encoder |
| Hindi | hi | Devanagari-Codepoint-Lookup + Last-Wins-Sub-Vokabular |
| Mandarin | zh | NLTokenizer(.simplifiedChinese)-Wortsegmentierung + Apple .mandarinToLatin + mitgeliefertes Pinyin → IPA-Wörterbuch + #N-Ton-Markierungen |
| Japanisch | ja | CFStringTokenizer-Kanji-Lesung + NFC-erhaltene Dakuten + Heiban-Pitch-Markierungen + Partikel/Begrüßungs-Overrides |
Das gemeinsame 2360-Token-Vokabular verkettet die Sub-Tokenizer jeder Sprache mit einem Pro-Sprach-Offset (in MagpieSubVocab festgehalten). Das Text-Embedding fügt nach dem Vokabular zwei zusätzliche Zeilen für BOS/EOS hinzu; eos_id = 2361 wird an jede Eingabesequenz angehängt.
Vordefinierte Sprecher
Der Checkpoint bettet fünf Sprecher-Kontexte ein (jeweils 110 Frames × 768 Dim), die als Präfix jeder AR-Dekodierung dienen. Die Sprecher-Identität ist über alle neun Sprachen hinweg konsistent.
| Index | CLI-Name | Identität |
|---|---|---|
| 0 | sofia | Sofia (Standard) |
| 1 | aria | Aria |
| 2 | jason | Jason |
| 3 | leo | Leo |
| 4 | john | John Van Stan |
Modell-Varianten
| Variante | Disk | RAM (Laden + Decode) | HuggingFace |
|---|---|---|---|
| INT4 (Standard) | ~247 MB | ~1.3 GB | aufklarer/Magpie-TTS-Multilingual-357M-MLX-4bit |
| INT8 | ~411 MB | ~1.6 GB | aufklarer/Magpie-TTS-Multilingual-357M-MLX-8bit |
Beide Bundles verwenden MLX Flat-Affine-Quantisierung (mlx_affine_flat, Group Size 64) und werden beim Laden auf FP32 dequantisiert — die Runtime-Aktivierungen sind in voller Präzision. INT4 und INT8 sind für dieses Modell akustisch nicht unterscheidbar; wähle INT4, außer du hast Speicher übrig.
CLI-Verwendung
# Englisch, greedy Dekodierung
speech speak "Hello, world." --engine magpie --magpie-speaker aria \
--magpie-temperature 0 -o out.wav
# Spanisch (eine der 9 Sprachen — wähle mit --language)
speech speak "Hola, mundo." --engine magpie --language es \
--magpie-speaker aria -o out.wav
# Japanisch — braucht stochastische Dekodierung (greedy bleibt bei erster Phrase hängen)
speech speak "こんにちは世界、これは音声合成システムです。" \
--engine magpie --language ja --magpie-temperature 0.6 \
--magpie-top-k 80 --seed 42 -o out.wav
# Streaming-Synthese mit Wiedergabe
speech speak "Streaming test" --engine magpie --stream --play
# Die 5 vordefinierten Sprecher auflisten
speech speak --engine magpie --list-speakers
# Vor-phonemisiertes IPA umgeht das sprachspezifische G2P
speech speak "həˈloʊ" --engine magpie --magpie-prephonemized -o out.wav
Optionen
| Option | Standard | Beschreibung |
|---|---|---|
--magpie-variant | int4 | Quantisierungs-Variante: int4 oder int8 |
--magpie-speaker | sofia | Vordefinierter Sprecher: sofia, aria, jason, leo, john |
--magpie-temperature | 0.6 | Sampling-Temperatur (0 = greedy) |
--magpie-top-k | 80 | Top-k-Filter fürs Sampling |
--magpie-max-frames | 500 | Hartes Limit für Codec-Frames (~23 s) |
--magpie-min-frames | 4 | Minimum-Frames bevor EOS erlaubt ist |
--magpie-prephonemized | aus | Behandelt Eingabe als IPA-/Phonem-Stream; überspringt sprachspezifisches G2P |
--language | english | Wählt den sprachspezifischen Tokenizer |
--stream | aus | Gibt AsyncStream<AudioChunk> statt einer einzelnen WAV aus |
--seed | — | Reproduzierbares Gumbel-Sampling |
Japanische Eingaben länger als ein Wort brauchen stochastische Dekodierung (--magpie-temperature 0.6 --magpie-top-k 80 --seed 42 spiegelt NeMos Referenz-Test). Greedy bleibt bei der ersten Phrase hängen, weil die Heiban-Pitch-Heuristik von der Wahrheit pro Wort abweicht.
Voice Cloning — nicht unterstützt
Magpie hat im Modell kein Zero-Shot-Speaker-Conditioning; nur die 5 vordefinierten Identitäten sind im Bundle. Die CLI lehnt die gemeinsamen Flags --voice-sample, --speaker und --instruct mit einem umsetzbaren Fehler ab, der auf --magpie-speaker oder die Engines mit Cloning-Unterstützung verweist (Qwen3-TTS Base, CosyVoice3, VoxCPM2).
Leistung (M4 Pro)
| Setting | Audio | Wall-Time | RTF |
|---|---|---|---|
| Batch, INT4, greedy, kurzer Prompt | 2.8 s | 0.88 s | 0.32 |
| Batch, INT4, greedy, Satz | 5.8 s | 1.35 s | 0.23 |
| Batch, INT4, sampled, 23 s Output | 23 s | 5.6 s | 0.24 |
| Streaming, INT4, sampled | 23 s | 21.6 s | 0.93 |
First-Packet-Latenz im Streaming-Modus ist ≈120 ms nach Modell-Load. Streaming-RTF ist höher, weil der Codec bei jeder Chunk-Ausgabe auf dem vollen Code-Buffer wiederholt läuft (eine künftige Revision kann den Codec-State cachen).
Swift-API
import MagpieTTS
let model = try await MagpieTTS.fromPretrained(variant: .int4)
// Batch-Synthese (en/es/de/fr/it/vi/hi/zh — greedy funktioniert)
let audio = try model.synthesize(
text: "Hello, world.",
speaker: .aria,
language: .english,
params: MagpieTTSParams(temperature: 0, topK: 1, maxSteps: 500))
// Japanisch — stochastisches Sampling verwenden
let audioJA = try model.synthesize(
text: "こんにちは世界、これは音声合成システムです。",
speaker: .aria,
language: .japanese,
params: MagpieTTSParams(temperature: 0.6, topK: 80,
maxSteps: 300, seed: 42))
// Streaming (AsyncStream<AudioChunk>)
let stream = model.synthesizeStream(
text: "Streaming text",
speaker: .aria,
language: .english,
firstChunkFrames: 8,
framesPerChunk: 25)
for try await chunk in stream {
// chunk.samples ist 22.05 kHz Mono Float32
}
CoreML-Backend (--engine magpie-coreml)
Neben dem MLX-Bundle gibt es ein CoreML-Bundle (aufklarer/Magpie-TTS-Multilingual-357M-CoreML-8bit, ~342 MB INT8). Vier .mlmodelc-Pakete — text_encoder, decoder_prefill, decoder_step, nanocodec_decoder — laufen auf ANE / GPU; eine FSQ-Inverse in Swift wandelt die gesampelten Codes in die 32-dim Latents um, die der Codec konsumiert.
# 8 Sprachen (kein Japanisch), 5 vordefinierte Sprecher
speech speak "Hello world." --engine magpie-coreml --magpie-speaker aria -o hi.wav
speech speak "Hola mundo." --engine magpie-coreml --language es --magpie-speaker leo -o es.wav
# --language ja leitet automatisch zum MLX-Backend (stderr-Hinweis)
speech speak "こんにちは" --engine magpie-coreml --language ja -o ja.wav
Vorbehalte gegenüber --engine magpie:
- Hybrid-Pipeline heute. Der 1-Layer LocalTransformer (der eigentliche Codebook-Sampling-Head, den NeMo trainiert) und die 8 Audio-Embedding-Tabellen sind nicht im CoreML-Bundle enthalten. Bei der ersten Synthese lädt das CoreML-Engine das MLX-INT4-Bundle nach, um beides zu fahren. ASR-Round-trip ist bit-genau identisch zum MLX-Backend; der Unterschied ist, dass dieses Engine zusätzlich das MLX-Bundle herunterlädt. Ein reiner CoreML-Pfad für reine ANE-Bereitstellung auf iOS braucht
local_transformer/*.npy+audio_embedding_*.npyim Bundle und einen Swift-Accelerate-LT (Folge-Arbeit). - Kein Streaming.
nanocodec_decoder.mlmodelcist auf ein festes 64-Frame-Fenster getraced. Längere Sequenzen werden intern gechunked, aber First-Packet-Latenz wäre ~3 s, wenn wir an Chunk-Grenzen emittieren würden.--streamwird mit umsetzbarem Fehler abgelehnt. - Kein Japanisch-Tokenizer. Das CoreML-Bundle enthält noch keine JA-Tokenizer-JSONs.
--language jamit diesem Engine fällt automatisch auf das MLX-Backend zurück.
Die Sprecher-Reihenfolge entspricht der speaker_info.json des CoreML-Bundles (0=John, 1=Sofia, 2=Aria, 3=Jason, 4=Leo — anders als MLX); das Sprecher-Enum mappt intern, sodass die CLI-Namen für beide Engines funktionieren.
Implementierungshinweise
Drei Bugs, die man beim Portieren von NeMo-Style-Multilingual-TTS kennen sollte:
- FSQ Floor-Division — MLX-swifts
/ist echte Division (mlx_divide); NeMos FSQ-Inverse nutzt Python//.MLX.floorDivide(...)verwenden, sonst dekodiert jedes FSQ-Slot zu fraktionalen Offsets und der Codec verschmiert das Audio. - Sub-Vokabular-Offsets — NeMos
AggregatedTTSTokenizerverkettet die Sprach-Vokabulare mit Offsets. Eine naive globale First-Occurrence-Map landet immer in der Englisch-Region und produziert für andere Sprachen unsinniges Audio. - Hindi Last-Wins-Dedup —
HindiCharsTokenizeremittiert doppelte Devanagari-Einträge in seinem eigenen Vokabular (CHARSET überlappt mit PUNCT_LIST). Pythons{l: i for i, l in enumerate(tokens)}-Dict-Comprehension behält die letzte Zuweisung; das spiegeln, nicht First-Occurrence.
Alle drei Fixes sind inline im Swift-Modul dokumentiert.
Quellen
- Upstream-Gewichte: nvidia/magpie_tts_multilingual_357m (NVIDIA Open Model License)
- Codec: nvidia/nemo-nano-codec-22khz-1.89kbps-21.5fps
- Paper: NanoCodec: Towards High-Quality Ultra Fast Speech LLM Inference (2025)
- Referenz-CoreML-Port: FluidInference/mobius
- Swift-Module: MagpieTTS (MLX) + MagpieTTSCoreML (CoreML)
- CoreML-Bundle: aufklarer/Magpie-TTS-Multilingual-357M-CoreML-8bit
Lizenz
- Modell-Gewichte: NVIDIA Open Model License (kommerzielle Nutzung erlaubt; siehe HuggingFace-Seite)
- Swift-Port + mitgelieferte IPA-/Pinyin-Wörterbücher: wie Upstream-NeMo (Wörterbücher Apache 2.0, Modell NVIDIA OML)