Remotion と VOICEVOX を組み合わせて、複数キャラクターが時系列で登場・発話する動画テンプレートです。 サンプルテーマは「ネコミミはなぜかわいいのか?」です。
npm install
VOICEVOX のエンジンを起動してください。既定では http://host.docker.internal:50021 を参照します。 詳細は公式リポジトリを参照してください。
https://github.com/VOICEVOX/voicevox_engine
src/data/yukkuri-composition/script.ts の characters と timeline を編集します。
show("sayo", {caption: "ネコミミ代表として、小夜が登場!"});
say("sayo-001", "sayo", "小夜です。ネコミミ代表として、耳のかわいさを証明しに来ました。");
say("zunda-005", "zundamon", "それじゃあ、また次回なのだ!");
characters: 表示名、VOICEVOX の speakerName / styleName、立ち絵設定を定義します。initialVisibleCharacters: 動画開始時から表示するキャラクターを定義します。show(...): キャラクターを画面に登場させ、任意の説明字幕を出します。say(...): キャラクターに読み上げさせ、字幕と音声を同期します。say(..., {voicevox: {styleName: "スタイル名"}}) を使います。npm run composition:create -- my-new-video
コンポジション名は kebab-case の slug で指定します。 上記の例では Remotion ID MyNewVideo と src/data/my-new-video/script.ts が作成されます。
作成後は、原則として src/data/my-new-video/script.ts の compositionTitle、characters、timeline を編集します。
音声と口パクを生成する場合は、作成された専用コマンドを実行します。
npm run voice:generate:my-new-video npm run lipsync:generate:my-new-video npm run start -- --webpack-poll=1000
npm run voice:generate
対象一覧と使い方を表示します。このコマンドだけでは音声ファイルを生成しません。 音声が未生成の行は、プレビュー時にテキスト長から尺を推定します。
全コンポジションの音声をまとめて生成する場合は、次を実行します。
npm run voice:generate:all
YukkuriComposition の音声だけを明示して生成する場合は、次も使えます。
npm run voice:generate:yukkuri-composition
src/data/yukkuri-composition/script.ts の say(...) から 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
ずんだもん別バージョン立ち絵デモの音声を生成する場合は、次を実行します。
npm run voice:generate:zundamon-rework-standee-demo
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 です。 VOICEVOX query 方式のコンポジションでは、VOICEVOX の audio_query に含まれるモーラ長から 直接 src/generated/lipsync/*.mouth.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 npm run lipsync:generate:zundamon-rework-standee-demo
処理順は 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/rhubarbtools/rhubarb/ または vendor/rhubarb/ 配下の実行ファイルrhubarbWindows / 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",
}
zundamon-rework-standee-demo は、Rhubarb ではなく VOICEVOX query 方式を使います。 この方式では VOICEVOX の audio_query に含まれる a / i / u / e / o などの 母音とモーラ長から口形タイムラインを作るため、日本語母音の確認デモに向いています。 既存コンポジションは従来どおり Rhubarb 方式です。
npm run start
Dev Container / Windows 共有フォルダなどでファイル変更検知が不安定な場合は、 Remotion の polling オプションを npm script 経由で渡します。
npm run start -- --webpack-poll=1000
npm run render YukkuriZundamon out/video.mp4
src/data/yukkuri-composition/script.tssrc/data/yukkuri-composition/voicevox-manifest.json (自動生成)src/generated/lipsync/manifest.json (自動生成)src/yukkuri-composition/index.tsxsrc/standee-sets.tsYukkuriZundamon: ずんだもんと小夜のゆっくり解説サンプルPizzaKilnSayo: 小夜のピザ窯サンプルPizzaOvenProject01: ピザ窯制作記録サンプルZundamonReworkStandeeDemo: zundamon-ohnegus-rework-baby の全表情表示と default 表情リップシンク確認デモ本テンプレートは、短編 VOICEVOX ドラマ動画や、実写映像を背景にした解説動画での利用を主用途としています。
そのため、字幕は @remotion/captions の Caption 型 JSON による単語単位・時刻単位の正式な字幕データとしては扱わず、src/data/yukkuri-composition/script.ts の say(...) / show(...) に紐づく発話単位・シーン単位の表示テキストとして扱います。
say(...) の字幕は VOICEVOX で生成した音声尺、または未生成時の推定尺に同期して表示します。SRT/VTT 互換、単語単位ハイライト、自動文字起こし字幕が必要になった場合は、その時点で @remotion/captions の導入を検討します。
字幕の任意位置で改行したい場合は、say(...) や subtitle の文字列内に \n を入れます。\n は字幕表示だけの改行として扱い、VOICEVOX 読み上げ用テキストからは取り除かれます。
VOICEVOX_URL (既定: http://host.docker.internal:50021)src/data/{コンポジション名}/script.ts の characters.*.voicevox で指定します。立ち絵本体、口パク画像、通常表示時の基本レイアウトは src/standee-sets.ts の standeeSets にまとめています。 新しい立ち絵を追加する場合は、次の流れで修正します。
public/image/ 配下に、立ち絵本体と口パク画像を配置します。imagePath や mouthImageDir には 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 が本体画像の上に同じサイズで重ねます。
素材ファイル名は、環境差による参照トラブルを避けるため、英数字・ハイフン・アンダースコア・ドットを基本にします。 表情や口形の意味は、ファイル名・ディレクトリ名・必要に応じた管理用データのどれかで追えるようにしてください。
現在の実装では、1 つの立ち絵ベースに対して mouthImageDir の中に次の 7 種類の口形画像を置く構成を標準とします。
a.png i.png u.png e.png o.png closed.png rest.png
Rhubarb が出力する X / A / B / C / D / E / F / G / H は、 src/lipsync/rhubarb-map.ts と scripts/lipsync-utils.js で上記の抽象口形へ変換します。 この変換は「音声解析結果を日本語向けの口形名へ丸める」ための共通ルールとして扱い、 表情差分や特定素材のファイル名を直接管理する場所にはしません。
表情ベースが複数ある素材では、表情ごとに同じ抽象口形をそろえるのを基本方針とします。 たとえば default、blink、confident のような表情がある場合は、それぞれの表情に a / i / u / e / o / closed / rest 相当の画像を持たせます。 表情側に存在しない口形がある場合は、rest または近い閉口画像へフォールバックできるようにします。
フル画像として表情と口形が一体化している素材は、既存の「本体画像 + 口だけ差分」方式へ無理に分解せず、 表情 ID と口形 ID の組み合わせで画像を選ぶ素材として扱います。 現時点では mouthImageDir/{mouth}.png の単一表情ベースが実装済みの安定形です。
将来的には、複数表情ベースのアバターを次のような形で扱えるように拡張する方針です。
expressions: {
default: {
rest: "image/example/default-rest.png",
closed: "image/example/default-closed.png",
a: "image/example/default-a.png",
i: "image/example/default-i.png",
u: "image/example/default-u.png",
e: "image/example/default-e.png",
o: "image/example/default-o.png",
},
confident: {
rest: "image/example/confident-rest.png",
closed: "image/example/confident-closed.png",
a: "image/example/confident-a.png",
i: "image/example/confident-i.png",
u: "image/example/confident-u.png",
e: "image/example/confident-e.png",
o: "image/example/confident-o.png",
},
}
この形では、口パクタイムラインは従来どおり Rhubarb 由来の抽象口形を返し、 脚本や演出側が現在の表情 ID を選び、描画側が expressions[expressionId][mouth] を参照します。 これにより、音声解析、表情演出、素材配置を分離して管理できます。
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 にします。YukkuriComposition は src/data/yukkuri-composition/script.ts、ピザ窯コンポジションは src/data/pizza-kiln/script.ts の characters.*.avatar で、使いたい立ち絵セットを展開します。 対象ファイルで getStandeeSet を import して使います。
avatar: {
...getStandeeSet("sayo_ohnegus_ai"),
accentColor: "#6b5f83",
nameplatePosition: "none",
idleAnimationType: "none",
speakingAnimationType: "rhubarbLipSync",
},
speakingAnimationType: "rhubarbLipSync" を指定すると、src/generated/lipsync/manifest.json の口形タイムラインに合わせて mouthImageDir の画像を切り替えます。
新しい時系列コンポジションでは、VQChronologicalScenario の assetWorkflow に VOICEVOX 音声生成と口パク生成のパスをセットで定義します。口形はコンポジションに 固定値を書かず、speech.id をキーに src/generated/lipsync/manifest.json から参照します。
通常の全身表示は src/standee-sets.ts の imageLayout で調整します。 コンポジションごとに特別な配置がある場合だけ、描画側を調整します。
src/yukkuri-composition/index.tsxsrc/pizza-kiln-composition.tsx の SayoStandeeピザ窯コンポジションは、通常背景用の STAGE_STANDEE_* と、実写動画右下用の CORNER_* でサイズと切り抜き方を分けています。
変更後は、可能な範囲で次を実行します。
./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.txt と src/data/script.json は互換用に残していますが、現在は参照しません。