はじめに
みなさまご存知の Text-to-Speech(TTS)ソフト VOICEVOX を使って、Ubuntu 24.04 on WSL2 で喋らせてみました。ホストの Windows 11 が Ubuntu 24.04 に PulseAudioサーバー を提供しているので、音声を合成したら、そちらへ流します。
コードの設計
VOICEVOXの使い方について、過去のブログ記事を漁ってみると、Linux 上で Docker で API サーバーを立てるとか、Windows にインストールして API でつなぐ方法を見つけました。ちょっと大変そうです。しかし、調べているうちに VOICEVOX Core には Python Binding もあることを発見。今回はこれを利用させてもらことにしました。
VOICEVOX Core レポジトリには、Pythonを使ったサンプルスクリプト talk.py があります。最初は、この talk.py をシェルスクリプトで包んで、一旦 wav データをファイルに書いてから、PulseAudio へ paplay で出そうと思ったのですが、サンプルなので Wav ファイルを出力して終わり。スピード調整などの機能も無さそう。
PulseAudio にも Python Binding あるようです。
ならばと VOICEVOX と PulseAudio を Python でガッチャンコくっつけて、生成した音声を直接流すスクリプトを書いてみることにしました。![]()
![]()
そういう訳で、Claude Code にお願いして誕生 したのが、こちらになります。
→ voicevox_paplay スクリプト
使い方
スクリプトの説明のため、先に使い方から説明します。後述のインストールと環境確認が済んだら実行できます。
1. 話者スタイルの確認
まずは、利用可能な 話者・スタイル ID の確認です。
$ voicevox_paplay --list-speakers
0.vvm
├── 四国めたん
│ ├── あまあま (style-id: 0) ◀
│ ├── ノーマル (style-id: 2)
│ ├── セクシー (style-id: 4)
│ └── ツンツン (style-id: 6)
├── ずんだもん
│ ├── あまあま (style-id: 1)
│ ├── ノーマル (style-id: 3)
│ ├── セクシー (style-id: 5)
│ └── ツンツン (style-id: 7)
├── 春日部つむぎ
│ └── ノーマル (style-id: 8)
└── 雨晴はう
└── ノーマル (style-id: 10)
右側の「◀」は --style-id オプションで指定する「現在選ばれている話者・スタイル」を示しています。今回のように明示的な指定がなったときは、一番小さい番号の style-id になります。
2. 喋らせてみる
スピーカーから音声を出します。
# テキストだけ指定(デフォルト話者)
$ voicevox_paplay "こんにちは"
# 標準入力から流す
$ echo "こんにちは" | voicevox_paplay
# 話者スタイルを指定
$ voicevox_paplay --style-id 3 "こんにちは、ずんだもんだよ!"
# 話者モデルファイルを指定(既定は 0.vvm)
$ voicevox_paplay --vvm 5.vvm --style 22 小声でずんだもんだよ
# スピードを 1.5 倍にする
$ voicevox_paplay --style-id 3 --speed 1.5 "早口で喋ります"
# WAV ファイルに保存(再生なし)
$ voicevox_paplay --style-id 3 --out output.wav "こんにちは"
3. verbose 出力
詳細なログを出力して、いま何が起こっているのか、動作を確認できます。
$ voicevox_paplay --verbose "テスト音声です"
[ 0.001s] --- args ---
[ 0.001s] style-id = -1
[ 0.001s] speed = 1.0
[ 0.001s] pitch = 0.0
[ 0.001s] intonation = 1.0
[ 0.001s] volume = 1.0
[ 0.001s] pre_silence = None
[ 0.001s] post_silence = None
[ 0.001s] device = CPU
[ 0.001s] voicevox_dir = /usr/lib/voicevox-core
[ 0.001s] onnxruntime = /usr/lib/voicevox-core/onnxruntime/lib/libvoicevox_onnxruntime.so.1.17.3
[ 0.001s] dict_dir = /usr/lib/voicevox-core/dict/open_jtalk_dic_utf_8-1.11
[ 0.001s] vvm_dir = /usr/lib/voicevox-core/models/vvms
[ 0.001s] vmms = ['0.vvm']
[ 0.001s] cache_dir = None
[ 0.001s] outfile = None
[ 0.001s] list_speakers = False
[ 0.001s] --- env ---
[ 0.001s] PULSE_SERVER = unix:/mnt/wslg/PulseServer
[ 0.001s] PULSE_COOKIE = (unset)
[ 0.001s] XDG_RUNTIME_DIR= /run/user/1000/
[ 0.001s] XDG_CACHE_HOME = (unset)
[ 0.001s] Python = 3.12.3 (main, Mar 3 2026, 12:15:18) [GCC 13.3.0]
[ 0.001s] cpu_count = 4
[ 0.001s] Onnxruntime.load_once: /usr/lib/voicevox-core/onnxruntime/lib/libvoicevox_onnxruntime.so.1.17.3
[ 0.010s] supported_devices: cpu
[ 0.010s] Synthesizer init: device=CPU, dict_dir=/usr/lib/voicevox-core/dict/open_jtalk_dic_utf_8-1.11, cpu_num_threads=4
[ 0.010s] is_gpu_mode=False
[ 0.010s] VVM search: dir=/usr/lib/voicevox-core/models/vvms, patterns=['0.vvm'], found=1
[ 0.010s] scanning meta: 0.vvm
[ 0.010s] 1 model(s) scanned
[ 0.010s] style-id resolved to 0
0.vvm: style-id: 0: 四国めたん (あまあま) ◀
0.vvm: style-id: 2: 四国めたん (ノーマル)
0.vvm: style-id: 4: 四国めたん (セクシー)
0.vvm: style-id: 6: 四国めたん (ツンツン)
0.vvm: style-id: 1: ずんだもん (あまあま)
0.vvm: style-id: 3: ずんだもん (ノーマル)
0.vvm: style-id: 5: ずんだもん (セクシー)
0.vvm: style-id: 7: ずんだもん (ツンツン)
0.vvm: style-id: 8: 春日部つむぎ (ノーマル)
0.vvm: style-id: 10: 雨晴はう (ノーマル)
[ 0.011s] target text:
テスト音声です
[ 0.011s] lazy loading: 0.vvm
[ 0.374s] loaded: 0.vvm
[ 0.374s] create_audio_query: style-id: 0 "四国めたん (あまあま)"
[ 0.385s] synthesis: {'style_id': 0, 'speed': 1.0, 'pitch': 0.0, 'intonation': 1.0, 'volume': 1.0, 'pre_silence': None, 'post_silence': None}
[ 0.757s] synthesis done: 65,580 bytes
[ 0.759s] PulseAudio playback: 1ch, 24000Hz, 16bit, 32768 frames (1.37s), pcm=65,536 bytes
[ 0.759s] timeout set: 11.4s
[ 0.762s] pa_simple_new done, writing
[ 1.079s] write done, draining
[ 2.159s] drain done
[ 2.160s] timeout cleared (remaining: 10.0s)
4. 音声キャッシュ
お喋りした音声の WAV データを、ファイルに自動保存し、再利用できます。話者やスピードなどが過去のお喋りと同一かをチェックし、同じであれば音声合成を省いて、保存した WAV データを直接再生します。
# 音声キャッシュを削除
$ rm ~/.cache/voicevox_paplay/ -rf
# 初回は、音声を再生後に保存
$ time voicevox_paplay --cache 以前に話した内容を覚えるよ
real 0m3.573s
user 0m2.991s
sys 0m0.180s
# 2回目以降、保存したデータを再生
$ time voicevox_paplay --cache 以前に話した内容を覚えるよ
real 0m2.523s
user 0m0.055s
sys 0m0.013s
音声合成モデル(vvmファイル)のローディング時間と合成時間が無いので、発話までのレスポンスが改善します。
--cache の代わりに --cache-dir を使えば、保存場所を指定できます。
インストール方法
1. VOICEVOX Core のインストール
VOICEVOX Core のダウンローダーで、モデルデータ等を取得します。
curl -sSfL https://github.com/VOICEVOX/voicevox_core/releases/latest/download/download-linux-x64 -o download
chmod +x download
sudo ./download -o /usr/lib/voicevox-core --exclude c-api
インストール先のディレクトリは -o /usr/lib/voicevox-core で指定しています。変更した場合は voicevox_paplay 実行時にも --voicevox-dir でこの場所を教えてあげる必要があります。もしくは、voicevox_paplay スクリプト自体を直接書き換えましょう。
自分の Home Directory に入れる場合は、もちろん sudo 不要です。
GitHub の rate limit でエラーになったら、gh auth login で認証しておくと緩和されます。
2. Python Binding のインストール
VOICEVOX Core と PulseAudio の Python パッケージを入れます。バージョンは適宜変更してください。
pip install https://github.com/VOICEVOX/voicevox_core/releases/download/0.16.0/voicevox_core-0.16.0+cpu-cp310-abi3-linux_x86_64.whl
pip install pasimple
3. voicevox_paplay のダウンロード
ダウンロード後、PATH の通った場所に置くか、PATH を編集して通しましょう。
curl -o voicevox_paplay https://raw.githubusercontent.com/h2suzuki/terminal-configs/main/files/voicevox_paplay
chmod +x voicevox_paplay
4. 【環境チェック】 PulseAudio に出力できることを確認
念の為、PulseAudio のオーディオ出力が生きているか、事前に確認しておきます。ここが通らないと、voicevox_paplay 以前の問題ですので。
$ paplay /usr/share/sounds/alsa/Front_Center.wav
スピーカーから、英語で「フロント・センター」と流れたはず。
なお、paplay は PulseAudio に含まれるテスト用コマンドです。Alsa に含まれる aplay の PulseAudio 版といったところ。
5. voicevox_paplay で喋らせてみる
最後に VOICEVOX で音声合成し、PulseAudio に流してみます。
$ voicevox_paplay フロント・センター
スピーカーから、日本語で「フロント・センター」と流れたら、幸せ。 ![]()
voicevox_paplay の docstring
インストール方法は、本スクリプト先頭の docstring にも記載してあります。
まとめ
- WSL2 環境では PulseAudio → WSLg 経由で、Windows ホストに簡単に音声出力できます。
- VOICEVOX Core の Python Binding を使えば、Docker や Windows アプリなしで、音声合成できます。
- VOICEVOX と PulseAudio を直結するスクリプト voicevox_paplay を作りました。
おまけ
この Python スクリプトは、素の Claude Code に依頼して書きましたが、最初のプロンプトで要件を伝えたところ、すぐ 70点くらいのコードを生成してくれました。そこからは私のレビューと Claude による修正の反復作業が続くのですが、Opus 4.6 だからか、異常終了するようなバグは数件だけでした。毎月のように精度が上がっていき、すごいなと。![]()
反復中のコード変更(10件のCommit)について、なぜ反復が必要だったかの原因分析も Claude Code にさせてみました。Claude と一緒に何ターンか推敲して、最終的に抽出された学びが、こちらです。
A. 仕様策定の不備 — ユーザーインターフェースの設計判断が欠落(4 件)
B. 局所的な正しさで完了する — 副次的影響・全体との整合を見ない(6 件)
C. 体験の質の参照基準がない — 「動く」の先の「良い」を評価していない(横断的)
提案された対策がこちら。
## 対策
### A → 仕様先行
実装前に「ユーザーに見える面」の設計判断を明示的に行う。
- デフォルト値の設計思想(副作用を伴う機能は opt-in か opt-out か)
- 出力の仕様(順序、書式、表示幅の扱い)
- エラー表示の方針(何をどこまで見せるか)
- ログ・verbose に含める情報と含めない情報の境界
### B → 副次的影響の確認
実装手段を選ぶとき、「目的を達成するか」だけでなく「他に何が起きるか」を確認する。
- この関数/オプションは、目的以外に何を出力・変更するか
- この追加は、既存のコード・出力と重複や矛盾を生まないか
- この値は、本当にリテラルで書くべきか、別の場所から導出すべきか
### C → 参照基準との比較
実装後、同じ領域で広く使われている良質なツールと体験を比較する。
「動く」は検証済みとして、その先の「良いか」を評価する基準を外部に求める。
## CLAUDE.md 向けルール
1. **仕様先行**: ユーザーに見える振る舞い(デフォルト値、出力形式、エラー表示、ログ内容)は実装前に決定する
2. **副次的影響の確認**: 実装手段を選ぶとき、目的の達成だけでなく「他に何が起きるか」を確認する
3. **参照基準との比較**: 実装後、同じ領域の良質なツールとユーザー体験を比較し、「動く」の先の「良い」を評価する
「動く」の先の「良い」を評価していない。とか、なかなか痺れる文言です(笑)
この辺りは Skills とかでカバーできるか色々と試してみようかと思います。
※ この記事も Claude in Chrome を利用して書いています。