Communitygithub.com

動画ローカライズ:声質クローンの中国語吹き替え

恒等タイムライン上で、動画の英語音声を元話者の声をクローンした中国語吹き替えに置き換え。映像も長さもそのまま、BGM を残し、中英二言語字幕を焼き込み、尺・コールドレビュー・聴感の品質ゲート付き。

動画ローカライズ:声質クローンの中国語吹き替え とは?

「動画ローカライズ」レシピのステップ 3(吹き替え)、ChHsiching/video-dubbing-skill より。元動画と二言語字幕の成果物を読み込み、既存成果には触れずに 2 本目のリリースを作ります。エージェントは吹き替え用に翻訳(字幕の断片ではなく、キューごとの文字数予算に収めた完全な文)し、尺ゲート・コールドレビュー・聴感ゲート・焼き込み後レビューを実施。決定的処理は `cook dub` CLI が担い、Demucs で BGM を保持、IndexTTS2 で話者の声をクローン、各キューの音声をキュー単位の atempo で元の時間枠に収め、ffmpeg で中英字幕を焼き込みます。出力は元と同じ長さの dubbed.mp4 とアップロード用 zh/en SRT。

対応✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ChHsiching/video-dubbing-skill/tree/HEAD/skills/video-dubbing

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

ビデオの元の英語音声を中国語の吹き替えに置き換え、同一タイムライン上で行います。ビデオの再タイミングは行われず、すべてのフレームは元のタイムスタンプを保持し、吹き替え版の長さは元のビデオとまったく同じになります。各キューの合成音声は、それぞれの元のウィンドウ(キューの開始 → 次のキューの開始)に収められ、キューごとにアテンポ(atempo)が適用されます。シンセシス(合成)が速くても遅くても、どちらもウィンドウ内に収まります。結果として、同じ速度で同じ画像、中国語音声、バイリンガル(ZH+EN)字幕が焼き込まれた第2のリリースが生成されます。

このスキルは、CLIではできない2つのクリエイティブな部分を担当します。吹き替え用の翻訳(字幕の断片化ではなく、文字数の予算に合わせて完全な文を書くこと)と、品質ゲートの実行(長さゲート、コールドレビュー、耳ゲート、焼き込み後レビュー)です。決定論的な実行(Demucs、IndexTTS2、アセンブリ、リタイミング、ffmpeg)は、cook CLIのcook dubサブコマンドによって処理されます。すべてのステージスクリプトはcookパッケージ(stages/dub/)内にあります。以下の呼び出しはすべてcookコマンドです。pip install -U video-cookで一度アップグレードすれば、すべてが更新されます。

このスキルを使用するタイミング

以下の条件を満たすビデオがある場合:

  • 生のビデオファイル(<output-root>/raw/<name>.raw.mp4) — オリジナルで、英語の音声が含まれている。
  • video-subtitleからのバイリンガル字幕ラン — 具体的にはtranscript/<name>.en.full.srt(whisperXのフラグメントからマージされた、完全な文の英語トランスクリプト)とtranscript/translations.txt。

中国語吹き替え版をリリースしたい場合。これらがまだない場合は、最初にvideo-download、次にvideo-subtitleを実行してください — このスキルはそれらの出力を読み取ります。

生成物

3つの成果物が生成されます: cooked/<name>.dubbed.mp4(生のビデオはそのまま、中国語吹き替え、焼き込み済みバイリンガル字幕 — 長さは生のビデオと同じ)、cloud-srt/zh.dub.srt + en.dub.srt(アップロード用字幕)。その他はすべてdubbed/の下の作業ファイルです。以下の注釈付きツリーは、それらすべてをマッピングしています。

ステップ6のチェックが合格するまで、実行は完了しません。

ディレクトリ構造

このスキルはdubbed/(作業ディレクトリ)を追加し、最終成果物をcooked/とcloud-srt/に書き込みます:

<output-root>/
├── raw/                            ← video-downloadからの入力(このスキルが読み取る)
│   └── <name>.raw.mp4
├── transcript/                     ← video-subtitleからの入力(このスキルが読み取り、追加する)
│   ├── <name>.en.full.srt          ← 完全な文の英語(吹き替えスクリプトのソース)
│   ├── translations_dub.txt        ← このスキルが書き込む: キューごとに1行の中国語
│   └── <name>.zh.dub.srt           ← このスキルが書き込む
├── cooked/                         ← 最終ビデオはここに格納される
│   ├── <name>.cooked.bar.mp4       ← video-subtitleからの出力(変更なし)
│   └── <name>.dubbed.mp4           ← このスキルの成果物
├── cloud-srt/                      ← アップロード用字幕はここに格納される
│   ├── zh.srt / en.srt             ← video-subtitleからの出力(変更なし)
│   └── zh.dub.srt / en.dub.srt     ← このスキルのアップロード用字幕
└── dubbed/                         ← このスキルの作業ディレクトリ
    ├── _reference/
    │   └── ref.wav
    └── _full/
        ├── _segments/              ← キューごとのIndexTTS2キャッシュ
        ├── dub.wav                 ← 元のクロック上の吹き替え
        ├── dubbing.srt             ← 作業ファイル(ZH、短縮前)
        ├── dubbing.short.srt       ← 作業ファイル(ZH、短縮出力)
        ├── dubbing.merged.srt      ← 作業ファイル(ZH、短縮後; cloud-srtにコピーされる)
        ├── dubbing.en.srt          ← 作業ファイル(完全な文のEN)
        ├── dubbing.en.short.srt / dubbing.en.merged.srt  ← その短縮+マージ出力
        ├── dubbing.bilingual.srt   ← 作業ファイル(バイリンガルユニオン; 焼き込まれるもの)
        ├── dubbing.cooked.ass     ← 作業ファイル(ass出力、burn.assにコピーされる)
        └── burn.ass                ← 実際に焼き込まれるASS

ルール: dubbed/ は作業ディレクトリです。cooked/<name>.dubbed.mp4 と cloud-srt/{zh,en}.dub.srt が成果物です。 raw/、transcript/<name>.zh.srt、cooked/<name>.cooked.mp4、cloud-srt/{zh,en}.srt には決して触れないでください — これらは video-subtitle に属します。このスキルが途中で失敗した場合でも、バイリンガルのcooked成果物は完全なままです。

パイプライン

クリエイティブ作業の後の2つのステージ: synth と assemble。cookを通じて呼び出されます:

cook dub synth    <root> <name> --python <indextts-venv>/Scripts/python.exe
cook dub assemble <root> <name> --python <indextts-venv>/Scripts/python.exe [--keep-subs]
# または両方:
cook dub full <root> <name> --python <indextts-venv>/Scripts/python.exe

以下のステップでは、各ステージが内部で何を行うかを説明します(出力を検証し、障害を診断できるようにするため)。cook dub <stage> コマンドが、それらを実行する方法です。

ステップ0 — 環境の解決

2つのPython環境が関係しており、それらを混同することが、このステップが存在する理由である障害モードです:

  • cookの環境(システムPythonまたは~/.venvs/video-tools/) — cook CLIが存在し、whisperX/yt-dlp/torch(字幕パイプライン用)がある場所。
  • IndexTTS2の環境(~/Git/index-tts/.venv) — indextts、torch、demucs、whisperxを保持する独立したvenv(吹き替えパイプラインは、上流のロックファイルに最後の2つを追加します)。これらの依存関係は大きく、意図的に分離されています。cookの環境にインストールしようとしないでください。

cookは、--pythonを介して各吹き替えステージをIndexTTS2 venvのサブプロセスとして実行するため、from indextts import ... はそこで解決されます。このスキルのすべての cook dub コマンドは --python <indextts-venv>/Scripts/python.exe を取ります。 venvパスを一度解決し(デフォルト ~/Git/index-tts/.venv)、実行全体で再利用します。

0a. 両方の環境にアクセス可能であることを確認:

<cook-venv>/Scripts/cook doctor                                    # whisperX/yt-dlp/ffmpeg
<indextts-venv>/Scripts/python -c "from indextts.infer_v2_5 import IndexTTS2; import demucs, whisperx; print('ok')"   # indextts (v2.5) + demucs + whisperx

0b. シングルスレッド制約。 IndexTTS2はシングルスレッドで実行する必要があります(OMP_NUM_THREADS=1)。そうしないと、品質の低い音声が生成されます。cookはtorchをインポートする前にこれを内部的に設定するため、自分でエクスポートする必要はありません。

cookのdoctorがwhisperX/yt-dlp/ffmpegのインストールを報告し、IndexTTS2 venvがindextts(v2.5)+ demucs + whisperxをインポートし、--pythonとして渡す<indextts-venv>/Scripts/python.exeの絶対パスがわかっている場合、このステップは完了です。

ステップ1 — 生のビデオからボーカルを分離

元のオーディオは1つのミックスされたトラックです(ボーカル+ BGM + SFX)。Demucsがそれを分割し、クリーンなリファレンスを抽出し、後でBGMをチェックできるようにします。

cook dub separate <output-root> <name> [--model htdemucs] --python <indextts-venv>/Scripts/python.exe

DemucsはIndexTTS2 venvにあるため、--pythonはそこを指します。htdemucs(シングルモデル、約3GB RAM)を使用してください。htdemucs_ft(4モデルのバッグ、約20GB RAM — 32GBマシンではOOM)は使用しないでください。品質は若干低下しますが、リファレンス抽出には十分です。

フォアグラウンドモード(デフォルト)では、cookが自動的に分離されたステムをdubbed/vocals.wav + dubbed/no_vocals.wavに移動します。--detachモードでは、完了マーカーが表示された後、自分で移動します。

dubbed/vocals.wav と dubbed/no_vocals.wav の両方が存在し、その長さが生のビデオの±0.5秒以内である場合、このステップは完了です。

ステップ2 — リファレンスクリップの抽出

IndexTTS2には、元の話者の14~30秒のクリーンなクリップが必要です。最大でも最初の15秒しか読み取らないため、フルキャパシティのウィンドウは短いものより優れています。IndexTTS2は音色だけでなく韻律もクローンします。ウィンドウの配信ペースが、吹き替え全体の配信ペースになります。デフォルトのピッカーは最も長い連続した音声を取得しますが、これは最も遅く、最も意図的な箇所を選択する傾向があり、多くの場合誤りです。スキャナーを優先してください:

cook dub scan-reference <output-root> <name> \
    [--full-band] [--python <indextts-venv>/Scripts/python.exe]

これは、固定された15秒のウィンドウを音節レート(密度とポーズでフィルタリング)でランク付けし、上位3つの候補を切り出します(--full-band を使用するとフルバンド48kコピーも)。密度だけがすべてではありません — IndexTTS2はウィンドウの韻律もクローンするため、活気についても候補をランク付けします: ウィンドウごとのpyin F0標準偏差と半音範囲(平坦なウィンドウは平坦な吹き替えになります。detect_pitch_frequency ではなく librosa.pyin を使用してください。detect_pitch_frequency は無音時に発声を報告するためです)。再利用する前に、既存のリファレンスファイルを認証してください: 再処理された派生物は、話者埋め込みとピッチ統計を保持しますが、波形相関は失うため、相関がゼロに近いことは「異なる話者」を意味しません — cook dub scan-reference --score <file> --anchor <anchor.wav> でアンカークリップとスコアリングし(同じ話者の実際のオーディオは0.94近くになります。合成されたものは0.66~0.76が通常の範囲です)、pyin統計を比較します。いずれにせよ: ユーザーの耳が常にリファレンスを選択します。 候補ごとにパイロット文を一度合成し(ステップ3a)、各テイクを小規模モデルのASRラウンドトリップで事前スクリーニングし(ドロップされた単語はガーブルを示します。繁体字中国語の出力は、ウィンドウがテイクににじみ出るアクセントを示します)、cook dub scan-reference --score でスコアリングし、ラウドネスが一致したwavファイルをユーザーに渡します。

dubbed/_reference/ref.wav が存在し、長さが14~30秒で、0.3秒を超える無音ギャップがない場合(ffmpeg -af silencedetect)、このステップは完了です。

ステップ3 — 吹き替え用の翻訳(エージェントが担当)

ここが、吹き替えが字幕と分岐するポイントです。translations.txt(字幕の翻訳)は使用しないでください — これはwhisperXのフラグメントカットに従っており、文を分割します。吹き替えには完全な文が必要であり、中国語が話されたときに自然に流れるようにするためです。

<output-root>/transcript/<name>.en.full.srt(完全な文の英語トランスクリプト)を読み取ります。各キューを自分で翻訳し、transcript/translations_dub.txt に書き込みます — 英語のキューごとに1行の中国語、行N = キューN。

文字数予算ルール — 吹き替え翻訳の核心。 各キューの予算は ウィンドウ秒数 × 通常の話速 であり、書く前に固定されます。ウィンドウはキューの開始から次のキューの開始まで(ウィンドウ + 後のポーズ)、話速は推定ではなく測定されます — ユーザーが選択したパイロットに対して cook dub measure-rate を実行します(手順: ステップ3a)。推定された話速はすべてのゲートを通過し、数秒のデッドエアに吹き替えられます: 推定5.0 char/sで予算を組んだ45分の講義が実際には6.93だった場合、83のキューが>2.5秒の無音を残し、82キューの再合成にコストがかかりました。予算に合わせて書きましょう: 少しオーバーしても問題ありませんが、著しくアンダーだと問題です。ショートテイクは自然なペースを維持し、呼吸のためのポーズを残すため、予算以下のテキストはデッドエアを意味し、予算以上のテキストは可聴な圧縮を意味します。リファレンスや長さ係数が変更された場合は再調整してください: 廃止されたリファレンスに合わせて調整された予算は、すべてのゲートを通過しても、依然としてデッドエアに吹き替えられます。話者ごとに長さ帯をまたいだ層化プローブ(5~10の代表的なキュー)を合成し、測定された話速でウィンドウに対してスクリプトを再確認します。手順は REFERENCE.md → 「話速の再調整」 を参照してください。予算を正当に埋める方法(そして「埋める」ことが何を発明してはならないか): REFERENCE.md → 「文字数予算を埋める」。

翻訳の原則(予算以外):

  • 完全な考えを翻訳し、断片ではない。 英語はすでに完全な文です。中国語のキューは1つの完全な考えです。
  • 中国の開発者がそうするように、技術用語は英語のままにする — spec、plan、prototype、agent、token、skill、session、branch、routeなど。 REFERENCE.md → 「用語保持リスト」 を参照してください。
  • 画面上に表示されるものは英語のままにする — UIラベル、コード、URL、ファイル名。例は REFERENCE.md → 「用語保持リスト」 にあります。
  • 画面上に表示されない場合で、標準的な中国語名がある概念は翻訳する — 実際の例は用語保持リストにあります。
  • 行数はキュー数と等しくなければなりません。 カウントを変更するための承認された方法は、ステップ3aの build_merge.py であり、両方のファイルを一緒に書き換えます。

次に、アセンブリ前のSRTを生成します(タイムスタンプは en.full.srt から継承):

cook dub make-srt <output-root> <name>

自己レビュー — 2回のパス、機械的なゲートの前に: すべての行を頭の中で話された文章として声に出して読んでください(人が言いそうなことに聞こえますか?)。次に、すべての行に対して以下の用語リストをスキャンします(画面上のものは英語のまま、標準的な概念は中国語にします)。これらは自分自身の作業をチェックすることです。以下のサブエージェントレビューはコールドリードです。

長さゲート — 機械的(純粋な算術、1秒で完了):

cook dub gate <output-root> <name> --rate <measured chars/s>

これは、すべての行をそのキューのウィンドウに対して3つの方法でクロスチェックします: 短い行(≤8音節 — IndexTTS2のナレーションペースのトラップ、より充実させるか、ステップ3aでマージさせる)、予算ミス(推定発話がウィンドウを大幅に超えている — テキストを削減する必要がある — または大幅に下回っている — ウィンドウにデッドエアが発生するため、埋める)、およびカバレッジ合計(Σ推定発話 / Σウィンドウ — 均一に薄い翻訳はすべての行チェックを通過しても、長い無音に吹き替えられます。健全な範囲は90~105%)。--rate は必須です(測定値。--self-measure は既存のテイクから再導出します)。終了コード1は、問題のある行とカバレッジの判定を一覧表示します。書き直して、合格するまで再実行してください。予算超過の行は、短く書き直すか、cook dub merge で隣の行にマージすることで修正します — アテンポ圧縮に任せて修正することは決してありません。

品質ゲート — ファンアウトサブエージェントレビュー(必須、合成前)。 耳ゲートとこのレビューだけが、何時間もの合成の前にあるゲートです。<name>.en.full.srt と translations_dub.txt の両方への読み取りアクセス権を持つサブエージェントをファンアウトし、すべてのキューについて以下をチェックするよう依頼します:

  1. 翻訳の正確性 — 中国語は英語を忠実に伝えていますか? 節の欠落、コンテンツの追加(英語が言っていないこと)、誤訳はありませんか?
  2. 固有名詞のスペル — 名前(人、製品、企業)はソースで使用されているものと正確に同じですか? 「Claude」は「克劳德」ではなく、「IndexTTS」は「索引TTS」ではありません。ただし、標準的な中国語名が実際に存在する場合はその限りではありません。
  3. TTSの読みやすさ — IndexTTS2はこれを自然に発音しますか? 不自然な文字シーケンス、孤立した漢字の pu はありませんか?

関連スキル