Magpie-TTS Multilingual
Apple Silicon 上の NVIDIA Magpie-TTS Multilingual 357M の Swift 実装 — NeMo の 22.05 kHz Nano-Codec をベースにした自己回帰型マルチコードブック TTS モデル。9 言語(英語、スペイン語、ドイツ語、フランス語、イタリア語、ベトナム語、ヒンディー語、中国語、日本語)、5 つの組み込みスピーカー。INT4(約 247 MB)または INT8(約 411 MB)で量子化。ストリーミング対応、初回パケット遅延約 120 ms。
1 つの小さなバンドルで 9 言語を一貫した声で話したい場面に最適です。5 つの組み込みスピーカーは全言語でアイデンティティが安定します — 多言語アシスタント、教育アプリ、言語切り替えのあるオーディオブック朗読など。ゼロショット声質クローニングが必要なら CosyVoice3、Qwen3-TTS Base、VoxCPM2 を使ってください。
アーキテクチャ
Magpie は 4 バンドルの MLX パイプライン:テキストエンコーダ → クロスアテンション付きデコーダ → LocalTransformer コードブックヘッド → 因果的 HiFi-GAN オーディオコーデック。バンドルは prefill と step のエントリポイントでデコーダ重みを共有し、上流の FluidInference の CoreML レイアウトと互換性を保ちます。
| 段階 | モジュール | 詳細 |
|---|---|---|
| 1. トークン化 | MagpieTokenizer | 言語ごとの G2P(IPA 辞書 / byT5 バイト / ピンイン / カタカナ)、共有 2360 トークン語彙 + トークナイザごとのオフセット、常に EOS 付加 |
| 2. テキストエンコーダ | MagpieTextEncoder | 因果 Transformer 6 層、d=768、k=3 畳み込み FFN |
| 3. デコーダ prefill | MagpieDecoder | クロスアテンション付き因果 12 層。110 フレームの組み込みスピーカーコンテキスト + BOS を KV キャッシュにシード。 |
| 4. LocalTransformer | MagpieLocalTransformer | 1 層のコードブック AR ヘッド、d=256。デコーダの hidden を条件として 1 フレーム 8 コードブックを逐次サンプリング。 |
| 5. デコーダ step | MagpieDecoder | EOS または 500 フレーム上限(約 23 秒)まで 1 フレームずつ AR 推進。 |
| 6. NanoCodec | MagpieNanoCodec | FSQ 逆変換 → 因果 HiFi-GAN → 22.05 kHz モノラル波形。 |
言語と G2P
9 言語すべてが SDK の testMultilingualRoundTrip で Qwen3-ASR ラウンドトリップを通過します。それぞれ専用のパイプラインを持ちます:
| 言語 | コード | G2P パイプライン |
|---|---|---|
| 英語 | en | CMU IPA 辞書(12.5 万語、同梱) |
| スペイン語 | es | スペイン語 IPA 辞書(同梱) |
| ドイツ語 | de | ドイツ語 IPA 辞書(同梱) |
| フランス語 | fr | byT5 UTF-8 バイトエンコーダ |
| イタリア語 | it | byT5 UTF-8 バイトエンコーダ |
| ベトナム語 | vi | byT5 UTF-8 バイトエンコーダ |
| ヒンディー語 | hi | デーヴァナーガリー コードポイント検索 + last-wins サブ語彙 |
| 中国語普通話 | zh | NLTokenizer(.simplifiedChinese) 単語分割 + Apple .mandarinToLatin + 同梱ピンイン→IPA 辞書 + #N 声調マーカー |
| 日本語 | ja | CFStringTokenizer 漢字読み + NFC で保持される濁点 + 平板アクセントマーカー + 助詞/挨拶オーバーライド |
共有 2360 トークン語彙は各言語のサブトークナイザをオフセット付きで連結(MagpieSubVocab に記録)。テキスト埋め込みは BOS / EOS 用に 2 行余分にあり、eos_id = 2361 が常に入力末尾に追加されます。
組み込みスピーカー
チェックポイントには 5 つのスピーカーコンテキスト(各 110 フレーム × 768 次元)が埋め込まれ、AR デコードの先頭に付与されます。スピーカーの同一性は 9 言語すべてで一貫しています。
| 番号 | CLI 名 | アイデンティティ |
|---|---|---|
| 0 | sofia | Sofia(デフォルト) |
| 1 | aria | Aria |
| 2 | jason | Jason |
| 3 | leo | Leo |
| 4 | john | John Van Stan |
モデルバリアント
| バリアント | ディスク | RAM(ロード+デコード) | HuggingFace |
|---|---|---|---|
| INT4(デフォルト) | 約 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 |
両バンドルとも MLX の flat affine 量子化(mlx_affine_flat、group size 64)を使い、ロード時に FP32 に逆量子化されるため実行時のアクティベーションはフル精度です。本モデルの INT4 と INT8 は聴感上区別できないので、ストレージに余裕がない限り INT4 で十分です。
CLI 使用法
# 英語、貪欲デコード
speech speak "Hello, world." --engine magpie --magpie-speaker aria \
--magpie-temperature 0 -o out.wav
# スペイン語(9 言語のいずれかを --language で選択)
speech speak "Hola, mundo." --engine magpie --language es \
--magpie-speaker aria -o out.wav
# 日本語 — 確率的デコードが必要(貪欲は最初のフレーズで止まる)
speech speak "こんにちは世界、これは音声合成システムです。" \
--engine magpie --language ja --magpie-temperature 0.6 \
--magpie-top-k 80 --seed 42 -o out.wav
# ストリーミング合成 + 再生
speech speak "Streaming test" --engine magpie --stream --play
# 5 つの組み込みスピーカーを一覧表示
speech speak --engine magpie --list-speakers
# 事前音素化された IPA は言語ごとの G2P をバイパス
speech speak "həˈloʊ" --engine magpie --magpie-prephonemized -o out.wav
オプション
| オプション | デフォルト | 説明 |
|---|---|---|
--magpie-variant | int4 | 量子化バリアント:int4 または int8 |
--magpie-speaker | sofia | 組み込みスピーカー:sofia、aria、jason、leo、john |
--magpie-temperature | 0.6 | サンプリング温度(0 = 貪欲) |
--magpie-top-k | 80 | Top-k サンプリングフィルタ |
--magpie-max-frames | 500 | コーデックフレームの上限(約 23 秒) |
--magpie-min-frames | 4 | EOS を許可する最小フレーム数 |
--magpie-prephonemized | OFF | 入力を IPA / 音素ストリームとして扱い、言語ごとの G2P をスキップ |
--language | english | 言語ごとのトークナイザを選択 |
--stream | OFF | 単一の WAV ではなく AsyncStream<AudioChunk> を出力 |
--seed | — | 再現可能な Gumbel サンプリング |
1 単語より長い日本語入力には確率的デコードが必要です(--magpie-temperature 0.6 --magpie-top-k 80 --seed 42 は NeMo の参照テストに対応)。貪欲は最初のフレーズで止まります — 平板アクセントのヒューリスティックが単語ごとの実値から外れるため。
声質クローニング — 非対応
Magpie モデルにはゼロショットのスピーカー条件付けがありません。バンドルには 5 つの組み込みアイデンティティのみが含まれます。CLI は共通の --voice-sample、--speaker、--instruct フラグを実行可能なエラーで拒否し、--magpie-speaker またはクローニング対応エンジン(Qwen3-TTS Base、CosyVoice3、VoxCPM2)への切り替えを案内します。
パフォーマンス(M4 Pro)
| 設定 | 音声 | 所要時間 | RTF |
|---|---|---|---|
| バッチ INT4 貪欲、短いプロンプト | 2.8 s | 0.88 s | 0.32 |
| バッチ INT4 貪欲、文 | 5.8 s | 1.35 s | 0.23 |
| バッチ INT4 サンプリング、23 秒出力 | 23 s | 5.6 s | 0.24 |
| ストリーミング INT4 サンプリング | 23 s | 21.6 s | 0.93 |
ストリーミング時のロード後の初回パケット遅延は約 120 ms。ストリーミング RTF が高いのは、各チャンク出力でコーデックがフルコードバッファに再実行されるため(将来的に状態キャッシュを追加可能)。
Swift API
import MagpieTTS
let model = try await MagpieTTS.fromPretrained(variant: .int4)
// バッチ合成(en/es/de/fr/it/vi/hi/zh — 貪欲で OK)
let audio = try model.synthesize(
text: "Hello, world.",
speaker: .aria,
language: .english,
params: MagpieTTSParams(temperature: 0, topK: 1, maxSteps: 500))
// 日本語 — 確率的サンプリングを使用
let audioJA = try model.synthesize(
text: "こんにちは世界、これは音声合成システムです。",
speaker: .aria,
language: .japanese,
params: MagpieTTSParams(temperature: 0.6, topK: 80,
maxSteps: 300, seed: 42))
// ストリーミング(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 は 22.05 kHz モノラル Float32
}
CoreML バックエンド(--engine magpie-coreml)
MLX バンドルに加えて、Magpie には CoreML バンドル(aufklarer/Magpie-TTS-Multilingual-357M-CoreML-8bit、~342 MB INT8)があります。4 つの .mlmodelc パッケージ — text_encoder、decoder_prefill、decoder_step、nanocodec_decoder — は ANE / GPU で実行されます。Swift 側の FSQ 逆変換が、サンプリングされたコードをコーデックが消費する 32 次元の潜在変数に変換します。
# 8 言語(日本語なし)、5 つの組み込み話者
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 は自動的に MLX バックエンドにルーティング(stderr ノート)
speech speak "こんにちは" --engine magpie-coreml --language ja -o ja.wav
--engine magpie との注意点:
- 現状はハイブリッドパイプライン。1 層 LocalTransformer(NeMo が訓練する真のコードブックサンプリングヘッド)と 8 つの音声埋め込みテーブルは CoreML バンドルに含まれていません。初回合成時に CoreML エンジンは MLX INT4 バンドルを遅延ロードして両方を駆動します。ASR ラウンドトリップは MLX バックエンドとビット単位で同一です。違いは、このエンジンも MLX バンドルをダウンロードすることです。ANE のみの iOS デプロイ用の純粋な CoreML パスには、バンドルが
local_transformer/*.npy+audio_embedding_*.npyを出荷し、Swift Accelerate LT が必要です(フォローアップ追跡中)。 - ストリーミングなし。
nanocodec_decoder.mlmodelcは固定の 64 フレームウィンドウでトレースされています。より長いシーケンスは内部でチャンク化されますが、チャンク境界で発行する場合、ファーストパケットレイテンシは ~3 秒になります。--streamは実行可能なエラーで拒否されます。 - 日本語トークナイザーなし。CoreML バンドルにはまだ JA トークナイザー JSON が含まれていません。このエンジンで
--language jaを使うと、自動的に MLX バックエンドにフォールバックします。
話者の順序は CoreML バンドルの speaker_info.json と一致します(0=John, 1=Sofia, 2=Aria, 3=Jason, 4=Leo — MLX とは異なる)。話者の列挙型は内部でマップされるため、CLI 名は両エンジンで機能します。
実装メモ
NeMo 風の多言語 TTS を移植する際に知っておくべきバグ 3 点:
- FSQ 整数除算 — MLX-swift の
/は実数除算(mlx_divide)。NeMo の FSQ 逆変換は Python//。MLX.floorDivide(...)を使わないと FSQ スロットが小数オフセットになり、コーデックの音声がにじみます。 - サブ語彙オフセット — NeMo の
AggregatedTTSTokenizerは各言語の語彙をオフセット付きで連結します。素朴な global first-occurrence マップは常に英語領域に着地し、他言語ではナンセンスな音声になります。 - ヒンディー語 last-wins 重複排除 —
HindiCharsTokenizerは自身の語彙内にデーヴァナーガリーの重複を持ちます(CHARSET と PUNCT_LIST の重複)。Python の{l: i for i, l in enumerate(tokens)}dict 内包は最後の代入を保持します。これに合わせて first-occurrence を使わないこと。
3 つの修正はすべて Swift モジュール 内にインラインで記載しています。
出典
- 上流重み:nvidia/magpie_tts_multilingual_357m(NVIDIA Open Model License)
- コーデック:nvidia/nemo-nano-codec-22khz-1.89kbps-21.5fps
- 論文:NanoCodec: Towards High-Quality Ultra Fast Speech LLM Inference(2025)
- 参考 CoreML 移植:FluidInference/mobius
- Swift モジュール:MagpieTTS(MLX)+ MagpieTTSCoreML(CoreML)
- CoreML バンドル:aufklarer/Magpie-TTS-Multilingual-357M-CoreML-8bit
ライセンス
- モデル重み:NVIDIA Open Model License(商用利用可、詳細は HuggingFace ページ)
- Swift 移植 + 同梱 IPA / ピンイン辞書:上流 NeMo に準拠(辞書は Apache 2.0、モデルは NVIDIA OML)