ネット上の話題を拾って短い動画へ仕立てる自作ツールを動かしている。事前に検査ログの緑表示を確かめて納品した動画に、試写から4件のダメ出しが返ってきた。
「声のトーンが不安定」「イントネーションがおかしい」「読み間違いがある」「内容が頭に入らない」。4つの指摘が並んでいた。
緑判定を出したはずの検品ゲートをすり抜けている。
試写での4件NG判定
自作ツールの検品ゲートは全項目パスの緑と判定していた。受け取った側が聞いた音は、シーンが切り替わるたびに声の高さが上下して落ち着かない仕上がりだった。
現場で起きた現象を整理した。
| 指摘項目 | 検査ゲートの状態 | 実際の音声と台本の状態 |
|---|---|---|
| 声のトーン | 合格(緑) | シーン境界で声の高さが急変 |
| 声質 | ログ出力のみ | 4シーンで12〜17%のずれを放置 |
| 単語の読み | 検査なし | 登録5シーン中3シーンで置換が不発 |
| 台本構造 | 検査スキップ | 構成案フィールド欠落で検査素通り |
問題は単一の不具合ではない。
音声測定、単語置換、台本構文の3箇所で、同じ緩さがすり抜けを招いていた。
アンカー差による個別判定
旧ゲートは各シーンの基本周波数をPraatのマニュアルにある標準範囲で測り、基準アンカーとの差分だけで合否を決めていた。アンカーとの差が一定以内なら合格という設計だ。
各シーンの測定値は、アンカーに対して+0.67半音と-0.69半音だった。個別の許容範囲に収まっているため、ゲートはどのシーンにも合格を出した。
だが、この判定方式は隣のシーンをまったく見ていない。
自己相関法によるピッチ分析で得たF0の実測値は268Hzから290Hzに散らばっていた。
個別には基準値の近傍にとどまっていても、正負に振れた2つのシーンが接続された瞬間、聴き手の耳には1.36半音の段差となって突き刺さる。
人の耳はアンカーとの絶対値ではなく直前の音との落差に反応する。
音響の測定では、音源が異なっても約9msの無音時間があればエコーの分離を知覚できるとされる。シーンの継ぎ目に置かれた無音が、1.36半音の落差を際立たせていた。
基準点との距離だけを測る絶対評価は、時系列で連続する表現の破綻を見落とす。
隣接シーンの1.36半音差
ログを掘り直すと、声質のずれが1312Hzから1596Hzまで19.3%も開いていた。
ピッチのずれは補正処理を通していた。声質のずれは数値を検出してログへ書き出すだけで、合否判定に組み込まず放置していた。
ログに記録した事実で満足し、判定を止めていた。
# 旧ゲート: 単独シーンの絶対評価
assert abs(scene.pitch - ANCHOR_PITCH) <= TOLERANCE
# 新ゲート: 隣接差と全体レンジの複合評価
assert abs(current_scene.pitch - prev_scene.pitch) <= ADJACENT_LIMIT
assert max(pitches) - min(pitches) <= RANGE_LIMIT
音色はピッチやラウドネスが揃っていても明確な違いとして知覚される。ピッチだけ整えて音色差を放任すれば、別の人物が交代で喋っているような違和感が残る。
読みの不発も同じ構造だった。台本を書き換えた際に読み辞書を更新せず放置した結果、登録5シーン中3シーンで置換が空振りしていた。
本文に対応するキーが存在しない場合、エンジンは何の警告も出さずに原文の漢字をそのまま読む。
改稿版でも同じ不発が5件起きていた。
台本構文の検査も穴だらけだった。構文チェッカーの冒頭に置かれた条件分岐が、構成案フィールドの存在を前提にしていた。
手作業で台本を整えた際に構成案フィールドを省いたところ、構文検査そのものが静かにスキップされた。
通過したのではなく、誰も検査していなかった。
段差チェックと三層の防御

隣接差と全体レンジと存在必須の検査で揃えた129件のテスト通過
隣接シーン間の段差チェックと、動画全体のピッチレンジを制限する検査を新設した。放置していた声質の乖離は、閾値を超えた時点で明示警告を発する仕様へ引き上げた。
台本検査は、構成案フィールドが存在しない場合に即座に違反を返す設計へ改めた。存在しない入力は合格ではなく異常として扱う。Pydanticのフィールド定義が必須指定を厳密に扱うのと同様、欠落を検知して停止させる。
読み辞書には、使われていない死んだキーの検出と、数字や助数詞に対する読み指定漏れの検査を追加した。
空振りした辞書を放置しない。
ただし、隣接段差の制限を厳しくしすぎると、強調や感情表現のための意図的な声のメリハリまで誤検知で弾く。音色の知覚を数値だけで完全に縛ることは難しいため、数値ゲートは異常値を早期に止める安全網として運用し、最終的なニュアンスは人の試写で確かめる前提を崩さない。
修正を入れたテストスイートを回すと、パス件数は109件から129件へ増えた。Pythonのdoctestが全件合格時に何も言わない仕様のように、テスト結果は静かに緑へ揃った。
1.36半音の段差を消すコードを書き終えた私の耳に、試写のダメ出しだけが一段高いトーンで残り続けている。



