🗣️

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_forwarddecode_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つ:

  1. POST /audio_query?text=こんにちは&speaker=3 — テキストをAudioQuery(JSON)に変換
  2. 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層ソフトウェア構造

Editor Electron/TypeScript — GUIエディタ。ピッチ曲線編集、キャラクター選択、再生
↓ HTTP (localhost:50021)
Engine Python/FastAPI — テキスト前処理(OpenJTalk)、APIサーバー、パラメータ編集、モーフィング
↓ ctypes FFI (C ABI)
Core Rust — ONNX RuntimeでDNN推論実行。yukarin_s / yukarin_sa / decodeモデル駆動
↓ ONNX Runtime
GPU/CPU CUDA (Nvidia) / DirectML (Nvidia+AMD, Windows) / CPU fallback

開発者バックグラウンド

ヒホ(Hiroshiba)— ドワンゴメディアビレッジ所属MLエンジニア。AI音声変換ライブラリ「yukarin」開発者。2021年8月初リリース。目標:無料キャラクター音声を作りたい人と使いたい人を繋ぐこと。

実践ステップ

1

日本語テキスト入力 → OpenJTalkが形態素解析+アクセント句(AccentPhrase)生成

2

yukarin_sモデルが音素別持続時間(長さ)を予測

3

yukarin_saモデルがモーラ別f0(音高/ピッチ)を予測

4

AudioQuery(JSON)をユーザーに返却 — ピッチ、速度、イントネーションを編集可能

5

decodeモデル(vocoder)が編集済みパラメータで24kHz WAV波形を生成

メリット

  • 完全無料+商用利用可能(クレジット表記必須)
  • 25種以上キャラクター×8種感情スタイル — 幅広い音声選択肢
  • AudioQueryでピッチ/速度/イントネーションを手動微調整可能
  • GPU加速時CPUの約330倍高速(ONNX Runtime)

デメリット

  • 日本語専用 — 英語/韓国語音声合成不可
  • GPU(CUDA)なしのCPUのみでは合成速度が遅い
  • DirectML版でマルチGPU環境(Optimus等)の問題報告あり
  • キャラクターごとに利用規約が異なる — 事前確認必須

ユースケース

ゆっくり実況代替 — 収益化問題のないキャラクターボイス YMM4 + VOICEVOX組み合わせで完全無料YouTube動画制作 キャラクター感情スタイル(あまあま/ツンツン/ささやき等)で多様な演出 REST API(localhost:50021)で自動化スクリプト/ボット連携