VOICEVOX — 無料AI音声合成エンジンの内部構造
テキスト→音素→ピッチ→波形、3段階分離型推論がユーザー編集を可能にする仕組み
VOICEVOXはYouTube実況・解説動画で最も使われている無料AI音声合成ソフトだ。ずんだもん、四国めたんなどのキャラクターボイスを生成する。
ただ、内部的にどう動いているかを知る人は多くない。
3段階分離型アーキテクチャ
VOICEVOXはVITSのようなend-to-endモデルではない。3つの独立DNNモデルを順次実行するcascade方式だ。
第1段階 — yukarin_s: 音素ID配列を受け取り、各音素の持続時間を予測する。「こんにちは」のk, o, N, n, i, ch, i, w, aそれぞれが何秒か。
第2段階 — yukarin_sa: 母音/子音ID+アクセント位置を受け取り、モーラ別f0(音高)を予測する。日本語の高低アクセントをここで決定する。
第3段階 — decode: 音素のonehotベクトル(45種、フレーム単位)とf0を受け取り、24kHzオーディオ波形を生成する。vocoder役。
こう分離した理由がある。第1〜2段階の結果をJSON(AudioQuery)でユーザーに返し、ユーザーがピッチと長さを直接調整した後に第3段階を実行できる。end-to-endモデルでは不可能なきめ細かい制御が可能になる。
テキスト → 音素変換
OpenJTalk(形態素解析器)が日本語テキストを音素+アクセント情報に分解する。VOICEVOXは自前でforkしたpyopenjtalkを使用。
フロー:日本語テキスト → MeCab形態素解析 → NJD → HTSラベル生成 → 正規表現パース → AccentPhraseリスト
音素は45種。pau(ポーズ)、cl(促音っ)、N(撥音ん)、大文字母音(A, I, U, E, O)は無声化母音。
3層ソフトウェア構造
VOICEVOXエディタ(Electron/TypeScript)→ Engine(Python/FastAPI)→ Core(Rust/ONNX Runtime)
Engineがテキスト前処理、APIサーバー、パラメータ編集を担当し、実際のDNN推論はCoreがONNX Runtimeで実行する。
Engine → Core:ctypes FFIが具体的に何をするか
CoreはRustで書かれた共有ライブラリ(.dll/.so/.dylib)だ。Python EngineはこれをctypesCで直接呼び出す。ONNX RuntimeはCore DLL内に組み込まれており、Python側はONNXセッションを一切触らない。
DLLロード順序:まずload_runtime_lib()がONNX Runtime DLLを事前ロードし、load_core()がCore DLLをロード。GPUタイプに応じて異なるバイナリを選択 — CUDA GPU → DirectML GPU → CPUフォールバック。
関数シグニチャバインディング:Coreの C関数(yukarin_s_forward、decode_forward等)の引数型をctypesで宣言:argtypes=(c_int, POINTER(c_long), POINTER(c_float))。全関数がc_boolを返し、失敗時はlast_error_message()でエラーを取得。
numpy ↔ Cポインタ変換:ここが核心。numpy配列のデータポインタをCポインタとして直接渡す:phoneme_list.ctypes.data_as(POINTER(c_long)) — コピーなし、C関数が同じメモリを読む。出力バッファもnumpy配列を事前確保してC関数が直接書き込む。decodeの場合np.empty((length * 256,), dtype=np.float32) — フレーム数×256がオーディオサンプル数。
スレッド安全性:CoreAdapterが全推論呼び出しをthreading.Lock()で囲む。ONNX Runtimeセッションがスレッドセーフでないため。with self.mutex:内でのみCore関数を呼び出す。
前後無音パディング:yukarin_sとyukarin_saは入力配列の前後に無音(0)を追加 — np.r_[0, phoneme_list, 0]。推論後に結果の両端を除去。Core仕様で必要な規則。
Core内部(Rust)
RustのC ABI関数(#[unsafe(no_mangle)] pub extern "C" fn yukarin_s_forward(...))がCポインタを受け取り、unsafe { std::slice::from_raw_parts }またはndarray::ArrayView::from_shape_ptrでRust配列に変換。ONNX Runtimeセッションで推論し、結果をCポインタにコピーバック。
ONNX Runtime統合はortクレート(pykeio/ort)を使用。libloading::Library::new()でONNX Runtime SO/DLLを動的ロード。GPU選択はCUDAExecutionProviderまたはDirectMLExecutionProviderをセッションビルダーに登録する方式。モデルは.vvmファイル(ZIPアーカイブ)内に.onnx形式で格納。
GPU加速
ONNX RuntimeがGPU推論を担当する。CPUに比べGPU(A4000)で約330倍速い。CUDAまたはDirectMLバックエンドをサポート。VOICEVOXは自前でカスタムビルドしたONNX Runtimeを使用。
YouTubeクリエイターに重要な理由
無料だ。商用利用可能(クレジット必須)。YMM4(ゆっくりムービーメーカー4)とHTTP APIで直接連携できる。キャラクター別の感情スタイル(あまあま、ツンツン、ささやき等)を切り替えられる。従来のゆっくりボイスの収益化不確実性問題を解決する。
DockerでHTTPサーバー実行
VOICEVOX EngineはFastAPIベースのHTTPサーバーだ。ローカルにインストールせずDockerですぐ起動できる。
docker pull voicevox/voicevox_engine:cpu-ubuntu20.04-latest
docker run --rm -p 50021:50021 voicevox/voicevox_engine:cpu-ubuntu20.04-latest
GPU版もある。nvidia-ubuntu20.04-latestタグを使い--gpus allオプションを付ける。
サーバーが起動するとhttp://localhost:50021/docsでSwagger UIから全APIを確認できる。コアAPIは2つ:
POST /audio_query?text=こんにちは&speaker=3— テキストをAudioQuery(JSON)に変換POST /synthesis?speaker=3— AudioQueryを受け取りWAVオーディオを返却
curl一行で音声生成できる。GUIエディタなしでスクリプトやサーバーサイドからTTSを自動化する時にこの方式が便利。YMM4も内部的にこのHTTP APIを呼び出している。
3段階推論パイプライン
| 段階 | モデル | 入力 | 出力 |
|---|---|---|---|
| 1 | yukarin_s | 音素ID配列 + style_id | 音素別持続時間(秒) |
| 2 | yukarin_sa | 母音/子音ID + アクセント位置 + style_id | モーラ別f0(音高、Hz) |
| 3 | decode | 音素onehot(45種、フレーム単位)+ f0 | 24kHzオーディオ波形 |
フレームレート:93.75fps(24000Hz / hop_size 256)。1フレーム ≈ 10.67ms
Engine → Core:ctypes FFI詳細
Python EngineがRust CoreのC ABI関数をctypesで直接呼び出す。ONNX RuntimeはCore DLL内にカプセル化されており、Python側はセッションを一切触らない。
核心パターン
- ✓ゼロコピー — C関数がnumpy配列のメモリを
array.ctypes.data_as(POINTER(c_long))で直接読み書き - ✓threading.Lock() — 全Core呼び出しを直列化。ONNX Runtimeセッションがスレッドセーフでないため
- ✓遅延ロード — style_id別に初回使用時にモデルをロード。起動時間短縮
- ✓エラー伝搬 — C関数がfalse返却 →
last_error_message()でRustエラーをPython例外に変換
Core内部(Rust → ONNX Runtime)
RustのC ABI関数がCポインタを受け取り → unsafe { slice::from_raw_parts }でndarrayに変換 → ONNXセッション実行 → as_standard_layout()でC-contiguous保証 → 出力ポインタにコピーバック。GPU選択はCUDAExecutionProviderまたはDirectMLExecutionProviderをセッションビルダーに登録。モデルは.vvmファイル(ZIPアーカイブ)内に.onnx形式で格納。
なぜend-to-endではなく分離型か
VITSのようなend-to-endモデルはテキストを入れれば直接音声が出る。便利だが中間結果を触れない。「この単語のピッチを上げたい」が不可能だ。
VOICEVOXは第1〜2段階の結果をAudioQuery(JSON)でユーザーに返す。ユーザーがエディタUIでピッチ曲線を直接描き、音素長を調整した後、第3段階(vocoder)だけ再実行する。この「編集可能な中間表現」がVOICEVOXの核心設計哲学だ。
3層ソフトウェア構造
開発者バックグラウンド
ヒホ(Hiroshiba)— ドワンゴメディアビレッジ所属MLエンジニア。AI音声変換ライブラリ「yukarin」開発者。2021年8月初リリース。目標:無料キャラクター音声を作りたい人と使いたい人を繋ぐこと。
実践ステップ
日本語テキスト入力 → OpenJTalkが形態素解析+アクセント句(AccentPhrase)生成
yukarin_sモデルが音素別持続時間(長さ)を予測
yukarin_saモデルがモーラ別f0(音高/ピッチ)を予測
AudioQuery(JSON)をユーザーに返却 — ピッチ、速度、イントネーションを編集可能
decodeモデル(vocoder)が編集済みパラメータで24kHz WAV波形を生成
メリット
- ✓ 完全無料+商用利用可能(クレジット表記必須)
- ✓ 25種以上キャラクター×8種感情スタイル — 幅広い音声選択肢
- ✓ AudioQueryでピッチ/速度/イントネーションを手動微調整可能
- ✓ GPU加速時CPUの約330倍高速(ONNX Runtime)
デメリット
- ✗ 日本語専用 — 英語/韓国語音声合成不可
- ✗ GPU(CUDA)なしのCPUのみでは合成速度が遅い
- ✗ DirectML版でマルチGPU環境(Optimus等)の問題報告あり
- ✗ キャラクターごとに利用規約が異なる — 事前確認必須