ブログに戻る
AIGoogleCloudFFmpeg個人開発

TTS エンジンを置き換えた。API 呼び出しは簡単だった。

Google Chirp 3 HD を使った AI 吹き替えで、音声合成よりも時間制約への適合が本質的な課題だった実装記録。19 言語対応までの道のり。

2026年9月13日

TTS エンジンを置き換えた。API 呼び出しは簡単だった。

Katto には既に AI 吹き替え機能があった。クリエイターがショート動画を選び、言語を指定すれば翻訳版が返ってくる。初期バージョンは Kokoro を使い、8 言語に対応していた。

最近、主要な音声エンジンを Google Cloud Text-to-Speech の Chirp 3 HD に移行した。目標は明快に見えた。音声品質を向上させ、対応言語を増やす。現在 Katto のエディタは 19 の吹き替え言語と 12 の厳選された音声を提供している。

移行作業はほぼ API の差し替えで済むと思っていた。

そうではなかった。

自然な文章を生成するのは簡単だ。その文章を、別の話者が残した正確な時間枠の中で開始・終了させることが、吹き替えの本質的な問題である。

元の話者が 2.4 秒で何かを言い、翻訳が 3.2 秒かかる場合、優れた TTS モデルでもクリップを救えない。音声が次の文に食い込むか、字幕がずれるか、あるいは急いでいるように聞こえるまで音声を加速しなければならない。

TTS リクエストは、システム全体の中で最も小さな部分だと判明した。

最終的に構築したパイプライン

キャッシュされた単語タイミング
        |
        v
固定時間スロットを持つフレーズセグメント
        |
        v
長さを考慮した翻訳
        |
        v
Google Chirp 3 HD、フレーズごとに合成
        |
        v
FFmpeg による境界付き音声調整
        |
        +----> 同じセグメントから生成された翻訳字幕
        |
        v
最終 MP4 再多重化

各段階は、より単純なバージョンが別の形で失敗したために存在している。

元のタイミングをデータとして保持する

Katto は既に文字起こしパイプラインから単語タイムスタンプを持っている。文字起こし全体を一つのブロックとして翻訳や TTS に送ることはしない。単語をフレーズにグループ化する。

フレーズは文の句読点で終わるか、0.6 秒を超える間があった後、または 6 秒を超えそうになったときに区切られる。すべてのセグメントは元の開始時刻と終了時刻を保持する。これらの数値が、後続のすべての段階における契約となる。

文字起こし全体を合成すればより滑らかに聞こえるかもしれないが、音声を動画のタイムラインに戻すために必要なアンカーが失われる。フレーズレベルの合成は、自然な音声に十分な文脈を保ちながら、有用なタイミング境界を維持する。

意味だけでなく、発話時間を考慮した翻訳

書き言葉として正しい翻訳が、吹き替え翻訳として悪い場合がある。言語によっては同じ意味を表現するのにより多くの音節が必要になる。直訳はあらゆるニュアンスを保持しても、割り当てられた時間枠に収まらないために使えないことがある。

各フレーズグループに対して、Claude Haiku に短い言い回しと少ない音節を優先する自然な口語翻訳を依頼する。レスポンスはすべてのソースセグメントインデックスを保持する。

プロンプトは音声システムの一部である。その役割は言語的正確性だけではない。後で必要となる時間伸縮を減らすことも目的としている。

これはヒューリスティックのままである。選択された音声もペースを変えるため、LLM は時間を保証できない。音声レイヤーは生成された WAV を測定する。ターゲット言語とソースセグメントをキーにしたプロセス内キャッシュにより、worker が動作している間は再レンダリングで翻訳を再利用できる。

フレーズごとに合成する

翻訳された各フレーズは、24 kHz LINEAR16 音声として Google Cloud Text-to-Speech REST API に送られる。音声名は Google のドキュメントに記載されている locale-Chirp3-HD-speaker 形式に従う。

Google は Katto が現在公開しているよりも多くの Chirp 3 HD ロケールをサポートしている。製品を意図的に 19 言語に制限した。UI、ロケールマッピング、フォント、字幕、フォールバック動作を含む完全なパスを配線した言語だけだ。API が言語を主張しても、周辺の製品が正しくサポートしているとは限らない。

音声を破壊せずにスロットに収める

合成後、生成された時間と元のスロットを比較する。

target_duration = segment["end"] - segment["start"]
ratio = generated_duration / target_duration

音声がわずかに長すぎる場合、FFmpeg の atempo フィルタでピッチを保ちながら速度を上げる。速度上昇は 1.3 倍に制限している。

この上限は重要だ。以前の 1.6 倍制限ではより多くのフレーズが収まったが、一部の音声が不自然に急いでいるように聞こえた。結果が不快であれば、完璧な整合性は役に立たない。

調整された各フレーズは、マスタートラックの元のオフセットに配置される。短いフレーズは自然な間を残す。長いフレーズは境界付き補正を受ける。これはすべての病的な翻訳を解決するわけではない。より有用なルールを強制する。数学的に完璧なタイミングを追求する前に、明瞭性を保持する。

音声と字幕に同じ時計を与える

合成レスポンスは音声を提供するが、新しく話されたテキストの信頼できる単語タイムスタンプは提供しない。単語レベルのタイミングを発明すれば、字幕は正確に見えるが間違っている。

したがって、吹き替えクリップは音声に使用したのと同じ翻訳セグメントから生成されたフレーズレベルの字幕を使用する。開始時刻と終了時刻は既に音声配置と一致している。

レンダラーはターゲットスクリプトをカバーするフォントも選択する。ラテン文字フォントだけでは日本語、中国語、ヒンディー語、アラビア語には不十分だ。イントロタイトルと AI フックも同じローカライゼーションパスに従うため、画面が一つの言語を話し、別の言語を表示することはない。

通常の本番環境の問題も重要だった

テストしたすべてのロケールで、すべての厳選されたスピーカーが機能したわけではない。選択された音声が利用できない場合、Katto は選択された性別の既知のデフォルトで一度再試行する。エディタのライブプレビューは同じ Google エンジンを呼び出す。別のモデルからのサンプルでは、ユーザーが決して受け取らない音声を選択させることになる。

本番 API キーは VPS の IPv4 アドレスに制限されているが、VPS は Google に到達する際に IPv6 を優先した。TTS 呼び出しが IPv4 を経由するように強制されるまで、有効なリクエストが拒否された。API、キー、コードは正しかった。ネットワークパスが正しくなかった。

吹き替えは別の動画バリアントを生成する。翻訳、合成、字幕レンダリング、または再多重化が失敗しても、元のクリップはそのまま残る。初期の 8 言語では、Google パスが無効または未設定の場合も Kokoro をフォールバックとして利用できる。

変わったこと、変わらなかったこと

目に見える結果は、吹き替え言語が 8 から 19 に拡大し、音声選択とライブプレビューが追加されたことだった。

より深い改善はアーキテクチャにあった。翻訳、音声、字幕、オーバーレイ、動画レンダリングが一つの時間単位、フレーズセグメントを共有するようになった。

システムは元の話者をクローンせず、リップシンクを実行せず、生成された単語タイムスタンプを発明しない。非常に長い翻訳は理想的なスロットを超える可能性がある。これらの制限は、パイプラインが提供できない精度を主張するよりも優れている。

音声エンジンを置き換えているつもりだった。結局、メディアパイプラインのほぼすべての段階間の契約を引き締めることになった。

API 呼び出しは簡単だった。結果を元のタイムラインに属させることが作業だった。

Katto は長い動画を短い縦型クリップに変換し、それらのクリップを 19 言語に吹き替えることができる。多言語動画を構築している場合、音声を急いでいるように聞こえさせずにタイミングをどう処理しているか、興味がある。

参考資料

関連記事

動画をバズるクリップに変える準備はできましたか?

Katto は長尺動画を自動でクリップ化・字幕付け・リフレームし、ショート動画に変換します。

Katto を無料で試す
TTS エンジンを置き換えた。API 呼び出しは簡単だった。 | Katto