VOICEVOXの/audio_queryのmoras[]から口パクを作る──母音→口形状マップと、実wav尺へのratio補正が「?」で崩れる話
音声合成せずに /audio_query だけを叩くと accent_phrases[].moras[] に consonant_length と vowel_length が入っており、speedScale で割れば発話タイミングを秒単位で復元できる。ただし末尾に「?」が付いたアクセント句だけは、moras[] が1バイトも変わらないまま実wavが0.128秒伸びる(VOICEVOX ENGINE 0.25.1・speedScale 1.15で実測)。手元の4,864クリップ(実音声8時間14分)で計算尺と実wav尺の比を測ると中央値0.998220だが、疑問符を含む480クリップだけ中央値1.044455に跳ねる。エンジン側の処理順を再現すると4,864件すべてサンプル単位で尺を的中できた。
目次
VOICEVOXで立ち絵の口パクを作るなら、/synthesis を叩く必要はない。/audio_query が返す accent_phrases[].moras[] に各モーラの consonant_length と vowel_length(いずれも秒)が入っているので、これを speedScale で割って足し上げれば、母音がいつ鳴るかを秒単位で復元できる。母音記号(a/i/u/e/o)を口の開き量にマップすればフレームごとの口形状が出る。ただし、末尾に「?」が付いたアクセント句だけは、moras[] が1バイトも変わらないまま実wavが伸びる。 エンジンが合成の直前に「継続長0.15秒の疑問形モーラ」を勝手に足すからで、/audio_query のレスポンスにはその0.15秒がどこにも現れない。当サイトの実装は「計算した総尺と実wav尺の比(ratio)で全体をスケールする」補正でこれを吸収するつもりだったが、実測すると疑問符を含むクリップではその補正がむしろ悪化させている(母音オンセット誤差のクリップ最大値・中央値が23.97ms→111.19ms)。正しくは、疑問形モーラの分を計算側に足してからratio補正をかける。
/audio_queryのmoras[]だけで発話タイミングは復元できる。 「ゼロ」と「ゼロ?」に対するmoras配列は完全一致し、違うのはis_interrogativeの真偽値だけ。それでも実wavは0.5120秒と0.6400秒で、差の0.128秒はレスポンスのどこにも書かれていない(VOICEVOX ENGINE 0.25.1・speaker=3・speedScale 1.15で実測)- 計算尺と実wav尺の比は、疑問符の有無で別物になる。 手元の4,864クリップ(実音声29,642.06秒=8時間14分)で測ると、疑問符なし4,384件は中央値0.997506・比が1.05を超えた件数0件。疑問符あり480件は中央値1.044455・43.1%(207件)が1.05超だった
- エンジンの処理順(前後無音→話速→93.75fpsでフレーム丸め)を再現すれば、合成せずに尺をサンプル単位で当てられる。 4,864件すべてで予測サンプル数と実wavのサンプル数の差が0だった(4,864/4,864=100.0000%)
症状:「ゼロ」と「ゼロ?」でmorasが同じなのに、wavの長さが違う
まず /audio_query の実レスポンスを見る。2026年8月14日、ローカルのVOICEVOX ENGINE 0.25.1(GET /version が "0.25.1" を返す)に POST /audio_query?text=ゼロ?&speaker=3 を投げて返ってきた本文をそのまま貼る(パラメータは既定値のまま)。
{
"accent_phrases": [
{
"moras": [
{
"text": "ゼ",
"consonant": "z",
"consonant_length": 0.11211186647415161,
"vowel": "e",
"vowel_length": 0.11219345778226852,
"pitch": 5.858644008636475
},
{
"text": "ロ",
"consonant": "r",
"consonant_length": 0.042679209262132645,
"vowel": "o",
"vowel_length": 0.2227582037448883,
"pitch": 5.829718112945557
}
],
"accent": 1,
"pause_mora": null,
"is_interrogative": true
}
],
"speedScale": 1.0,
"pitchScale": 0.0,
"intonationScale": 1.0,
"volumeScale": 1.0,
"prePhonemeLength": 0.1,
"postPhonemeLength": 0.1,
"pauseLength": null,
"pauseLengthScale": 1.0,
"outputSamplingRate": 24000,
"outputStereo": false,
"kana": "ゼ'ロ?"
}
同じ条件で text=ゼロ(疑問符なし)を投げ、両者の accent_phrases[0]["moras"] をPythonの == で比較すると True が返る。アクセント句の中で差分があるキーは is_interrogative ただ1つ。 consonant_length も vowel_length も、末尾モーラの pitch すら1ビットも動かない(クエリ全体では読み取り専用の kana が ゼ'ロ / ゼ'ロ? で異なるが、これは合成用のクエリとしては無視されるフィールドである)。
ところが、この2つのクエリに speedScale=1.15 / intonationScale=1.25 / prePhonemeLength=0.03 / postPhonemeLength=0.08 を設定して /synthesis に投げると、返るwavの長さが違う。
| 入力テキスト | is_interrogative |
morasから計算した尺 |
実wav尺 | 計算値の誤差 |
|---|---|---|---|---|
ゼロ |
false | 0.5359秒 | 0.5120秒(12,288サンプル) | −0.0239秒(−0.72フレーム@30fps) |
ゼロ? |
true | 0.5359秒 | 0.6400秒(15,360サンプル) | +0.1041秒(+3.12フレーム@30fps) |
本当に。 |
false | 0.8279秒 | 0.8107秒(19,456サンプル) | −0.0173秒(−0.52フレーム) |
本当に? |
true | 0.8279秒 | 0.9387秒(22,528サンプル) | +0.1107秒(+3.32フレーム) |
「ゼロ」と「ゼロ?」の実wavの差は 0.6400 − 0.5120 = 0.1280秒。30fpsで約3.8フレーム分、口が閉じたまま音だけ鳴る。短いスライドほど比率として効いてくる。
原因1:moras[]の型は公式スキーマどおりで、そこに疑問形の情報は無い
Mora と AccentPhrase のスキーマは公式のAPIドキュメント(VOICEVOX ENGINE OSS API Document、2026年8月14日確認)に載っている。同ページはOpenAPIスペックをページ内に埋め込んでおり、ローカルの0.25.1が /openapi.json で返すスキーマとPythonの == で比較したところ、Mora・AccentPhrase とも完全一致した。
Mora(description:モーラ(子音+母音)ごとの情報。)の各フィールドは次のとおり。
text:文字/consonant:子音の音素/consonant_length:子音の長さ/vowel:母音の音素/vowel_length:母音の長さ/pitch:音高
required は ["text", "vowel", "vowel_length", "pitch"] の4つで、consonant と consonant_length は必須ではない。手元の220,358モーラのうち55,519モーラ(25.19%)で consonant_length が null だった。内訳を母音記号で見ると i 12,959/o 12,009/N(撥音「ん」)11,692/e 5,792/cl(促音「っ」)5,010/u 4,292/a 3,765 で、母音単独のモーラと撥音・促音がここに入る。m.get("consonant_length") or 0 のように0へ落とす処理が要るのはこのため。
AccentPhrase(description:アクセント句ごとの情報。)は次のとおり。
moras:モーラのリスト/accent:アクセント箇所/pause_mora:アクセント句の末尾につく無音モーラ。null の場合は無音モーラを付けない。/is_interrogative:疑問系かどうか(default: false)
is_interrogative の説明は「疑問系かどうか」だけで、それが尺にどう影響するかはスキーマに書かれていない。pause_mora が moras の外側にぶら下がっている点も注意で、moras を舐めるだけのループは句間のポーズを丸ごと落とす。
AudioQuery 側の関連フィールドの説明も引いておく。
speedScale:全体の話速/prePhonemeLength:音声の前の無音時間/postPhonemeLength:音声の後の無音時間/pauseLengthScale:句読点などの無音時間(倍率)。デフォルト値は1/pauseLength:句読点などの無音時間。nullのときは無視される。デフォルト値はnull
原因2:エンジンは合成の直前に「継続長0.15秒の疑問形モーラ」を足している
答えはエンジンのソースにある。voicevox_engine/tts_pipeline/tts_engine.py(0.25.1タグ、2026年8月14日取得)の28行目に定数がある。
UPSPEAK_LENGTH = 0.15
UPSPEAK_PITCH_ADD = 0.3
UPSPEAK_PITCH_MAX = 6.5
そしてこの関数のdocstringが、やっていることをそのまま書いている。
def _apply_interrogative_upspeak( accent_phrases: list[AccentPhrase], enable_interrogative_upspeak: bool ) -> list[AccentPhrase]: """必要に応じて各アクセント句の末尾へ疑問形モーラ(同一母音・継続長 0.15秒・音高↑)を付与する"""
付与の条件はコメント付きで明示されている。
# 疑問形補正条件: 疑問形アクセント句 & 末尾有声モーラ if accent_phrase.is_interrogative and moras[-1].pitch > 0:
つまり「is_interrogative が真」かつ「末尾モーラが有声(pitch > 0)」のとき、末尾と同じ母音で vowel_length=0.15 のモーラが1つ追加される。これは synthesize_wave() の中で行われるので、/audio_query のレスポンスには絶対に現れない。
この処理を制御するのが /synthesis のクエリパラメータ enable_interrogative_upspeak で、OpenAPIスペック上の説明は「疑問系のテキストが与えられたら語尾を自動調整する」、default: true である。明示的にfalseを渡さない限り、常に有効になる。
0.15秒がそのまま出力に乗るわけではない。追加されたモーラも後段の speedScale 除算とフレーム丸めを通るので、speedScale=1.15 なら 0.15 / 1.15 = 0.13043秒 → 93.75fpsで round(12.2283) = 12 フレーム → 12 × 256 / 24000 = 0.1280秒。上で実測した「ゼロ」と「ゼロ?」の差 0.1280秒と一致する。
原因3:前後無音もspeedScaleで割られる(prePhonemeLengthを素で足すとズレる)
同じファイルの _query_to_decoder_feature() が、クエリを波形に変換する前の処理順を決めている。
moras = to_flatten_moras(query.accent_phrases) # 設定を適用する moras = _apply_prepost_silence(moras, query) moras = _apply_pause_length(moras, query) moras = _apply_pause_length_scale(moras, query) moras = _apply_speed_scale(moras, query)
_apply_prepost_silence() は prePhonemeLength / postPhonemeLength の長さを持つ無音モーラをモーラ列の前後に挿入する。その後に _apply_speed_scale() が走る。
def _apply_speed_scale(moras: list[Mora], query: AudioQuery) -> list[Mora]: """モーラ系列へ音声合成用のクエリがもつ話速スケール(`speedScale`)を適用する""" for mora in moras: mora.vowel_length /= query.speedScale if mora.consonant_length: mora.consonant_length /= query.speedScale
対象は「モーラ列の全要素」なので、前後無音も speedScale で割られる。前後無音を割らずに素で足す実装(当サイトのものがこれだった)は、speedScale=1.15 / pre=0.03 / post=0.08 の場合に (0.03+0.08) × (1 − 1/1.15) = 約0.0143秒だけ長く見積もる。1クリップあたり30fpsで0.4フレーム強の系統誤差になる。
原因4:長さは秒ではなくフレームに丸められる(93.75fps・偶数丸め)
最後に、モーラの長さは音素ごとにフレーム数へ丸められる。
def _to_frame(sec: float) -> int: FRAMERATE = 93.75 # 24000 / 256 [frame/sec] # NOTE: `round` は偶数丸め。移植時に取扱い注意。詳細は voicevox_engine#552 sec_rounded: NDArray[np.float64] = np.round(sec * FRAMERATE) return sec_rounded.astype(np.int32).item()
_count_frame_per_unit() は「音素ごとにフレーム長を算出し、和をモーラのフレーム長とする」と書いてあるとおり、子音と母音を別々に丸めてから足す。したがって秒で足し上げた値とは必ず端数がずれる。コメントにあるとおり np.round は偶数丸め(round half to even)なので、Pythonの組み込み round() と同じ挙動になる。
また audio_postprocessing.py の _apply_output_sampling_rate() は「サンプリングレート一致のときはスルー」なので、outputSamplingRate が既定の24000のままならリサンプルは入らない。つまり出力サンプル数 = 総フレーム数 × 256 がそのまま成立する。
対処1:口形状マップ(母音→開き量)
母音記号を口の開き量へ落とすマップは、当サイトの実装では次の5つだけである。
# 母音 → 口形状 (0=閉じ 1=小 2=開 3=大)
VOWEL_SHAPE = {"a": 3, "o": 2, "e": 2, "i": 1, "u": 1}
ここで必ず .lower() を通すこと。VOICEVOXの母音記号は小文字だけではない。voicevox_engine/tts_pipeline/phoneme.py(0.25.1タグ)に型定義がある。
# NOTE: `Vowel` は母音 (a/i/u/e/o の有声・無声) + 無音 pau + 撥音 N ("ん") + 促音 cl ("っ") # NOTE: 型の名称は暫定的 BaseVowel = Literal["pau", "N", "a", "cl", "e", "i", "o", "u"] Vowel = BaseVowel | Literal["A", "E", "I", "O", "U"]
同ファイルには _UNVOICED_MORA_TAIL_PHONEMES = ["A", "I", "U", "E", "O", "cl", "pau"] という定義もあり、大文字は無声化母音を表す。手元の220,358モーラの内訳は次のとおりだった。
| 母音記号 | 出現数 | 割合 | 扱い |
|---|---|---|---|
a |
55,503 | 25.19% | 形状3 |
o |
53,090 | 24.09% | 形状2 |
i |
32,553 | 14.77% | 形状1 |
e |
28,649 | 13.00% | 形状2 |
u |
26,511 | 12.03% | 形状1 |
N(撥音「ん」) |
11,692 | 5.31% | 除外 |
cl(促音「っ」) |
5,010 | 2.27% | 除外 |
I(無声化) |
3,910 | 1.77% | .lower()で形状1 |
U(無声化) |
3,440 | 1.56% | .lower()で形状1 |
I と U を合わせて7,350モーラ、全体の3.34%ある。.lower() を忘れると、この3.34%が既定値へ落ちる。なお A / E / O は型定義上は存在するが、この220,358モーラの中には1件も出現しなかった。pause_mora(vowel が pau)は moras の外側に9,932件あった。
対処2:疑問形モーラを計算側に足す
疑問符の0.15秒を計算に入れるのは、ソースの条件をそのまま写した2行を足すだけである。
UPSPEAK_LENGTH = 0.15 # tts_engine.py の定数と同じ値
for ap in query["accent_phrases"]:
for m in ap["moras"]:
... # consonant_length / vowel_length を speedScale で割って積み上げ
# エンジンが合成直前に足す疑問形モーラ(enable_interrogative_upspeak の既定はtrue)
if ap.get("is_interrogative") and ap["moras"] and ap["moras"][-1]["pitch"] > 0:
t += UPSPEAK_LENGTH / speed_scale
if ap.get("pause_mora"):
t += (ap["pause_mora"].get("vowel_length") or 0) / speed_scale
この1行を足さないまま「計算尺と実wav尺の比で全体をスケールする」補正をかけると、疑問符を含むクリップでは補正が逆効果になる。 末尾1か所に集中している0.128秒を、クリップ全体へ均等にばらまくからである。手元の4,864クリップ・220,358モーラで、母音オンセット時刻がエンジンの実フレーム配置からどれだけずれるかを4通りの方式で測った結果が次である。
| 方式 | 誤差の中央値 | p95 | 最大 | クリップ内最大値の中央値 | 最悪クリップ |
|---|---|---|---|---|---|
| A:補正なし | 14.18ms | 51.01ms | 215.32ms | 30.10ms | 215.32ms |
| B:ratio補正のみ(当サイトの現行実装) | 8.90ms | 35.61ms | 193.06ms | 22.30ms | 193.06ms |
| C:疑問形モーラを足す(ratio無し) | 14.04ms | 48.60ms | 134.57ms | 29.74ms | 134.57ms |
| D:疑問形モーラ+ratio補正 | 8.32ms | 26.58ms | 77.53ms | 20.45ms | 77.53ms |
疑問符の有無で分けると、AとBの評価が逆転する。
| クリップ種別 | 件数 | 補正なしのクリップ内最大値(中央値) | ratio補正のクリップ内最大値(中央値) |
|---|---|---|---|
| 疑問符なし | 4,384 | 30.66ms | 21.06ms(改善) |
| 疑問符あり | 480 | 23.97ms | 111.19ms(悪化) |
ratio補正は疑問符なしのクリップでは効いているが、疑問符ありでは中央値を4.6倍に悪化させている。方式Dなら最悪クリップが193.06ms(30fpsで5.79フレーム)から77.53ms(同2.33フレーム)まで下がる。
対処3:合成せずに尺を「サンプル単位で」当てる
エンジンの処理順(前後無音の挿入 → pauseLength → pauseLengthScale → speedScale 除算 → 音素ごとに93.75fpsで偶数丸め)をそのまま再現すると、/synthesis を叩かずにwavのサンプル数を正確に出せる。
FRAMERATE = 93.75 # 24000 / 256
UPSPEAK_LENGTH = 0.15
def to_frame(sec):
return int(round(sec * FRAMERATE)) # Python の round も偶数丸め
def predict_samples(q):
moras = []
for ap in q["accent_phrases"]:
ms = [dict(m) for m in ap["moras"]]
if ap.get("is_interrogative") and ms and ms[-1]["pitch"] > 0:
ms.append({"consonant_length": None, "vowel": ms[-1]["vowel"],
"vowel_length": UPSPEAK_LENGTH})
moras += ms
if ap.get("pause_mora"):
moras.append(dict(ap["pause_mora"]))
# 前後無音を「speedScale を割る前に」挿入する
sil = lambda L: {"consonant_length": None, "vowel": "sil", "vowel_length": L}
moras = [sil(q["prePhonemeLength"])] + moras + [sil(q["postPhonemeLength"])]
if q.get("pauseLength") is not None:
for m in moras:
if m["vowel"] == "pau":
m["vowel_length"] = q["pauseLength"]
for m in moras:
if m["vowel"] == "pau":
m["vowel_length"] *= q.get("pauseLengthScale", 1.0)
sp = q["speedScale"]
frames = 0
for m in moras:
frames += to_frame(m["vowel_length"] / sp)
if m.get("consonant_length") is not None:
frames += to_frame(m["consonant_length"] / sp)
return frames * 256 # outputSamplingRate が 24000 のとき
この記事に載せたコードをそのまま実行して、手元にキャッシュしてある audio_query のレスポンスJSONと、それを合成した実wavのペア4,864組で検証した。予測サンプル数と実wavのサンプル数の差の分布は {0: 4864}、つまり全件で差が0だった(4,864 / 4,864 = 100.0000%)。 上の「ゼロ/ゼロ?/本当に。/本当に?」のライブ検証4件でも、予測は12,288/15,360/19,456/22,528サンプルで実wavと完全に一致した。さらに実台本1本(140スライド)をその場で合成した140クリップでも、合計尺が小数6桁表示まで一致した。
これは実用上の副産物が大きい。当サイトの duration_check.py はこの性質を使って、音声を1本も合成せずに台本全体の尺を測る(docstringに「台本の実尺をビルド前にVOICEVOXで測る(音声合成せずaudio_queryだけ=数秒)」と書いてある)。字数チェックでは捕まらない「数字や英語のモーラ展開で伸びるスライド」を、レンダリング前に弾ける。
その速度差も同じ環境で測った。実際の台本(140スライド・4,132字)に対し、全スライドへ /audio_query を投げるだけなら数秒で終わり、同じ140スライドを /synthesis まで通すと10分前後かかる。同じ台本・同じマシンで2回測った結果が次である。
| 実行 | /audio_query のみ |
/synthesis まで |
1スライドあたり(合成) |
|---|---|---|---|
| 1回目(執筆時) | 2.85秒 | 574.55秒 | 4.104秒 |
| 2回目(検品時の再実行) | 1.18秒 | 623.71秒 | 4.455秒 |
倍率は安定しない。 上の2回で「合成込みの総時間 ÷ クエリだけの時間」を出すと約202倍と約529倍で、2.6倍も開く。クエリ側が1〜3秒の範囲でぶれるため、比を取ると分母のぶれがそのまま倍率に乗る。安定して言えるのは桁のほうで、クエリだけなら秒のオーダー、合成まで通すと10分のオーダーになる。
そしてこのライブ実行140本でも、予測は外れなかった。合成前に計算した総尺と、実際に合成した140本のwavの合計は、いずれも591.4880秒(14,195,712サンプル)で、差は0.000000秒(小数6桁表示)だった。1本ずつ見ても140本すべてでサンプル数の差が0である。「10分の動画になるか、8分にしかならないか」を、音声を1本も作らずに数秒で判定できる。
観測条件
| 項目 | 内容 |
|---|---|
| 日付 | 2026年8月14日 |
| OS | macOS 15.1.1(Darwin 24.1.0・arm64) |
| エンジン | VOICEVOX ENGINE 0.25.1(GET http://localhost:50021/version が "0.25.1" を返す) |
| ソースコード | GitHubの 0.25.1 タグ。tts_engine.py は master(コミット da71eda・2026-08-07)との差分が TypeAlias 構文の変更2行(typing のimport行と LatestVersion の型エイリアス宣言)だけで、尺に関わるロジックは同一。phoneme.py と audio_postprocessing.py は master と完全一致 |
| 話者 | 単発のライブ検証(ゼロ/ゼロ? ほか)は speaker=3(ずんだもん ノーマル)。キャッシュ4,864組は speaker=3 と speaker=2(四国めたん ノーマル)の2スタイル混在で、元台本から辿れた4,182組の内訳は speaker=3 が2,553組・speaker=2 が1,629組。残り682組は元台本が更新済みで特定できていない |
| クエリ設定 | speedScale=1.15 / intonationScale=1.25 / prePhonemeLength=0.03 / postPhonemeLength=0.08 / pitchScale=0.0 / volumeScale=1.0 / pauseLength=null / pauseLengthScale=1.0 / outputSamplingRate=24000 / outputStereo=false |
| 検証コーパス | .audio_cache/ に蓄積した audio_query レスポンスJSONと合成済みwavのペア4,864組(ファイル9,728本)。実音声の合計29,642.06秒=8時間14分。全件が上記のクエリ設定 |
| ライブ検証 | 上記に加えて、実台本1本(140スライド・4,132字)をその場で /audio_query→/synthesis に通した140クリップ。および「ゼロ/ゼロ?/本当に。/本当に?/そうです?/マジ?/はい?」の単発7件 |
| 内訳 | アクセント句55,479/モーラ220,358(pause_mora 9,932は別カウント)。is_interrogative が真のアクセント句は505(0.91%)。疑問符を含むクリップは480/4,864(9.87%) |
| 出力wav | 24000Hz・モノラル・16bit |
計算尺(現行実装:前後無音を素で足し、疑問形モーラを考慮しない)と実wav尺の比の分布は次のとおり。
| 母集団 | 件数 | 比の中央値 | 平均 | 比>1.05の件数 | 絶対誤差の中央値 | 同p95 | 同最大 |
|---|---|---|---|---|---|---|---|
| 全体 | 4,864 | 0.998220 | 1.003201 | 207 | 0.0231秒 | 0.1152秒 | 0.2960秒 |
| 疑問符なし | 4,384 | 0.997506 | 0.996881 | 0 | 0.0207秒 | 0.0622秒 | 0.1248秒 |
| 疑問符あり | 480 | 1.044455 | 1.060919 | 207(43.1%) | 0.1153秒 | 0.2114秒 | 0.2960秒 |
全体の分布は最小0.957334・最大1.333733、p05が0.987076、p95が1.043862、p99が1.127531。比が1.0±0.01に収まったのは3,909件(80.37%)だった。総尺どうしを比べると計算29,643.04秒 対 実測29,642.06秒で、8時間14分に対する差は−0.98秒しかない。総尺だけ見ると誤差は打ち消し合って見えるが、クリップ単位では最大0.2960秒(30fpsで8.88フレーム)ずれている。
正直な但し書き
当サイトの過去メモにあった「ratio 0.996」は再現しなかった。 今回4,864組を測り直した中央値は全体で0.998220、疑問符なしに限れば0.997506である。0.996という値がどの母集団・どの時点の設定で得られたものかは特定できていない。記事化にあたって過去の値は採らず、今回の実測値に置き換えた。
「誤差」の基準はエンジンの実フレーム配置であって、人間の聴感ではない。 母音オンセットの誤差表(A〜Dの比較)は、エンジンのソースが定める通りにフレームを積み上げた位置と、計算値との差を測ったものである。総尺については実wavのサンプル数と4,864件全件で一致することを確認したが、個々のモーラの位置が実際の波形とどれだけ合っているかは、強制アライメントなどでは検証していない。 「口が音より何ミリ秒早いか」を聴いて判定したわけでもない。
無声化母音を口形状にマップしてよいかは検証していない。 手元の実装は I / U(無声化母音、全体の3.34%)を .lower() して有声と同じ形状に落としている。無声化していても口の形自体は作られるという前提だが、見た目としてどちらが自然かは比較していない。
A / E / O は型定義に存在するが、今回のコーパスには出現しなかった。 出現しないと断定はできない(当サイトの台本の傾向でしかない)ので、実装側は既定値へのフォールバックを残しておくべきである。
enable_interrogative_upspeak=false を渡した場合は測っていない。 OpenAPIスペック上の既定値が true であることと、既定のまま叩いた結果しか確認していない。falseを渡せば0.15秒は付かないはずだが、そのケースの実測はしていない。
pauseLength が null 以外のケース、speedScale が1.15以外のケース、24000Hz以外の outputSamplingRate は未検証。 4,864組すべてが同一設定だったため、_apply_pause_length() の分岐と soxr によるリサンプル経路は実際には一度も通っていない。上のコードはソースの記述どおりに書いてあるだけで、動作確認は取れていない。
/audio_query と /synthesis の速度差は、この1台・この1本の台本での結果であり、倍率として持ち出せる数字ではない。 同じ台本を同じマシンで2回測っただけで約202倍と約529倍に開いた(クエリ側が1.18秒と2.85秒でぶれたため)。エンジンをCPUで動かしているかGPUで動かしているかは確認しておらず、/initialize_speaker による事前初期化もしていない。合成側の所要時間はハードウェアと設定で大きく変わるはずで、倍率をそのまま他の環境に持ち込むことはできない。持ち帰れるのは「クエリだけなら秒、合成まで通すと分」という桁の差のほうである。
試した話者スタイルは2つだけである。 speaker=3(ずんだもん ノーマル)と speaker=2(四国めたん ノーマル)で、サンプル単位の一致はこの2スタイルにまたがって成立している(4,864組の内訳は上の観測条件のとおり)。モーラ長は話者スタイルごとに推論されるので値そのものは変わるが、UPSPEAK_LENGTH が定数である以上0.15秒の付与自体は話者に依らないはずである。ただし他の話者・他のスタイル(あまあま、ツンツン等)では確認していない。
「日本語で誰も書いていない」は当サイトの調査範囲での話。 2026年8月14日時点で「voicevox 口パク」のサジェストで拾える記事はAviUtlやYMM4などGUIツール前提のものだった、というのが調べた範囲である。存在しないことの証明はしていない。
出典
- VOICEVOX ENGINE OSS API Document(
/audio_query、Mora・AccentPhrase・AudioQueryスキーマ、2026年8月14日確認): https://voicevox.github.io/voicevox_engine/api/ - VOICEVOX/voicevox_engine - GitHub(2026年8月14日確認): https://github.com/VOICEVOX/voicevox_engine
voicevox_engine/tts_pipeline/tts_engine.py(0.25.1タグ。UPSPEAK_LENGTH、_apply_interrogative_upspeak()、_apply_prepost_silence()、_apply_speed_scale()、_count_frame_per_unit()、_to_frame()、_query_to_decoder_feature()、2026年8月14日取得): https://github.com/VOICEVOX/voicevox_engine/blob/0.25.1/voicevox_engine/tts_pipeline/tts_engine.pyvoicevox_engine/tts_pipeline/phoneme.py(0.25.1タグ。BaseVowel/Vowel/_UNVOICED_MORA_TAIL_PHONEMES、2026年8月14日取得): https://github.com/VOICEVOX/voicevox_engine/blob/0.25.1/voicevox_engine/tts_pipeline/phoneme.pyvoicevox_engine/tts_pipeline/audio_postprocessing.py(0.25.1タグ。_apply_output_sampling_rate()、2026年8月14日取得): https://github.com/VOICEVOX/voicevox_engine/blob/0.25.1/voicevox_engine/tts_pipeline/audio_postprocessing.py- AI時短ラボ 実測ログ: ローカルのVOICEVOX ENGINE 0.25.1に対する
/audio_query・/synthesisの直接実行、およびmotion_pipeline/.audio_cache/のクエリJSON・wavペア4,864組の一括検証(いずれも2026年8月14日実施)
出典・参照資料
AIニュースの解説を動画でも
YouTubeでは注目ニュースの背景を解説し、Xでは新着記事をお知らせしています。
コメント
まだコメントはありません。最初のコメントを書いてみませんか?
AIについて聞きたいことはありますか?
質問箱で無料で受け付けています。回答は公開され、他の方の参考にもなります。
質問箱を見る →新しい記事をメールで受け取る
AIの新しい発表を、出典付きで整理して届けます。