Ofox運営チームによる技術共有です。自社の検証素材をもとに、AIを使って記事を編集しました。本稿で確認するのは、保存済み音声を使った動画の再構成です。新しい音声生成、発音の母語話者評価、リップシンクの検証は行いません。
製品動画を日本語にするとき、英語のタイムラインに翻訳音声を置くだけでは、語尾が切れることがあります。Gemini TTSなどで台詞を生成した後は、文字数ではなく実ファイルの長さを測り、シーンごとに配置を決めます。
ここでは30秒のスクリーンショット動画を、保存済みの日本語WAV5本と組み合わせます。音声の生成と動画編集を分けるため、最初の再構成にAPIキーは必要ありません。素材とスクリプトは後述の原文からダウンロードできます。
1. 翻訳する単位と映像の範囲を決める
台本は段落ごとではなく、画面の操作に対応する台詞に分けます。今回の順序は、目的の確認、モデル一覧、ドキュメント、APIリファレンス、最後の確認、の5つです。元動画の区切りは0、4、11、18、25、30秒で、各シーンは4、7、7、7、5秒あります。
本例の映像は英語UIの記録です。声を日本語にしても、画面やタイトルカードまで日本語化したとは言えません。人物が話す映像ではないため、口の動きを合わせる処理もありません。自社の画面を使うなら、台詞が指す操作と実際の画面を一つずつ照合してください。
台詞には製品名、略語、数字、翻訳しないUIラベルを明記します。日本語原稿の冒頭は「わかりやすい製品動画は、目的を明確にすることから始まります。」です。意味を保ってローカライズし、古いスクリーンショットにある価格やキャンペーンを現在の条件として読み上げないようにします。
2. 保存済みWAVを測る
元の公開プロジェクトでは2026年10月9日、Ofox経由でGemini TTSの日本語・韓国語音声を生成しています。今回は保存済みファイルの再利用で、新しい課金リクエストを送っていません。過去の音声設定やモデル名を、現在の提供条件や品質保証に読み替えないでください。
FFmpegとffprobeが使える環境で、まず1本を調べます。
ffprobe -v error \
-show_entries stream=codec_name,sample_rate,channels \
-show_entries format=duration -of json ja/line-1.wav
同梱音声は24kHz、モノラル、16bit PCMのWAVです。測るのは先頭・末尾の無音を含むファイル全体の長さで、単語境界ではありません。
| シーン | 日本語WAV | 元シーン | 確認すること |
|---|---|---|---|
| 目的の確認 | 5.32秒 | 4秒 | 音声だけで元の尺を超える |
| モデル一覧 | 4.80秒 | 7秒 | 音声後に画面を見る時間を残す |
| ドキュメント | 3.80秒 | 7秒 | 台詞と画面の対応を確認する |
| APIリファレンス | 6.12秒 | 7秒 | 前後の余白も含めて計算する |
| 最後の確認 | 5.48秒 | 5秒 | 最終文の語尾を切らない |
この5文は「日本語は常に長い」という一般則ではありません。同じ声でも文章、句読点、生成結果で長さは変わります。台詞を直したら新しいWAVを測り直します。
3. 長いシーンを整数フレームで延長する
今回の編集ルールは、音声の前に0.35秒、後に少なくとも0.65秒の余白を取り、元シーンが短ければ最後のフレームを保持することです。音声を速めたり切ったりしません。30fpsでは次の式になります。
import math
original_seconds = 4.0
wav_seconds = 5.32
frames = math.ceil(max(original_seconds, wav_seconds + 1.0) * 30)
scene_seconds = frames / 30
print(frames, scene_seconds) # 190, 約6.333秒
最初の日本語音声は0.35秒から始まり、5.67秒で終わります。シーン終端は約6.333秒なので、音声後にも約0.663秒を残せます。「5.32秒の声を4秒の枠へ無理に入れる」処理ではありません。
7秒のシーンに4.80秒の声を置く場合は、元の7秒を維持します。短い声だからといって、画面を見る時間まで削る必要はありません。前のシーンが伸びると、後続の音声開始位置も累積して変わります。固定の英語開始時刻を使い回さず、言語ごとに時間表を保存してください。
静止画の製品デモには使いやすい方法ですが、口元、カーソル操作、速い動きまで自然に延長できるとは限りません。その場合は該当シーンを編集し直します。固定30秒の配信枠なら、台詞を短くして音声を作り直すなど、別の判断が必要です。
4. 同梱プロジェクトを再構成する
原文の「再現用プロジェクト」を展開し、そのフォルダーで実行します。Python 3、FFmpeg、ffprobeが必要です。Pythonスクリプトが利用するのは標準ライブラリです。コマンドはインストール確認であり、FFmpeg自体をインストールするものではありません。
python3 --version
ffmpeg -version
ffprobe -version
python3 assemble_localized.py ja source.mp4 rebuilt-ja
jaは保存済み日本語WAVのフォルダー、source.mp4は同梱の30秒動画、rebuilt-jaは新しい出力先です。スクリプトは既存の出力ディレクトリを拒否するので、再実行では別の名前を使ってください。
各言語フォルダーにはline-1.wavからline-5.wavまでの音声があります。韓国語ならjaをkoに、出力先をrebuilt-koに変更します。入力フォルダーだけ変えず、日本語音声を韓国語版として配布しないようにしてください。
出力はnarrated.mp4、narration.wav、timing.json、probe.jsonです。時間表には元の区間、新しいシーン長、音声開始・終了、追加した静止時間、WAVのハッシュが残ります。保存済みの台本はnarration-drafts.jsonで確認できます。
本記事の準備時にも、同じ保存済み素材から日本語版をローカルで再構成しました。生成された動画は約33.97秒で、ストリーム検査と全体のデコードを通過しています。これはローカル組み立ての結果です。新しいGemini呼び出しの成功率や、人間による音声評価を証明するものではありません。
5. 語尾・画面・ファイルを別々に検証する
ffprobe -v error -show_streams -show_format -of json rebuilt-ja/narrated.mp4
ffmpeg -v error -i rebuilt-ja/narrated.mp4 -f null -
映像はH.264、音声はAACになり、長さが計画したタイムラインと対応しているか確認します。FFmpegがエラーを出さず全体をデコードできるのは技術検査です。発音の正しさ、聞きやすさ、画面の分かりやすさは別の確認です。
自分の台本で作った版を配布する前には、音付きで全体を聴き、「API」、製品名、最終文の語尾、次の画面に進む位置を確認してください。文字起こしは補助にできますが、生成音声そのものの聴取を置き換えません。単語字幕を付けるなら、文単位の時間表から単語時刻を推測せず、別の書き起こしとアラインメント工程を用意します。
| 問題 | 対応 |
|---|---|
| 元動画が30秒ではない | この参照スクリプトの入力条件を確認し、区切りと編集計画を変更する |
| WAVの形式が違う | サンプルレート、チャンネル数、PCM形式を確認する。声の再生成が必要とは限らない |
| 語尾が切れる | WAV全体の長さ、余白、後続シーンの開始位置を再計算する |
| 台本を直したのに古い声が流れる | 対応WAVとハッシュを確認し、その言語の全体を組み直す |
| 画面は日本語化されていない | ナレーションのみの翻訳と明記し、必要なら実際の現地語画面を別に取得する |
新しいTTS生成は別工程で、利用権限、現在のモデル設定、課金条件を確認してから実行します。タイムアウトを「送信されていない」と判断して自動再送しないでください。編集で失敗しただけなら、保存済み音声を再利用してローカル入力を直す方が、問題を切り分けやすくなります。
最後に、承認済み台本、対応WAV、言語別時間表、最終MP4を一組で保管します。繰り返し使えるのはこの対応関係であり、測定前の音声をすべて同じタイムラインへ置くことではありません。
原文と音声・動画・再現用プロジェクト:Gemini TTSの多言語ナレーション実例。原文での生成記録と本稿のローカル再構成を区別しています。費用やモデル間の優劣は比較していません。