remotion_docker_devcontainer / voicevox-remotion-template /
@Syuji Syuji authored 12 days ago
..
public 生成物を管理対象から除外 14 days ago
scripts 発音差し替え用のreadAsを追加 13 days ago
src VOICEVOXマニフェストを更新 12 days ago
.eslintrc.cjs 音声同期口パク機能を実装 15 days ago
AGENTS.MD 口パクデータ生成コマンドの引数なし実行時にはヘルプを表示、allで全件生成を行う機能を追加 13 days ago
README.md 字幕改行の使い方を追記 12 days ago
package-lock.json Remotion依存関係を更新 16 days ago
package.json 口パクデータ生成コマンドの引数なし実行時にはヘルプを表示、allで全件生成を行う機能を追加 13 days ago
tsconfig.json Remotionプロジェクト設定を追加する 23 days ago
README.md

Remotion x VOICEVOX ゆっくり解説テンプレート

Remotion と VOICEVOX を組み合わせて、複数キャラクターが時系列で登場・発話する動画テンプレートです。 サンプルテーマは「ネコミミはなぜかわいいのか?」です。

使い方

1. 依存関係をインストール

npm install

2. VOICEVOX エンジンを起動

VOICEVOX のエンジンを起動してください。既定では http://host.docker.internal:50021 を参照します。 詳細は公式リポジトリを参照してください。

https://github.com/VOICEVOX/voicevox_engine

3. 脚本を編集

src/data/yukkuri-composition/script.tscharacterstimeline を編集します。

show("sayo", {caption: "ネコミミ代表として、小夜が登場!"});
say("sayo-001", "sayo", "小夜です。ネコミミ代表として、耳のかわいさを証明しに来ました。");
say("zunda-005", "zundamon", "それじゃあ、また次回なのだ!");
  • characters: 表示名、VOICEVOX の speakerName / styleName、立ち絵設定を定義します。
  • initialVisibleCharacters: 動画開始時から表示するキャラクターを定義します。
  • show(...): キャラクターを画面に登場させ、任意の説明字幕を出します。
  • say(...): キャラクターに読み上げさせ、字幕と音声を同期します。
  • 行ごとにスタイルを変える場合は say(..., {voicevox: {styleName: "スタイル名"}}) を使います。

4. 音声を生成

npm run voice:generate

対象一覧と使い方を表示します。このコマンドだけでは音声ファイルを生成しません。 音声が未生成の行は、プレビュー時にテキスト長から尺を推定します。

全コンポジションの音声をまとめて生成する場合は、次を実行します。

npm run voice:generate:all

YukkuriComposition の音声だけを明示して生成する場合は、次も使えます。

npm run voice:generate:yukkuri-composition

src/data/yukkuri-composition/script.tssay(...) から public/audio/yukkuri-composition/lines/*.wav を生成し、 src/data/yukkuri-composition/voicevox-manifest.json に長さ・話者・スタイル情報を記録します。

ピザ窯サンプルの音声を生成する場合は、次を実行します。

npm run voice:generate:pizza-kiln

PizzaOvenProject01 の音声を生成する場合は、次を実行します。

npm run voice:generate:pizza-oven-project-01

5. 口パク指示データを生成

Rhubarb Lip Sync CLI を使い、VOICEVOX 音声から口形タイムラインを生成します。

npm run lipsync:generate

対象一覧と使い方を表示します。このコマンドだけでは口パクデータを生成しません。

生成物は、Rhubarb の生 JSON が public/lipsync/raw/*.rhubarb.json、 Remotion 用に正規化した JSON が src/generated/lipsync/*.mouth.json、 プレビュー時に同期 import する集約 manifest が src/generated/lipsync/manifest.json です。

全コンポジションの口パクデータをまとめて生成する場合は、次を実行します。

npm run lipsync:generate:all

単体音声だけ再生成する場合は、次のように音声ファイルを指定できます。

npm run lipsync:generate -- public/audio/yukkuri-composition/lines/zunda-001.wav

時系列シナリオ単位で再生成する場合は、対応する VOICEVOX manifest を指定します。

npm run lipsync:generate -- --source-manifest src/data/yukkuri-composition/voicevox-manifest.json

コンポジション別の専用コマンドもあります。

npm run lipsync:generate:yukkuri-composition
npm run lipsync:generate:pizza-kiln
npm run lipsync:generate:pizza-oven-project-01

処理順は 1. npm run voice:generate:all または対象別の voice:generate:*2. npm run lipsync:generate:all または対象別の lipsync:generate:*3. npm run start です。音声を作り直したら、口パク指示データも再生成してください。

Rhubarb CLI は次の順で検出します。

  • RHUBARB_BIN に指定された実行ファイル
  • node_modules/.bin/rhubarb
  • tools/rhubarb/ または vendor/rhubarb/ 配下の実行ファイル
  • PATH 上の rhubarb

Windows / Linux / macOS で実行ファイル名が異なることがあります。 Dev Container で使う場合は Linux 版 Rhubarb を配置し、必要なら RHUBARB_BIN=/usr/local/bin/rhubarb のように指定してください。

日本語音声では Rhubarb の phonetic recognizer を使います。音声のみからの推定なので、 日本語の母音完全一致ではなく、動画用に自然に見える口パクを目的にしています。 Rhubarb 口形は次のように丸めます。

{
  X: "rest",
  A: "closed",
  B: "i",
  C: "e",
  D: "a",
  E: "o",
  F: "u",
  G: "i",
  H: "e",
}

6. プレビュー

npm run start

7. レンダリング

npm run render YukkuriZundamon out/video.mp4

編集ポイント

  • 時系列脚本: src/data/yukkuri-composition/script.ts
  • 音声タイミング: src/data/yukkuri-composition/voicevox-manifest.json (自動生成)
  • 口パクタイミング: src/generated/lipsync/manifest.json (自動生成)
  • 映像の構成: src/yukkuri-composition/index.tsx
  • 立ち絵セット: src/standee-sets.ts

字幕方針

本テンプレートは、短編 VOICEVOX ドラマ動画や、実写映像を背景にした解説動画での利用を主用途としています。

そのため、字幕は @remotion/captionsCaption 型 JSON による単語単位・時刻単位の正式な字幕データとしては扱わず、src/data/yukkuri-composition/script.tssay(...) / show(...) に紐づく発話単位・シーン単位の表示テキストとして扱います。

say(...) の字幕は VOICEVOX で生成した音声尺、または未生成時の推定尺に同期して表示します。SRT/VTT 互換、単語単位ハイライト、自動文字起こし字幕が必要になった場合は、その時点で @remotion/captions の導入を検討します。

字幕の任意位置で改行したい場合は、say(...)subtitle の文字列内に \n を入れます。\n は字幕表示だけの改行として扱い、VOICEVOX 読み上げ用テキストからは取り除かれます。

VOICEVOX設定

  • VOICEVOX_URL (既定: http://host.docker.internal:50021)
  • 話者とスタイルは各コンポジションの src/data/{コンポジション名}/script.tscharacters.*.voicevox で指定します。

立ち絵セットの追加・差し替え

立ち絵本体、口パク画像、通常表示時の基本レイアウトは src/standee-sets.tsstandeeSets にまとめています。 新しい立ち絵を追加する場合は、次の流れで修正します。

1. 画像素材を配置

public/image/ 配下に、立ち絵本体と口パク画像を配置します。imagePathmouthImageDir には public からの相対パスを指定します。

public/image/zundamon_ohnegus_ai_base.png
public/image/zundamon-ohnegus-ai-rhubarb-mouths/
  a.png
  i.png
  u.png
  e.png
  o.png
  closed.png
  rest.png

口パク画像は、立ち絵本体と同じキャンバス寸法・同じ位置合わせにしてください。 口だけが差分として重なる前提で、LipSyncedStandeeImage が本体画像の上に同じサイズで重ねます。

2. src/standee-sets.ts にセットを追加

standeeSets に、素材パスと基本レイアウトを追加します。

"zundamon_ohnegus_ai": {
  kind: "zundamon",
  imagePath: "image/zundamon_ohnegus_ai_base.png",
  mouthImageDir: "image/zundamon-ohnegus-ai-rhubarb-mouths",
  imageLayout: {
    width: 540,
    maxHeight: 730,
    translateY: 0,
    flipX: true,
  },
},
  • kind: キャラクター種別です。既存は "zundamon" / "sayo" です。
  • imagePath: 立ち絵本体のパスです。
  • mouthImageDir: a.png などの口パク画像を置いたディレクトリです。
  • imageLayout.width: 通常コンポジションでの表示幅です。
  • imageLayout.maxHeight: 通常コンポジションでの最大表示高さです。
  • imageLayout.translateY: 通常コンポジションで上下位置を微調整します。
  • imageLayout.flipX: 左右反転したい場合に true にします。

3. キャラクター定義で使うセットを選ぶ

YukkuriComposition は src/data/yukkuri-composition/script.ts、ピザ窯コンポジションは src/data/pizza-kiln/script.tscharacters.*.avatar で、使いたい立ち絵セットを展開します。 対象ファイルで getStandeeSet を import して使います。

avatar: {
  ...getStandeeSet("sayo_ohnegus_ai"),
  accentColor: "#6b5f83",
  nameplatePosition: "none",
  idleAnimationType: "none",
  speakingAnimationType: "rhubarbLipSync",
},

speakingAnimationType: "rhubarbLipSync" を指定すると、src/generated/lipsync/manifest.json の口形タイムラインに合わせて mouthImageDir の画像を切り替えます。

新しい時系列コンポジションでは、VQChronologicalScenarioassetWorkflow に VOICEVOX 音声生成と Rhubarb 口パク生成のパスをセットで定義します。口形はコンポジションに 固定値を書かず、speech.id をキーに src/generated/lipsync/manifest.json から参照します。

4. コンポジション固有の見栄えを調整

通常の全身表示は src/standee-sets.tsimageLayout で調整します。 コンポジションごとに特別な配置がある場合だけ、描画側を調整します。

  • YukkuriComposition: src/yukkuri-composition/index.tsx
  • ピザ窯コンポジション: src/pizza-kiln-composition.tsxSayoStandee

ピザ窯コンポジションは、通常背景用の STAGE_STANDEE_* と、実写動画右下用の CORNER_* でサイズと切り抜き方を分けています。

5. 確認

変更後は、可能な範囲で次を実行します。

./node_modules/.bin/tsc --noEmit
npm run lint
./node_modules/.bin/remotion still src/index.ts YukkuriZundamon /tmp/yukkuri.png --frame=160
./node_modules/.bin/remotion still src/index.ts PizzaKilnSayo /tmp/pizza-kiln.png --frame=30

音声や say(...) を変更した場合は、対象別の voice:generate:* または npm run voice:generate:all と、 対象別の lipsync:generate:* または npm run lipsync:generate:all も実行してください。 立ち絵画像だけを差し替える場合は、口パクタイミングの再生成は不要です。

以前の public/audio/zundamon.txtsrc/data/script.json は互換用に残していますが、現在は参照しません。