diff --git a/voicevox-remotion-template/README.md b/voicevox-remotion-template/README.md index b2ed6d1..da4fb97 100644 --- a/voicevox-remotion-template/README.md +++ b/voicevox-remotion-template/README.md @@ -40,8 +40,9 @@ 上記の例では Remotion ID `MyNewVideo` と `src/data/my-new-video/script.ts` が作成されます。 -作成後は、原則として `src/data/my-new-video/script.ts` の -`compositionTitle`、`characters`、`timeline` を編集します。 +作成後は、`src/data/my-new-video/script.ts` の +`compositionTitle` と `timeline`、必要に応じて +`src/data/my-new-video/characters.ts` のキャラクター定義を編集します。 音声と口パクを生成する場合は、作成された専用コマンドを実行します。 diff --git a/voicevox-remotion-template/package.json b/voicevox-remotion-template/package.json index 4be982a..f6bb2d4 100644 --- a/voicevox-remotion-template/package.json +++ b/voicevox-remotion-template/package.json @@ -20,7 +20,9 @@ "voice:generate:yukkuri-composition": "node scripts/voicevox-generate.js --project yukkuri-composition", "voice:generate:pizza-kiln": "node scripts/voicevox-generate.js --project pizza-kiln", "voice:generate:pizza-oven-project-01": "node scripts/voicevox-generate.js --project pizza-oven-project-01", - "voice:generate:zundamon-rework-standee-demo": "node scripts/voicevox-generate.js --project zundamon-rework-standee-demo" + "voice:generate:zundamon-rework-standee-demo": "node scripts/voicevox-generate.js --project zundamon-rework-standee-demo", + "voice:generate:zundamon-jiron": "node scripts/voicevox-generate.js --project zundamon-jiron", + "lipsync:generate:zundamon-jiron": "node scripts/generate-lipsync.js --project zundamon-jiron" }, "dependencies": { "@remotion/google-fonts": "4.0.460", diff --git a/voicevox-remotion-template/public/audio/common_bgm/saturn-3-music-rockabye-baby-music-box-lullaby-loopable-527146.mp3 b/voicevox-remotion-template/public/audio/common_bgm/saturn-3-music-rockabye-baby-music-box-lullaby-loopable-527146.mp3 new file mode 100644 index 0000000..e0ce300 --- /dev/null +++ b/voicevox-remotion-template/public/audio/common_bgm/saturn-3-music-rockabye-baby-music-box-lullaby-loopable-527146.mp3 Binary files differ diff --git a/voicevox-remotion-template/public/audio/common_bgm/saturn-3-music-rockabye-baby-music-box-lullaby-loopable-527146.mp3.png b/voicevox-remotion-template/public/audio/common_bgm/saturn-3-music-rockabye-baby-music-box-lullaby-loopable-527146.mp3.png new file mode 100644 index 0000000..14db55c --- /dev/null +++ b/voicevox-remotion-template/public/audio/common_bgm/saturn-3-music-rockabye-baby-music-box-lullaby-loopable-527146.mp3.png Binary files differ diff --git a/voicevox-remotion-template/public/audio/common_bgm/saturn-3-music-rockabye-baby-music-box-lullaby-loopable-527146.mp3.text b/voicevox-remotion-template/public/audio/common_bgm/saturn-3-music-rockabye-baby-music-box-lullaby-loopable-527146.mp3.text new file mode 100644 index 0000000..36e2b9c --- /dev/null +++ b/voicevox-remotion-template/public/audio/common_bgm/saturn-3-music-rockabye-baby-music-box-lullaby-loopable-527146.mp3.text @@ -0,0 +1,35 @@ +音源ライセンス・使用条件メモ + +対象ファイル: +saturn-3-music-rockabye-baby-music-box-lullaby-loopable-527146.mp3 + +出典: +Pixabay +https://pixabay.com/ja/music/%E5%AD%90%E5%AE%88%E5%94%84-rockabye-baby-music-box-lullaby-loopable-527146/ + +作品情報: +- タイトル: Rockabye Baby - Music Box Lullaby - Loopable +- 作者: Saturn-3-Music +- メディアタイプ: MP3 +- 長さ: 1:26 +- 公開日: May 19, 2026 +- Pixabayページ上の説明: オルゴール、子守唄、ベル音楽。無料で使用可能。 + +ライセンス: +- Pixabay Content License +- ライセンス概要: https://pixabay.com/service/license-summary/ +- 利用規約: https://pixabay.com/service/terms/ +- 確認日: 2026-05-28 +- 利用規約の最終更新日: November 18, 2024 + +使用条件メモ: +- Pixabay Content License の範囲で、無料で使用可能。 +- 商用・非商用の作品内で使用、コピー、変更、適応が可能。 +- 作者または Pixabay へのクレジット表記は必須ではないが、推奨されている。 +- クレジットする場合の例: by Saturn-3-Music via Pixabay +- 音源単体、または実質的に音源単体のまま販売・配布することは禁止。 +- 誤認を招く使い方、違法・不道徳な使い方は禁止。 +- 商標、ロゴ、ブランド、第三者の権利など追加の権利確認が必要になる場合があるため、公開・商用利用時は最新のPixabay利用規約を確認する。 + +このリポジトリでの想定用途: +- Remotion動画のBGMとして、映像・音声・字幕などを組み合わせた新しい制作物の一部として使用する。 diff --git a/voicevox-remotion-template/public/audio/common_bgm/saturn-3-music-rockabye-baby-music-box-lullaby-loopable-527146.pixabay.png b/voicevox-remotion-template/public/audio/common_bgm/saturn-3-music-rockabye-baby-music-box-lullaby-loopable-527146.pixabay.png new file mode 100644 index 0000000..849753c --- /dev/null +++ b/voicevox-remotion-template/public/audio/common_bgm/saturn-3-music-rockabye-baby-music-box-lullaby-loopable-527146.pixabay.png Binary files differ diff --git a/voicevox-remotion-template/public/audio/common_soundeffect/Door 6_5 Open.wav b/voicevox-remotion-template/public/audio/common_soundeffect/Door 6_5 Open.wav new file mode 100644 index 0000000..bf508a6 --- /dev/null +++ b/voicevox-remotion-template/public/audio/common_soundeffect/Door 6_5 Open.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/common_soundeffect/Door 6_5 Open.wav.text b/voicevox-remotion-template/public/audio/common_soundeffect/Door 6_5 Open.wav.text new file mode 100644 index 0000000..3c90e02 --- /dev/null +++ b/voicevox-remotion-template/public/audio/common_soundeffect/Door 6_5 Open.wav.text @@ -0,0 +1,33 @@ +音源ライセンス・使用条件メモ + +対象ファイル: +Door 6_5 Open.wav + +出典: +Unity Asset Store +https://assetstore.unity.com/packages/audio/sound-fx/rpg-dungeon-sounds-173550 + +アセット情報: +- アセット名: RPGとダンジョンサウンド +- Publisher: Cafofo +- ファイル名: Door 6_5 Open.wav +- 現在の状態: Unity Asset Store での取り扱いは終了している。 + +ライセンス: +- 標準 Unity Asset Store EULA +- Unity Asset Store Terms of Service and EULA: + https://unity.com/legal/as-terms +- 確認日: 2026-05-28 +- Unity Asset Store Terms of Service and EULA の最終更新日: December 4, 2024 + +使用条件メモ: +- Unity Asset Store EULA では、購入・取得したアセットは販売ではなくライセンス供与として扱われる。 +- Non-Restricted Asset の場合、EULA の制限に従い、アセットを実質的な独自コンテンツを含む電子アプリケーションまたはデジタルメディアへ組み込み、埋め込みコンポーネントとして利用できる。 +- 組み込み済みの Licensed Product の一部として、再生・表示・配信・収益化できる。 +- アセット単体、またはアセットが主要部分となる形での再配布、販売、貸与、サブライセンス、商用化は禁止。 +- アセット購入費用の共有や、購入者以外へ利用権を分配する行為は禁止。 +- Unity Asset Store またはアセットを、Provider および Unity の明示的同意なく AI / 機械学習モデルの学習、データセット作成、抽出、スクレイピング等に使用することは禁止。 +- Restricted Asset や別途 Provider EULA が付属する場合は、その条件が優先される可能性がある。公開・商用利用時は取得時のライセンス情報と最新の Unity EULA を確認する。 + +このリポジトリでの想定用途: +- Remotion動画内の効果音として、映像・音声・字幕などを組み合わせた制作物の一部として使用する。 diff --git a/voicevox-remotion-template/public/audio/common_soundeffect/nc174220_osikko7.mp3 b/voicevox-remotion-template/public/audio/common_soundeffect/nc174220_osikko7.mp3 new file mode 100644 index 0000000..8fd16b0 --- /dev/null +++ b/voicevox-remotion-template/public/audio/common_soundeffect/nc174220_osikko7.mp3 Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-001.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-001.wav new file mode 100644 index 0000000..989f0f2 --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-001.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-002.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-002.wav new file mode 100644 index 0000000..b2d0590 --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-002.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-003.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-003.wav new file mode 100644 index 0000000..48e153e --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-003.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-004.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-004.wav new file mode 100644 index 0000000..ec1ec00 --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-004.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-005.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-005.wav new file mode 100644 index 0000000..99d3311 --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-005.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-006.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-006.wav new file mode 100644 index 0000000..ec9f45a --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-006.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-007.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-007.wav new file mode 100644 index 0000000..4e2b8b6 --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-007.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-008.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-008.wav new file mode 100644 index 0000000..92e6887 --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-sayo-008.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-001.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-001.wav new file mode 100644 index 0000000..b251216 --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-001.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-002.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-002.wav new file mode 100644 index 0000000..d052da2 --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-002.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-003.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-003.wav new file mode 100644 index 0000000..4376ea6 --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-003.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-004.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-004.wav new file mode 100644 index 0000000..5194731 --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-004.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-005.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-005.wav new file mode 100644 index 0000000..3c3fa7b --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-005.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-006.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-006.wav new file mode 100644 index 0000000..8d7e160 --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-006.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-007.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-007.wav new file mode 100644 index 0000000..e41a63b --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-007.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-008.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-008.wav new file mode 100644 index 0000000..ce3290b --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-008.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-009.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-009.wav new file mode 100644 index 0000000..380df03 --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-009.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-010.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-010.wav new file mode 100644 index 0000000..7177a3a --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-010.wav Binary files differ diff --git a/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-011.wav b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-011.wav new file mode 100644 index 0000000..e0150c4 --- /dev/null +++ b/voicevox-remotion-template/public/audio/zundamon-jiron/lines/zundamon-jiron-zunda-011.wav Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/_inspection/back_hair.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/_inspection/back_hair.png new file mode 100644 index 0000000..ad17dda --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/_inspection/back_hair.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/_inspection/ears.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/_inspection/ears.png new file mode 100644 index 0000000..4187582 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/_inspection/ears.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/_inspection/eyes.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/_inspection/eyes.png new file mode 100644 index 0000000..bec4166 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/_inspection/eyes.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/_inspection/mouths.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/_inspection/mouths.png new file mode 100644 index 0000000..7bcc957 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/_inspection/mouths.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/license.txt b/voicevox-remotion-template/public/image/sayo_by_sayonaka/license.txt new file mode 100644 index 0000000..b64732d --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/license.txt @@ -0,0 +1,49 @@ +小夜/SAYO立ち絵素材② ライセンス・使用許諾メモ + +確認日: 2026-05-28 + +入手元: +https://sayo-official.booth.pm/items/4586976 + +公式ガイドライン: +https://316soramegu.wixsite.com/sayo-official/guideline + +素材情報: +- BOOTH 商品名: 小夜/SAYO立ち絵素材② +- 配布元: 小夜/SAYO公式ショップ +- 素材制作者: さよなか +- ファイル名: SAYOFont_v1.2.zip + +使用許諾の要約: +- 小夜/SAYOの公式立ち絵素材。 +- PSDToolでの使用が想定されている。 +- 映像制作を中心に様々な用途で利用できる。 +- 商用利用は、動画・配信への利用、およびそれに付随するサムネイル等に限り可能。 + BOOTH 商品ページでは、スーパーチャット・広告収入などが例示されている。 +- その他の用途で商用利用する場合は、別途問い合わせが必要。 +- 加筆・加工は可能。ただし、小夜/SAYOだと分からない改変や、 + 小夜/SAYOではないキャラクターへの改変は禁止。 +- ニコニコ動画へ投稿する場合は、親作品登録・コンテンツツリー登録が推奨されている。 + BOOTH 商品ページ記載の親作品: + https://seiga.nicovideo.jp/seiga/im10824761 + +禁止事項の要約: +- 誹謗中傷を内容に含むものへの利用。 +- 第三者の権利等を侵害するものへの利用。 +- イメージを著しく損なう内容、公序良俗に著しく反する内容を含むものへの利用。 +- 犯罪の助長を目的としたものへの利用。 +- 衣装を脱がせた状態での利用。 + ただし、素体は衣装差分を作成したい場合のみ利用可能。 +- 素材の二次配布。 +- 著作情報を偽ること。 +- その他、「小夜/SAYO公式素材」利用規約に違反する内容、 + または小夜/SAYO運営が不適切と判断する行為への利用。 + +免責・注意: +- 「小夜/SAYO公式素材」を利用した作品において損害が発生しても、 + 小夜/SAYO運営は責任を負わないとされている。 +- 配布データ内の readme も確認すること。 +- 規約は小夜/SAYO運営の判断により、通知なく変更される場合がある。 +- このファイルは確認時点の公開情報を元にした作業用メモであり、 + 正式な利用可否は必ず入手元ページ、公式ガイドライン、 + 配布データ内 readme の最新内容で確認すること。 diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/mouth_lipsync_index.csv b/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/mouth_lipsync_index.csv new file mode 100644 index 0000000..0066357 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/mouth_lipsync_index.csv @@ -0,0 +1,6 @@ +filename,vowel,layer,memo +sayo_mouth_a.png,あ / a,▽,開いた口。 +sayo_mouth_i.png,い / i,イー,横長の口。 +sayo_mouth_u.png,う / u,ちゅ,すぼめた口。 +sayo_mouth_e.png,え / e,半開き,軽く開いた口。 +sayo_mouth_o.png,お / o,O,丸い大きめの口。 diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/sayo_mouth_a.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/sayo_mouth_a.png new file mode 100644 index 0000000..581c6e9 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/sayo_mouth_a.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/sayo_mouth_e.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/sayo_mouth_e.png new file mode 100644 index 0000000..3edc459 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/sayo_mouth_e.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/sayo_mouth_i.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/sayo_mouth_i.png new file mode 100644 index 0000000..89976a0 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/sayo_mouth_i.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/sayo_mouth_o.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/sayo_mouth_o.png new file mode 100644 index 0000000..ef42708 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/sayo_mouth_o.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/sayo_mouth_u.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/sayo_mouth_u.png new file mode 100644 index 0000000..a5b1a75 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync/sayo_mouth_u.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync_index.md b/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync_index.md new file mode 100644 index 0000000..2a54fc2 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/mouth_lipsync_index.md @@ -0,0 +1,12 @@ +# リップシンク用 口形対応表 + +- すべて背景透明PNGです。 +- 2000x3000pxの元キャンバス位置を維持しているため、立ち絵にそのまま重ねられます。 + +| ファイル名 | 母音 | 元レイヤー | 用途メモ | +|---|---|---|---| +| sayo_mouth_a.png | あ / a | ▽ | 開いた口。 | +| sayo_mouth_i.png | い / i | イー | 横長の口。 | +| sayo_mouth_u.png | う / u | ちゅ | すぼめた口。 | +| sayo_mouth_e.png | え / e | 半開き | 軽く開いた口。 | +| sayo_mouth_o.png | お / o | O | 丸い大きめの口。 | diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/previews/mouth_lipsync_contact_sheet.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/previews/mouth_lipsync_contact_sheet.png new file mode 100644 index 0000000..eadd64e --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/previews/mouth_lipsync_contact_sheet.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/previews/variant_contact_sheet.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/previews/variant_contact_sheet.png new file mode 100644 index 0000000..88beb92 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/previews/variant_contact_sheet.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/a.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/a.png new file mode 100644 index 0000000..581c6e9 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/a.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/closed.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/closed.png new file mode 100644 index 0000000..38e2a2a --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/closed.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/e.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/e.png new file mode 100644 index 0000000..3edc459 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/e.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/i.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/i.png new file mode 100644 index 0000000..89976a0 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/i.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/o.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/o.png new file mode 100644 index 0000000..ef42708 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/o.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/rest.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/rest.png new file mode 100644 index 0000000..38e2a2a --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/rest.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/u.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/u.png new file mode 100644 index 0000000..a5b1a75 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/rhubarb-mouths/u.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variant_index.csv b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variant_index.csv new file mode 100644 index 0000000..661f5eb --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variant_index.csv @@ -0,0 +1,19 @@ +filename,description,eye,brow,mouth,ear,extras +sayo_001_neutral.png,通常顔。基本の待機・地の文用。,通常,通常,ω,通常,影 +sayo_002_neutral_talk.png,通常顔の話し中。普段のセリフ用。,通常,通常,半開き,通常,影 +sayo_003_smile.png,笑顔。相づち・軽い喜び用。,にっこり,通常,へへ,開き,影_閉じ目用 +sayo_004_smile_talk.png,明るい話し中。解説のポジティブな場面用。,通常_キラキラ,通常,▽,開き,影 / 頬 +sayo_005_closed_smile.png,目閉じ笑顔。ひと呼吸置く場面・照れ笑い用。,とじ目,通常,ω,通常,影_閉じ目用 / 頬 +sayo_006_embarrassed.png,照れ。軽い恐縮・うれしい焦り用。,右向き_うるうる,しょんぼり,v,下がり,影 / 照れ +sayo_007_shy_side.png,目そらし。言いにくい話題・小声の反応用。,視線,しょんぼり,~,下がり,影 / 照れ2 +sayo_008_surprised.png,驚き。気づき・リアクション用。,驚愕,上向き,O,開き,影 / ショック +sayo_009_panic.png,焦り・パニック。予想外の展開用。,驚愕_ハイライトなし,上向き,あゎ…,下がり,影 / 汗 / ショック +sayo_010_angry.png,怒り。ツッコミ・強めの反論用。,猫目,上向き,おこ,開き,影 / 怒り +sayo_011_annoyed.png,じと目。不満・呆れ・冷静なツッコミ用。,半目_視線,通常,への字,通常,影_半目用 +sayo_012_smug.png,得意げ。いたずらっぽい補足・小ネタ用。,半目_視線,通常,にへ,開き,影_半目用 +sayo_013_sad.png,しょんぼり。困り・落ち込み用。,上目遣い_うるうる,しょんぼり,への字,下がり,影 / 涙 +sayo_014_crying.png,大泣き。強い悲しみ・オーバーリアクション用。,><,しょんぼり,ガバッ,下がり,影_閉じ目用 / 大泣き +sayo_015_dizzy.png,混乱・目回し。情報過多・困惑用。,通常_ぐるぐる,上向き,o,通常,影 / 汗 +sayo_016_love.png,好意・ハイテンション。推し語り・かわいい反応用。,通常_ハート,通常,ちゅ,開き,影 / 照れ2 +sayo_017_left_glance.png,左向き。別対象への視線誘導用。,左向き,通常,ー,通常,影 +sayo_018_right_glance.png,右向き。別対象への視線誘導用。,右向き,通常,ー,通常,影 diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variant_index.md b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variant_index.md new file mode 100644 index 0000000..240ec8a --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variant_index.md @@ -0,0 +1,26 @@ +# 小夜ちゃん 立ち絵差分対応表 + +- PSD: `I:\tmp\20260528_19_11_sayo\SAYOFont1.2.psd` +- 条件: 帽子なし、猫耳あり、背景透明PNG、2000x3000pxの元キャンバス位置を維持 +- 書き出し方針: 動画で使いやすい基本感情・会話向け差分に絞り込み + +| ファイル名 | 差分内容 | 目 | 眉 | 口 | 耳 | 追加パーツ | +|---|---|---|---|---|---|---| +| sayo_001_neutral.png | 通常顔。基本の待機・地の文用。 | 通常 | 通常 | ω | 通常 | 影 | +| sayo_002_neutral_talk.png | 通常顔の話し中。普段のセリフ用。 | 通常 | 通常 | 半開き | 通常 | 影 | +| sayo_003_smile.png | 笑顔。相づち・軽い喜び用。 | にっこり | 通常 | へへ | 開き | 影_閉じ目用 | +| sayo_004_smile_talk.png | 明るい話し中。解説のポジティブな場面用。 | 通常_キラキラ | 通常 | ▽ | 開き | 影 / 頬 | +| sayo_005_closed_smile.png | 目閉じ笑顔。ひと呼吸置く場面・照れ笑い用。 | とじ目 | 通常 | ω | 通常 | 影_閉じ目用 / 頬 | +| sayo_006_embarrassed.png | 照れ。軽い恐縮・うれしい焦り用。 | 右向き_うるうる | しょんぼり | v | 下がり | 影 / 照れ | +| sayo_007_shy_side.png | 目そらし。言いにくい話題・小声の反応用。 | 視線 | しょんぼり | ~ | 下がり | 影 / 照れ2 | +| sayo_008_surprised.png | 驚き。気づき・リアクション用。 | 驚愕 | 上向き | O | 開き | 影 / ショック | +| sayo_009_panic.png | 焦り・パニック。予想外の展開用。 | 驚愕_ハイライトなし | 上向き | あゎ… | 下がり | 影 / 汗 / ショック | +| sayo_010_angry.png | 怒り。ツッコミ・強めの反論用。 | 猫目 | 上向き | おこ | 開き | 影 / 怒り | +| sayo_011_annoyed.png | じと目。不満・呆れ・冷静なツッコミ用。 | 半目_視線 | 通常 | への字 | 通常 | 影_半目用 | +| sayo_012_smug.png | 得意げ。いたずらっぽい補足・小ネタ用。 | 半目_視線 | 通常 | にへ | 開き | 影_半目用 | +| sayo_013_sad.png | しょんぼり。困り・落ち込み用。 | 上目遣い_うるうる | しょんぼり | への字 | 下がり | 影 / 涙 | +| sayo_014_crying.png | 大泣き。強い悲しみ・オーバーリアクション用。 | >< | しょんぼり | ガバッ | 下がり | 影_閉じ目用 / 大泣き | +| sayo_015_dizzy.png | 混乱・目回し。情報過多・困惑用。 | 通常_ぐるぐる | 上向き | o | 通常 | 影 / 汗 | +| sayo_016_love.png | 好意・ハイテンション。推し語り・かわいい反応用。 | 通常_ハート | 通常 | ちゅ | 開き | 影 / 照れ2 | +| sayo_017_left_glance.png | 左向き。別対象への視線誘導用。 | 左向き | 通常 | ー | 通常 | 影 | +| sayo_018_right_glance.png | 右向き。別対象への視線誘導用。 | 右向き | 通常 | ー | 通常 | 影 | diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_001_neutral.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_001_neutral.png new file mode 100644 index 0000000..12ccb49 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_001_neutral.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_002_neutral_talk.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_002_neutral_talk.png new file mode 100644 index 0000000..663b0f8 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_002_neutral_talk.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_003_smile.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_003_smile.png new file mode 100644 index 0000000..e095e41 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_003_smile.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_004_smile_talk.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_004_smile_talk.png new file mode 100644 index 0000000..b540196 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_004_smile_talk.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_005_closed_smile.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_005_closed_smile.png new file mode 100644 index 0000000..470a613 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_005_closed_smile.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_006_embarrassed.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_006_embarrassed.png new file mode 100644 index 0000000..364ea0b --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_006_embarrassed.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_007_shy_side.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_007_shy_side.png new file mode 100644 index 0000000..344d809 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_007_shy_side.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_008_surprised.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_008_surprised.png new file mode 100644 index 0000000..4fff403 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_008_surprised.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_009_panic.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_009_panic.png new file mode 100644 index 0000000..54989f6 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_009_panic.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_010_angry.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_010_angry.png new file mode 100644 index 0000000..286f5cf --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_010_angry.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_011_annoyed.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_011_annoyed.png new file mode 100644 index 0000000..7ea7721 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_011_annoyed.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_012_smug.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_012_smug.png new file mode 100644 index 0000000..c44be54 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_012_smug.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_013_sad.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_013_sad.png new file mode 100644 index 0000000..7dbd9e0 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_013_sad.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_014_crying.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_014_crying.png new file mode 100644 index 0000000..050f02f --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_014_crying.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_015_dizzy.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_015_dizzy.png new file mode 100644 index 0000000..44fbe3c --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_015_dizzy.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_016_love.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_016_love.png new file mode 100644 index 0000000..833b5da --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_016_love.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_017_left_glance.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_017_left_glance.png new file mode 100644 index 0000000..b32f031 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_017_left_glance.png Binary files differ diff --git a/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_018_right_glance.png b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_018_right_glance.png new file mode 100644 index 0000000..cbfcf24 --- /dev/null +++ b/voicevox-remotion-template/public/image/sayo_by_sayonaka/variants/sayo_018_right_glance.png Binary files differ diff --git a/voicevox-remotion-template/scripts/create-composition.js b/voicevox-remotion-template/scripts/create-composition.js index ebd4114..15042b6 100644 --- a/voicevox-remotion-template/scripts/create-composition.js +++ b/voicevox-remotion-template/scripts/create-composition.js @@ -141,9 +141,9 @@ } }; -// 用途: script.ts、timing.ts、voicevox-manifest.json を data ディレクトリへ作成する。 +// 用途: script.ts、characters.ts、timing.ts、voicevox-manifest.json を data ディレクトリへ作成する。 // 使用方法: 衝突チェック後に呼び出し、テンプレートから data ファイル一式を生成する。 -// オプションや引数詳細: script.ts と timing.ts は slug 等のプレースホルダーを置換する。 +// オプションや引数詳細: script.ts、characters.ts、timing.ts は slug 等のプレースホルダーを置換する。 const createDataFiles = async ({paths, replacements}) => { await copyTemplateFile({ from: path.join(templateRoot, "data/script.ts.template"), @@ -151,6 +151,11 @@ replacements, }); await copyTemplateFile({ + from: path.join(templateRoot, "data/characters.ts.template"), + to: path.join(paths.dataDir, "characters.ts"), + replacements, + }); + await copyTemplateFile({ from: path.join(templateRoot, "data/timing.ts.template"), to: path.join(paths.dataDir, "timing.ts"), replacements, @@ -294,8 +299,9 @@ 上記の例では Remotion ID \`MyNewVideo\` と \`src/data/my-new-video/script.ts\` が作成されます。 -作成後は、原則として \`src/data/my-new-video/script.ts\` の -\`compositionTitle\`、\`characters\`、\`timeline\` を編集します。 +作成後は、\`src/data/my-new-video/script.ts\` の +\`compositionTitle\` と \`timeline\`、必要に応じて +\`src/data/my-new-video/characters.ts\` のキャラクター定義を編集します。 音声と口パクを生成する場合は、作成された専用コマンドを実行します。 diff --git a/voicevox-remotion-template/scripts/generate-lipsync.js b/voicevox-remotion-template/scripts/generate-lipsync.js index 8a78509..9c3950f 100644 --- a/voicevox-remotion-template/scripts/generate-lipsync.js +++ b/voicevox-remotion-template/scripts/generate-lipsync.js @@ -29,6 +29,10 @@ sourceManifest: "src/data/zundamon-rework-standee-demo/voicevox-manifest.json", engine: "voicevox-query", }, + { + name: "zundamon-jiron", + sourceManifest: "src/data/zundamon-jiron/voicevox-manifest.json", + }, ]; const lipsyncTargetByName = new Map( diff --git a/voicevox-remotion-template/scripts/templates/dialogue-standee/component.tsx.template b/voicevox-remotion-template/scripts/templates/dialogue-standee/component.tsx.template index 21fa3b9..e186cec 100644 --- a/voicevox-remotion-template/scripts/templates/dialogue-standee/component.tsx.template +++ b/voicevox-remotion-template/scripts/templates/dialogue-standee/component.tsx.template @@ -26,7 +26,9 @@ VQCaptionOverlay, VQCharacterStage, VQSpeechOverlay, + VQTimelineAudio, VQWarmGradientBackground, + type VQAudioEvent, type VQMouthResolver, } from "./lib/VQRemotionLib"; import {getMouthForSpeechFrame} from "./lipsync/manifest"; @@ -36,7 +38,9 @@ from: number; durationInFrames: number; visibleCharacters: CharacterId[]; - focusedCharacter: CharacterId; + focusedCharacter?: CharacterId; + avatarFlipXByCharacter: Partial>; + avatarTranslateYByCharacter: Partial>; }>; const clampInterpolation = { @@ -51,15 +55,56 @@ backgroundColor: "rgba(255, 255, 255, 0.88)", } as const; +const flipXForFacingDirection = (direction: "left" | "right") => + direction === "left"; + +const characterForEvent = (event: TimelineEvent) => + "character" in event ? event.character : undefined; + +const doesEventAdvanceTimeline = (event: TimelineEvent) => + event.type !== "audio" && + event.type !== "standeeFacingDirection" && + event.type !== "standeeVerticalOffset"; + +const audioSequenceDuration = ( + scheduledAudio: ScheduledTimelineEvent & Readonly<{event: VQAudioEvent}>, + timelineEndFrame: number +) => { + const hasExplicitDuration = + scheduledAudio.event.durationFrames !== undefined || + scheduledAudio.event.durationSeconds !== undefined; + + return scheduledAudio.event.playback === "loop" && !hasExplicitDuration + ? Math.max(0, timelineEndFrame - scheduledAudio.from) + : scheduledAudio.durationInFrames; +}; + // 用途: script.ts の timeline をフレーム単位の表示スケジュールへ変換する。 // 使用方法: Composition コンポーネント内で fps を渡して呼び出す。 -// オプションや引数詳細: show と say は character を持つ前提で、表示済みキャラクターを維持する。 +// オプションや引数詳細: character を持つイベントだけ表示対象とフォーカスに反映する。 const scheduleTimeline = (fps: number): ScheduledTimelineEvent[] => { let cursor = 0; const visibleCharacters = new Set(initialVisibleCharacters); + const avatarFlipXByCharacter: Partial> = {}; + const avatarTranslateYByCharacter: Partial> = {}; + let focusedCharacter: CharacterId | undefined = initialVisibleCharacters[0]; return timeline.map((event, index) => { - visibleCharacters.add(event.character); + const eventCharacter = characterForEvent(event); + if (eventCharacter) { + visibleCharacters.add(eventCharacter); + focusedCharacter = eventCharacter; + } + + if (event.type === "standeeFacingDirection") { + avatarFlipXByCharacter[event.character] = flipXForFacingDirection( + event.direction + ); + } + + if (event.type === "standeeVerticalOffset") { + avatarTranslateYByCharacter[event.character] = event.translateY; + } const durationInFrames = durationForTimelineEvent(event, fps); const scheduledEvent = { @@ -67,11 +112,19 @@ from: cursor, durationInFrames, visibleCharacters: Array.from(visibleCharacters), - focusedCharacter: event.character, + focusedCharacter, + avatarFlipXByCharacter: {...avatarFlipXByCharacter}, + avatarTranslateYByCharacter: {...avatarTranslateYByCharacter}, }; - cursor += durationInFrames; - if (index < timeline.length - 1) { + if (doesEventAdvanceTimeline(event) && durationInFrames > 0) { + cursor += durationInFrames; + } + if ( + doesEventAdvanceTimeline(event) && + durationInFrames > 0 && + index < timeline.length - 1 + ) { cursor += __SNAKE_ID___GAP_FRAMES; } @@ -146,6 +199,10 @@ ); } + if (event.type === "audio") { + return ; + } + if (event.type !== "show") { return null; } @@ -185,17 +242,40 @@ : undefined; const speakingCharacter = activeSpeech?.character; const speakingLocalFrame = activeSpeech ? frame - activeSegment.from : 0; + const timelineEndFrame = scheduledEvents.reduce( + (endFrame, scheduledEvent) => + Math.max(endFrame, scheduledEvent.from + scheduledEvent.durationInFrames), + 0 + ); - const sequences = scheduledEvents.map((scheduledEvent, index) => ( - - - - )); + const sequences = scheduledEvents.map((scheduledEvent, index) => + ( + scheduledEvent.event.type === "audio" + ? audioSequenceDuration( + scheduledEvent as ScheduledTimelineEvent & + Readonly<{event: VQAudioEvent}>, + timelineEndFrame + ) + : scheduledEvent.durationInFrames + ) > 0 ? ( + , + timelineEndFrame + ) + : scheduledEvent.durationInFrames + } + premountFor={Math.min(fps, scheduledEvent.from)} + > + + + ) : null + ); return ( {sequences} diff --git a/voicevox-remotion-template/scripts/templates/dialogue-standee/data/characters.ts.template b/voicevox-remotion-template/scripts/templates/dialogue-standee/data/characters.ts.template new file mode 100644 index 0000000..02f138f --- /dev/null +++ b/voicevox-remotion-template/scripts/templates/dialogue-standee/data/characters.ts.template @@ -0,0 +1,61 @@ +import {getStandeeSet, type AvatarDefinition} from "../../standee-sets"; + +export type VoicevoxVoice = Readonly<{ + speakerName: string; + styleName: string; +}>; + +export type CharacterDefinition = Readonly<{ + displayName: string; + voicevox: VoicevoxVoice; + avatar: AvatarDefinition; +}>; + +export const characters = { + zundamon: { + displayName: "ずんだもん", + voicevox: { + speakerName: "ずんだもん", + styleName: "ノーマル", + }, + avatar: { + ...getStandeeSet("zundamon_ohnegus_ai"), + imageLayout: { + ...getStandeeSet("zundamon_ohnegus_ai").imageLayout, + sourceWidth: 1024, + sourceHeadWidth: 610, + targetHeadWidth: 250, + translateY: -72, + }, + accentColor: "#79d36f", + nameplatePosition: "none", + idleAnimationType: "none", + speakingAnimationType: "rhubarbLipSync", + }, + }, + sayo: { + displayName: "小夜", + voicevox: { + speakerName: "小夜/SAYO", + styleName: "ノーマル", + }, + avatar: { + ...getStandeeSet("sayo_ohnegus_ai"), + imageLayout: { + ...getStandeeSet("sayo_ohnegus_ai").imageLayout, + sourceWidth: 1024, + sourceHeadWidth: 520, + targetHeadWidth: 250, + translateY: -72, + }, + accentColor: "#6b5f83", + nameplatePosition: "none", + idleAnimationType: "none", + speakingAnimationType: "rhubarbLipSync", + }, + }, +} as const satisfies Record; + +export type CharacterId = keyof typeof characters; + +export const initialVisibleCharacters: CharacterId[] = ["zundamon"]; diff --git a/voicevox-remotion-template/scripts/templates/dialogue-standee/data/script.ts.template b/voicevox-remotion-template/scripts/templates/dialogue-standee/data/script.ts.template index 19ce32f..ef21698 100644 --- a/voicevox-remotion-template/scripts/templates/dialogue-standee/data/script.ts.template +++ b/voicevox-remotion-template/scripts/templates/dialogue-standee/data/script.ts.template @@ -1,61 +1,22 @@ -import {getStandeeSet, type AvatarDefinition} from "../../standee-sets"; import { defineVQTimeline, say, + standeeFacingDirection, + standeeVerticalOffset, type VQCustomTimelineEvent, + type VQStandeeFacingDirectionEvent, + type VQStandeeVerticalOffsetEvent, type VQSpeechEvent, type VQTimelineEvent, type VQTimelineInputEvent, } from "../../lib/VQRemotionLib/timeline"; +import type {CharacterId, VoicevoxVoice} from "./characters"; + +export {characters, initialVisibleCharacters} from "./characters"; +export type {CharacterDefinition, CharacterId, VoicevoxVoice} from "./characters"; export const compositionTitle = "__TITLE__"; -export type VoicevoxVoice = Readonly<{ - speakerName: string; - styleName: string; -}>; - -export type CharacterDefinition = Readonly<{ - displayName: string; - voicevox: VoicevoxVoice; - avatar: AvatarDefinition; -}>; - -export const characters = { - zundamon: { - displayName: "ずんだもん", - voicevox: { - speakerName: "ずんだもん", - styleName: "ノーマル", - }, - avatar: { - ...getStandeeSet("zundamon_ohnegus_ai"), - accentColor: "#79d36f", - nameplatePosition: "none", - idleAnimationType: "none", - speakingAnimationType: "rhubarbLipSync", - }, - }, - sayo: { - displayName: "小夜", - voicevox: { - speakerName: "小夜/SAYO", - styleName: "ノーマル", - }, - avatar: { - ...getStandeeSet("sayo_ohnegus_ai"), - accentColor: "#6b5f83", - nameplatePosition: "none", - idleAnimationType: "none", - speakingAnimationType: "rhubarbLipSync", - }, - }, -} as const satisfies Record; - -export type CharacterId = keyof typeof characters; - -export const initialVisibleCharacters: CharacterId[] = ["zundamon"]; - export type ShowOptions = Readonly<{ caption?: string; durationSeconds?: number; @@ -75,12 +36,16 @@ export type TimelineEvent = VQTimelineEvent< CharacterId, VoicevoxVoice, - ShowEvent + | ShowEvent + | VQStandeeFacingDirectionEvent + | VQStandeeVerticalOffsetEvent >; export type TimelineInputEvent = VQTimelineInputEvent< CharacterId, VoicevoxVoice, - ShowEvent + | ShowEvent + | VQStandeeFacingDirectionEvent + | VQStandeeVerticalOffsetEvent >; // 用途: キャラクターを画面に登場させ、任意の説明字幕を表示する。 @@ -98,9 +63,11 @@ export const timeline = defineVQTimeline([ say("__SLUG__-zunda-001", "zundamon", "みなさんこんにちは、ずんだもんなのだ!"), say("__SLUG__-zunda-002", "zundamon", "今日は新しい動画の下書きを作っていくのだ。"), + standeeVerticalOffset("__SLUG__-zunda-y-001", "zundamon", -120), show("sayo", { caption: "小夜が登場!", }), say("__SLUG__-sayo-001", "sayo", "小夜です。ここから脚本を書き換えて、動画を育てていきましょう。"), + standeeFacingDirection("__SLUG__-zunda-facing-001", "zundamon", "right"), say("__SLUG__-zunda-003", "zundamon", "音声を作ったら、口パクも忘れずに生成するのだ。"), ] satisfies readonly TimelineInputEvent[]); diff --git a/voicevox-remotion-template/scripts/templates/dialogue-standee/data/timing.ts.template b/voicevox-remotion-template/scripts/templates/dialogue-standee/data/timing.ts.template index 6f80cd3..65bda4d 100644 --- a/voicevox-remotion-template/scripts/templates/dialogue-standee/data/timing.ts.template +++ b/voicevox-remotion-template/scripts/templates/dialogue-standee/data/timing.ts.template @@ -52,10 +52,24 @@ return durationForSpeech(event, fps); } + if (event.type === "audio") { + if (event.durationFrames && Number.isFinite(event.durationFrames)) { + return Math.max(1, Math.ceil(event.durationFrames)); + } + + if (event.durationSeconds && Number.isFinite(event.durationSeconds)) { + return Math.max(1, Math.ceil(event.durationSeconds * fps)); + } + + return 0; + } + if ( event.type === "clearStill" || event.type === "clearVideo" || - event.type === "standeePosition" + event.type === "standeePosition" || + event.type === "standeeFacingDirection" || + event.type === "standeeVerticalOffset" ) { return 0; } @@ -67,6 +81,11 @@ return Math.max(1, Math.ceil(durationSeconds * fps)); }; +const doesTimelineEventAdvanceTimeline = (event: TimelineEvent) => + event.type !== "audio" && + event.type !== "standeeFacingDirection" && + event.type !== "standeeVerticalOffset"; + export const __PASCAL_ID__AssetWorkflow: VQScenarioAssetWorkflow = defineVQScenarioAssetWorkflow({ voicevox: { @@ -86,6 +105,11 @@ fps = __SNAKE_ID___FPS ) => timeline.reduce((sum, event, index) => { + const durationInFrames = durationForTimelineEvent(event, fps); + if (!doesTimelineEventAdvanceTimeline(event) || durationInFrames <= 0) { + return sum; + } + const gap = index < timeline.length - 1 ? __SNAKE_ID___GAP_FRAMES : 0; - return sum + durationForTimelineEvent(event, fps) + gap; + return sum + durationInFrames + gap; }, 0); diff --git a/voicevox-remotion-template/scripts/voicevox-generate.js b/voicevox-remotion-template/scripts/voicevox-generate.js index f6cefc8..a28f51a 100644 --- a/voicevox-remotion-template/scripts/voicevox-generate.js +++ b/voicevox-remotion-template/scripts/voicevox-generate.js @@ -39,6 +39,12 @@ manifest: "src/data/zundamon-rework-standee-demo/voicevox-manifest.json", lipsyncEngine: "voicevox-query", }, + { + name: "zundamon-jiron", + script: "src/data/zundamon-jiron/script.ts", + output: "public/audio/zundamon-jiron/lines", + manifest: "src/data/zundamon-jiron/voicevox-manifest.json", + }, ]; const voiceTargetByName = new Map( diff --git a/voicevox-remotion-template/src/data/pizza-kiln/timing.ts b/voicevox-remotion-template/src/data/pizza-kiln/timing.ts index d6ade97..d0dadfa 100644 --- a/voicevox-remotion-template/src/data/pizza-kiln/timing.ts +++ b/voicevox-remotion-template/src/data/pizza-kiln/timing.ts @@ -85,7 +85,9 @@ if ( event.type === "clearStill" || event.type === "clearVideo" || - event.type === "standeePosition" + event.type === "standeePosition" || + event.type === "standeeFacingDirection" || + event.type === "standeeVerticalOffset" ) { return 0; } diff --git a/voicevox-remotion-template/src/data/pizza-oven-project-01/timing.ts b/voicevox-remotion-template/src/data/pizza-oven-project-01/timing.ts index fc7402e..9b7e466 100644 --- a/voicevox-remotion-template/src/data/pizza-oven-project-01/timing.ts +++ b/voicevox-remotion-template/src/data/pizza-oven-project-01/timing.ts @@ -74,7 +74,9 @@ if ( event.type === "clearStill" || event.type === "clearVideo" || - event.type === "standeePosition" + event.type === "standeePosition" || + event.type === "standeeFacingDirection" || + event.type === "standeeVerticalOffset" ) { return 0; } diff --git a/voicevox-remotion-template/src/data/yukkuri-composition/timing.ts b/voicevox-remotion-template/src/data/yukkuri-composition/timing.ts index bf874b9..1aa5d5b 100644 --- a/voicevox-remotion-template/src/data/yukkuri-composition/timing.ts +++ b/voicevox-remotion-template/src/data/yukkuri-composition/timing.ts @@ -49,7 +49,9 @@ if ( event.type === "clearStill" || event.type === "clearVideo" || - event.type === "standeePosition" + event.type === "standeePosition" || + event.type === "standeeFacingDirection" || + event.type === "standeeVerticalOffset" ) { return 0; } diff --git a/voicevox-remotion-template/src/data/zundamon-jiron/characters.ts b/voicevox-remotion-template/src/data/zundamon-jiron/characters.ts new file mode 100644 index 0000000..c61de53 --- /dev/null +++ b/voicevox-remotion-template/src/data/zundamon-jiron/characters.ts @@ -0,0 +1,61 @@ +import {getStandeeSet, type AvatarDefinition} from "../../standee-sets"; + +export type VoicevoxVoice = Readonly<{ + speakerName: string; + styleName: string; +}>; + +export type CharacterDefinition = Readonly<{ + displayName: string; + voicevox: VoicevoxVoice; + avatar: AvatarDefinition; +}>; + +export const characters = { + zundamon: { + displayName: "ずんだもん", + voicevox: { + speakerName: "ずんだもん", + styleName: "ノーマル", + }, + avatar: { + ...getStandeeSet("zundamon_ohnegus_rework_baby_default"), + imageLayout: { + ...getStandeeSet("zundamon_ohnegus_rework_baby_default").imageLayout, + sourceWidth: 3000, + sourceHeadWidth: 1600, + targetHeadWidth: 230, + translateY: -72, + }, + accentColor: "#79d36f", + nameplatePosition: "none", + idleAnimationType: "none", + speakingAnimationType: "rhubarbLipSync", + }, + }, + sayo: { + displayName: "小夜", + voicevox: { + speakerName: "小夜/SAYO", + styleName: "ノーマル", + }, + avatar: { + ...getStandeeSet("sayo_by_sayonaka"), + imageLayout: { + ...getStandeeSet("sayo_by_sayonaka").imageLayout, + sourceWidth: 2000, + sourceHeadWidth: 860, + targetHeadWidth: 280, + translateY: -72, + }, + accentColor: "#6b5f83", + nameplatePosition: "none", + idleAnimationType: "none", + speakingAnimationType: "rhubarbLipSync", + }, + }, +} as const satisfies Record; + +export type CharacterId = keyof typeof characters; + +export const initialVisibleCharacters: CharacterId[] = ["zundamon"]; diff --git a/voicevox-remotion-template/src/data/zundamon-jiron/script.ts b/voicevox-remotion-template/src/data/zundamon-jiron/script.ts new file mode 100644 index 0000000..d673e62 --- /dev/null +++ b/voicevox-remotion-template/src/data/zundamon-jiron/script.ts @@ -0,0 +1,106 @@ +import { + audio, + wait, + defineVQTimeline, + say, + standeeFacingDirection, + standeeVerticalOffset, + type VQCustomTimelineEvent, + type VQStandeeFacingDirectionEvent, + type VQStandeeVerticalOffsetEvent, + type VQSpeechEvent, + type VQTimelineEvent, + type VQTimelineInputEvent, +} from "../../lib/VQRemotionLib/timeline"; +import type {CharacterId, VoicevoxVoice} from "./characters"; + +export {characters, initialVisibleCharacters} from "./characters"; +export type {CharacterDefinition, CharacterId, VoicevoxVoice} from "./characters"; + +export const compositionTitle = "ずんだもんの持論"; + +export type ShowOptions = Readonly<{ + caption?: string; + durationSeconds?: number; +}>; + +export type SpeechEvent = VQSpeechEvent; + +export type ShowEvent = VQCustomTimelineEvent< + "show", + { + character: CharacterId; + caption?: string; + durationSeconds?: number; + } +>; + +export type TimelineEvent = VQTimelineEvent< + CharacterId, + VoicevoxVoice, + | ShowEvent + | VQStandeeFacingDirectionEvent + | VQStandeeVerticalOffsetEvent +>; +export type TimelineInputEvent = VQTimelineInputEvent< + CharacterId, + VoicevoxVoice, + | ShowEvent + | VQStandeeFacingDirectionEvent + | VQStandeeVerticalOffsetEvent +>; + +// 用途: キャラクターを画面に登場させ、任意の説明字幕を表示する。 +// 使用方法: timeline 内で show("sayo", {caption: "小夜が登場!"}) のように呼び出す。 +// オプションや引数詳細: durationSeconds を省略すると timing.ts の既定秒数で表示する。 +export const show = ( + character: CharacterId, + options: ShowOptions = {} +): ShowEvent => ({ + type: "show", + character, + ...options, +}); + +export const timeline = defineVQTimeline([ + audio("zundamon-jiron-bgm-001", "audio/common_bgm/saturn-3-music-rockabye-baby-music-box-lullaby-loopable-527146.mp3", { + playback: "loop", + volume: 0.2, + }), + say("zundamon-jiron-zunda-001", "zundamon", "ふー、着替えて落ち着いたのだ。"), + say("zundamon-jiron-zunda-002", "zundamon", "やっぱり寮ではこのスタイルが一番なのだ。"), + standeeVerticalOffset("zundamon-jiron-zunda-y-001", "zundamon", -150), + audio("zundamon-jiron-sound-effect-002", "audio/common_soundeffect/Door 6_5 Open.wav", + { + playback: "once", + durationSeconds: 1.5, + } + ), + show("sayo", { }), + say("zundamon-jiron-sayo-001", "sayo", "あ、ずんだもんだ。"), + standeeFacingDirection("zundamon-jiron-zunda-facing-001", "zundamon", "right"), + say("zundamon-jiron-sayo-002", "sayo", "あったかくなってきたにしても、おむつ一枚はちょっとラフな格好すぎない?"), + say("zundamon-jiron-zunda-003", "zundamon", "そんなことないのだ!これが夏のベストスタイルなのだ!"), + say("zundamon-jiron-zunda-004", "zundamon", "小夜もこのスタイルにしてみればいいのだ。たぶん似合うし、快適なのだ!"), + say("zundamon-jiron-sayo-003", "sayo", "えー、私はいいや。なんか清潔じゃない感じがするし…。"), + say("zundamon-jiron-zunda-005", "zundamon", "それは偏見なのだ!"), + say("zundamon-jiron-zunda-006", "zundamon", "考えてみるのだ。紙おむつは常に新品をあてることになるから、むしろ清潔なのだ!"), + say("zundamon-jiron-zunda-007", "zundamon", "毎日おろしたての下着を身につけるようなものなのだ。洗濯して使い続ける下着よりも清潔なのは明らかなのだ。"), + say("zundamon-jiron-sayo-004", "sayo", "うーん、そう言われると確かにそうかも…。"), + say("zundamon-jiron-zunda-008", "zundamon", "なのだ?だからみんなもこのスタイルを試してみるといいのだ!"), + say("zundamon-jiron-sayo-005", "sayo", "確かに言ってることはまちがってないんだけど、\n清潔なのは「穿いた直後」の話だよね?"), + say("zundamon-jiron-zunda-009", "zundamon", "うっ…!"), + audio("zundamon-jiron-sound-effect-001", "audio/common_soundeffect/nc174220_osikko7.mp3", + { + playback: "once", + durationSeconds: 10.0, + } + ), + wait(2.0), + say("zundamon-jiron-zunda-010", "zundamon", "……丁度いま、清潔じゃなくなったのだ。"), + say("zundamon-jiron-sayo-006", "sayo", "そうだね…。"), + say("zundamon-jiron-sayo-007", "sayo", "(……でも、漏らさないで使う限りはずんだもんが言うとおりなのかな?)"), + say("zundamon-jiron-zunda-011", "zundamon", "こまめに替えれば大丈夫なのだ!\nなので、小夜もおむつスタイルを試してみるといいのだ!"), + say("zundamon-jiron-sayo-008", "sayo", "考えておくね。\nずんだもんは早くおむつ替えてね。"), + say("zundamon-jiron-zunda-012", "zundamon", "そうするのだ!") +] satisfies readonly TimelineInputEvent[]); diff --git a/voicevox-remotion-template/src/data/zundamon-jiron/timing.ts b/voicevox-remotion-template/src/data/zundamon-jiron/timing.ts new file mode 100644 index 0000000..8916c38 --- /dev/null +++ b/voicevox-remotion-template/src/data/zundamon-jiron/timing.ts @@ -0,0 +1,115 @@ +import { + defineVQScenarioAssetWorkflow, + type VQScenarioAssetWorkflow, +} from "../../lib/VQRemotionLib/scenario"; +import {timeline, type SpeechEvent, type TimelineEvent} from "./script"; +import voicevoxManifest from "./voicevox-manifest.json"; + +type ManifestEntry = { + id: string; + character?: string; + speakerName?: string; + styleName?: string; + speakerId?: number; + file: string; + durationSeconds: number; +}; + +const manifestEntries = voicevoxManifest as ManifestEntry[]; +const manifestById = new Map( + manifestEntries.map((entry) => [entry.id, entry]) +); + +export const ZUNDAMON_JIRON_FPS = 30; +export const ZUNDAMON_JIRON_GAP_FRAMES = 6; +export const ZUNDAMON_JIRON_DEFAULT_SHOW_SECONDS = 1.5; + +export const hasAudioForSpeech = (speech: SpeechEvent) => + manifestById.has(speech.id); + +export const audioFileForSpeech = (speech: SpeechEvent) => + manifestById.get(speech.id)?.file ?? `audio/zundamon-jiron/lines/${speech.id}.wav`; + +export const durationForSpeech = ( + speech: SpeechEvent, + fps = ZUNDAMON_JIRON_FPS +) => { + const entry = manifestById.get(speech.id); + if (entry && Number.isFinite(entry.durationSeconds)) { + return Math.max(1, Math.ceil(entry.durationSeconds * fps)); + } + + const textForEstimate = speech.readAs ?? speech.text; + const estimatedSeconds = Math.max(1.2, textForEstimate.length * 0.11); + return Math.ceil(estimatedSeconds * fps); +}; + +export const durationForTimelineEvent = ( + event: TimelineEvent, + fps = ZUNDAMON_JIRON_FPS +) => { + if (event.type === "say") { + return durationForSpeech(event, fps); + } + + if (event.type === "audio") { + if (event.durationFrames && Number.isFinite(event.durationFrames)) { + return Math.max(1, Math.ceil(event.durationFrames)); + } + + if (event.durationSeconds && Number.isFinite(event.durationSeconds)) { + return Math.max(1, Math.ceil(event.durationSeconds * fps)); + } + + return 0; + } + + if ( + event.type === "clearStill" || + event.type === "clearVideo" || + event.type === "standeePosition" || + event.type === "standeeFacingDirection" || + event.type === "standeeVerticalOffset" + ) { + return 0; + } + + const durationSeconds = + "durationSeconds" in event && event.durationSeconds !== undefined + ? event.durationSeconds + : ZUNDAMON_JIRON_DEFAULT_SHOW_SECONDS; + return Math.max(1, Math.ceil(durationSeconds * fps)); +}; + +const doesTimelineEventAdvanceTimeline = (event: TimelineEvent) => + event.type !== "audio" && + event.type !== "standeeFacingDirection" && + event.type !== "standeeVerticalOffset"; + +export const ZundamonJironAssetWorkflow: VQScenarioAssetWorkflow = + defineVQScenarioAssetWorkflow({ + voicevox: { + scriptPath: "src/data/zundamon-jiron/script.ts", + outputDir: "public/audio/zundamon-jiron/lines", + manifestPath: "src/data/zundamon-jiron/voicevox-manifest.json", + }, + rhubarb: { + sourceManifestPath: "src/data/zundamon-jiron/voicevox-manifest.json", + manifestPath: "src/generated/lipsync/manifest.json", + outputDir: "src/generated/lipsync", + rawOutputDir: "public/lipsync/raw", + }, + }); + +export const totalZundamonJironDurationInFrames = ( + fps = ZUNDAMON_JIRON_FPS +) => + timeline.reduce((sum, event, index) => { + const durationInFrames = durationForTimelineEvent(event, fps); + if (!doesTimelineEventAdvanceTimeline(event) || durationInFrames <= 0) { + return sum; + } + + const gap = index < timeline.length - 1 ? ZUNDAMON_JIRON_GAP_FRAMES : 0; + return sum + durationInFrames + gap; + }, 0); diff --git a/voicevox-remotion-template/src/data/zundamon-jiron/voicevox-manifest.json b/voicevox-remotion-template/src/data/zundamon-jiron/voicevox-manifest.json new file mode 100644 index 0000000..5bfefce --- /dev/null +++ b/voicevox-remotion-template/src/data/zundamon-jiron/voicevox-manifest.json @@ -0,0 +1,173 @@ +[ + { + "id": "zundamon-jiron-zunda-001", + "character": "zundamon", + "speakerName": "ずんだもん", + "styleName": "ノーマル", + "speakerId": 3, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-001.wav", + "durationSeconds": 2.7306666666666666 + }, + { + "id": "zundamon-jiron-zunda-002", + "character": "zundamon", + "speakerName": "ずんだもん", + "styleName": "ノーマル", + "speakerId": 3, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-002.wav", + "durationSeconds": 3.2 + }, + { + "id": "zundamon-jiron-sayo-001", + "character": "sayo", + "speakerName": "小夜/SAYO", + "styleName": "ノーマル", + "speakerId": 46, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-sayo-001.wav", + "durationSeconds": 1.8666666666666667 + }, + { + "id": "zundamon-jiron-sayo-002", + "character": "sayo", + "speakerName": "小夜/SAYO", + "styleName": "ノーマル", + "speakerId": 46, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-sayo-002.wav", + "durationSeconds": 5.696 + }, + { + "id": "zundamon-jiron-zunda-003", + "character": "zundamon", + "speakerName": "ずんだもん", + "styleName": "ノーマル", + "speakerId": 3, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-003.wav", + "durationSeconds": 3.9466666666666668 + }, + { + "id": "zundamon-jiron-zunda-004", + "character": "zundamon", + "speakerName": "ずんだもん", + "styleName": "ノーマル", + "speakerId": 3, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-004.wav", + "durationSeconds": 5.621333333333333 + }, + { + "id": "zundamon-jiron-sayo-003", + "character": "sayo", + "speakerName": "小夜/SAYO", + "styleName": "ノーマル", + "speakerId": 46, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-sayo-003.wav", + "durationSeconds": 4.789333333333333 + }, + { + "id": "zundamon-jiron-zunda-005", + "character": "zundamon", + "speakerName": "ずんだもん", + "styleName": "ノーマル", + "speakerId": 3, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-005.wav", + "durationSeconds": 1.7173333333333334 + }, + { + "id": "zundamon-jiron-zunda-006", + "character": "zundamon", + "speakerName": "ずんだもん", + "styleName": "ノーマル", + "speakerId": 3, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-006.wav", + "durationSeconds": 6.826666666666667 + }, + { + "id": "zundamon-jiron-zunda-007", + "character": "zundamon", + "speakerName": "ずんだもん", + "styleName": "ノーマル", + "speakerId": 3, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-007.wav", + "durationSeconds": 8.597333333333333 + }, + { + "id": "zundamon-jiron-sayo-004", + "character": "sayo", + "speakerName": "小夜/SAYO", + "styleName": "ノーマル", + "speakerId": 46, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-sayo-004.wav", + "durationSeconds": 3.1573333333333333 + }, + { + "id": "zundamon-jiron-zunda-008", + "character": "zundamon", + "speakerName": "ずんだもん", + "styleName": "ノーマル", + "speakerId": 3, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-008.wav", + "durationSeconds": 4.501333333333333 + }, + { + "id": "zundamon-jiron-sayo-005", + "character": "sayo", + "speakerName": "小夜/SAYO", + "styleName": "ノーマル", + "speakerId": 46, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-sayo-005.wav", + "durationSeconds": 7.370666666666667 + }, + { + "id": "zundamon-jiron-zunda-009", + "character": "zundamon", + "speakerName": "ずんだもん", + "styleName": "ノーマル", + "speakerId": 3, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-009.wav", + "durationSeconds": 0.5546666666666666 + }, + { + "id": "zundamon-jiron-zunda-010", + "character": "zundamon", + "speakerName": "ずんだもん", + "styleName": "ノーマル", + "speakerId": 3, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-010.wav", + "durationSeconds": 2.997333333333333 + }, + { + "id": "zundamon-jiron-sayo-006", + "character": "sayo", + "speakerName": "小夜/SAYO", + "styleName": "ノーマル", + "speakerId": 46, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-sayo-006.wav", + "durationSeconds": 0.8853333333333333 + }, + { + "id": "zundamon-jiron-sayo-007", + "character": "sayo", + "speakerName": "小夜/SAYO", + "styleName": "ノーマル", + "speakerId": 46, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-sayo-007.wav", + "durationSeconds": 4.949333333333334 + }, + { + "id": "zundamon-jiron-zunda-011", + "character": "zundamon", + "speakerName": "ずんだもん", + "styleName": "ノーマル", + "speakerId": 3, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-011.wav", + "durationSeconds": 6.496 + }, + { + "id": "zundamon-jiron-sayo-008", + "character": "sayo", + "speakerName": "小夜/SAYO", + "styleName": "ノーマル", + "speakerId": 46, + "file": "audio/zundamon-jiron/lines/zundamon-jiron-sayo-008.wav", + "durationSeconds": 4.053333333333334 + } +] diff --git a/voicevox-remotion-template/src/generated/lipsync/manifest.json b/voicevox-remotion-template/src/generated/lipsync/manifest.json index 24febe9..95c86e0 100644 --- a/voicevox-remotion-template/src/generated/lipsync/manifest.json +++ b/voicevox-remotion-template/src/generated/lipsync/manifest.json @@ -8536,6 +8536,3485 @@ "source": "postPhoneme" } ] + }, + "zundamon-jiron-zunda-001": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-001.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 2.73, + "cues": [ + { + "start": 0, + "end": 0.16, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.16, + "end": 0.41, + "mouth": "i", + "source": "B" + }, + { + "start": 0.41, + "end": 0.49, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.49, + "end": 0.77, + "mouth": "i", + "source": "B" + }, + { + "start": 0.77, + "end": 1.04, + "mouth": "rest", + "source": "X" + }, + { + "start": 1.04, + "end": 1.13, + "mouth": "i", + "source": "B" + }, + { + "start": 1.13, + "end": 1.21, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.21, + "end": 1.32, + "mouth": "e", + "source": "C" + }, + { + "start": 1.32, + "end": 1.39, + "mouth": "o", + "source": "E" + }, + { + "start": 1.39, + "end": 1.47, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.47, + "end": 1.65, + "mouth": "u", + "source": "F" + }, + { + "start": 1.65, + "end": 1.86, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.86, + "end": 1.96, + "mouth": "i", + "source": "B" + }, + { + "start": 1.96, + "end": 2.03, + "mouth": "e", + "source": "C" + }, + { + "start": 2.03, + "end": 2.1, + "mouth": "i", + "source": "B" + }, + { + "start": 2.1, + "end": 2.24, + "mouth": "e", + "source": "C" + }, + { + "start": 2.24, + "end": 2.38, + "mouth": "i", + "source": "B" + }, + { + "start": 2.38, + "end": 2.59, + "mouth": "e", + "source": "C" + }, + { + "start": 2.59, + "end": 2.66, + "mouth": "i", + "source": "B" + }, + { + "start": 2.66, + "end": 2.73, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-zunda-002": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-002.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 3.2, + "cues": [ + { + "start": 0, + "end": 0.1, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.1, + "end": 0.15, + "mouth": "i", + "source": "B" + }, + { + "start": 0.15, + "end": 0.19, + "mouth": "e", + "source": "C" + }, + { + "start": 0.19, + "end": 0.72, + "mouth": "i", + "source": "B" + }, + { + "start": 0.72, + "end": 0.86, + "mouth": "u", + "source": "F" + }, + { + "start": 0.86, + "end": 1.14, + "mouth": "i", + "source": "B" + }, + { + "start": 1.14, + "end": 1.39, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.39, + "end": 1.47, + "mouth": "i", + "source": "B" + }, + { + "start": 1.47, + "end": 1.57, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.57, + "end": 1.94, + "mouth": "i", + "source": "B" + }, + { + "start": 1.94, + "end": 2.01, + "mouth": "e", + "source": "C" + }, + { + "start": 2.01, + "end": 2.09, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.09, + "end": 2.25, + "mouth": "i", + "source": "B" + }, + { + "start": 2.25, + "end": 2.33, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.33, + "end": 2.71, + "mouth": "e", + "source": "C" + }, + { + "start": 2.71, + "end": 2.78, + "mouth": "i", + "source": "B" + }, + { + "start": 2.78, + "end": 3.06, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.06, + "end": 3.14, + "mouth": "i", + "source": "B" + }, + { + "start": 3.14, + "end": 3.2, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-sayo-001": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-sayo-001.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 1.86, + "cues": [ + { + "start": 0, + "end": 0.04, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.04, + "end": 0.08, + "mouth": "i", + "source": "B" + }, + { + "start": 0.08, + "end": 0.21, + "mouth": "u", + "source": "F" + }, + { + "start": 0.21, + "end": 0.26, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.26, + "end": 0.34, + "mouth": "i", + "source": "B" + }, + { + "start": 0.34, + "end": 0.83, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.83, + "end": 0.92, + "mouth": "i", + "source": "B" + }, + { + "start": 0.92, + "end": 0.99, + "mouth": "e", + "source": "C" + }, + { + "start": 0.99, + "end": 1.06, + "mouth": "i", + "source": "B" + }, + { + "start": 1.06, + "end": 1.2, + "mouth": "e", + "source": "C" + }, + { + "start": 1.2, + "end": 1.28, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.28, + "end": 1.79, + "mouth": "i", + "source": "B" + }, + { + "start": 1.79, + "end": 1.86, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-zunda-003": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-003.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 3.94, + "cues": [ + { + "start": 0, + "end": 0.12, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.12, + "end": 0.18, + "mouth": "i", + "source": "B" + }, + { + "start": 0.18, + "end": 0.24, + "mouth": "e", + "source": "C" + }, + { + "start": 0.24, + "end": 0.34, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.34, + "end": 0.4, + "mouth": "i", + "source": "B" + }, + { + "start": 0.4, + "end": 0.46, + "mouth": "e", + "source": "C" + }, + { + "start": 0.46, + "end": 0.54, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.54, + "end": 0.61, + "mouth": "u", + "source": "F" + }, + { + "start": 0.61, + "end": 0.82, + "mouth": "i", + "source": "B" + }, + { + "start": 0.82, + "end": 1.03, + "mouth": "u", + "source": "F" + }, + { + "start": 1.03, + "end": 1.1, + "mouth": "e", + "source": "C" + }, + { + "start": 1.1, + "end": 1.41, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.41, + "end": 1.51, + "mouth": "i", + "source": "B" + }, + { + "start": 1.51, + "end": 1.64, + "mouth": "rest", + "source": "X" + }, + { + "start": 1.64, + "end": 1.9, + "mouth": "i", + "source": "B" + }, + { + "start": 1.9, + "end": 2.04, + "mouth": "e", + "source": "C" + }, + { + "start": 2.04, + "end": 2.11, + "mouth": "i", + "source": "B" + }, + { + "start": 2.11, + "end": 2.25, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.25, + "end": 2.3, + "mouth": "i", + "source": "B" + }, + { + "start": 2.3, + "end": 2.35, + "mouth": "a", + "source": "D" + }, + { + "start": 2.35, + "end": 2.43, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.43, + "end": 2.58, + "mouth": "i", + "source": "B" + }, + { + "start": 2.58, + "end": 2.78, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.78, + "end": 3.47, + "mouth": "i", + "source": "B" + }, + { + "start": 3.47, + "end": 3.54, + "mouth": "o", + "source": "E" + }, + { + "start": 3.54, + "end": 3.8, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.8, + "end": 3.88, + "mouth": "i", + "source": "B" + }, + { + "start": 3.88, + "end": 3.94, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-sayo-002": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-sayo-002.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 5.69, + "cues": [ + { + "start": 0, + "end": 0.05, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.05, + "end": 0.12, + "mouth": "i", + "source": "B" + }, + { + "start": 0.12, + "end": 0.33, + "mouth": "e", + "source": "C" + }, + { + "start": 0.33, + "end": 0.47, + "mouth": "u", + "source": "F" + }, + { + "start": 0.47, + "end": 0.61, + "mouth": "i", + "source": "B" + }, + { + "start": 0.61, + "end": 0.69, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.69, + "end": 0.76, + "mouth": "e", + "source": "C" + }, + { + "start": 0.76, + "end": 0.83, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.83, + "end": 1.56, + "mouth": "i", + "source": "B" + }, + { + "start": 1.56, + "end": 1.86, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.86, + "end": 2.37, + "mouth": "rest", + "source": "X" + }, + { + "start": 2.37, + "end": 2.45, + "mouth": "i", + "source": "B" + }, + { + "start": 2.45, + "end": 2.53, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.53, + "end": 2.85, + "mouth": "e", + "source": "C" + }, + { + "start": 2.85, + "end": 3.06, + "mouth": "i", + "source": "B" + }, + { + "start": 3.06, + "end": 3.14, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.14, + "end": 3.2, + "mouth": "e", + "source": "C" + }, + { + "start": 3.2, + "end": 3.33, + "mouth": "i", + "source": "B" + }, + { + "start": 3.33, + "end": 3.42, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.42, + "end": 3.62, + "mouth": "e", + "source": "C" + }, + { + "start": 3.62, + "end": 3.69, + "mouth": "i", + "source": "B" + }, + { + "start": 3.69, + "end": 3.77, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.77, + "end": 3.94, + "mouth": "e", + "source": "C" + }, + { + "start": 3.94, + "end": 4.15, + "mouth": "i", + "source": "B" + }, + { + "start": 4.15, + "end": 4.29, + "mouth": "a", + "source": "D" + }, + { + "start": 4.29, + "end": 4.43, + "mouth": "e", + "source": "C" + }, + { + "start": 4.43, + "end": 4.51, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.51, + "end": 4.74, + "mouth": "o", + "source": "E" + }, + { + "start": 4.74, + "end": 4.81, + "mouth": "e", + "source": "C" + }, + { + "start": 4.81, + "end": 4.89, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.89, + "end": 4.94, + "mouth": "e", + "source": "C" + }, + { + "start": 4.94, + "end": 5.06, + "mouth": "i", + "source": "B" + }, + { + "start": 5.06, + "end": 5.14, + "mouth": "closed", + "source": "A" + }, + { + "start": 5.14, + "end": 5.21, + "mouth": "e", + "source": "C" + }, + { + "start": 5.21, + "end": 5.56, + "mouth": "i", + "source": "B" + }, + { + "start": 5.56, + "end": 5.69, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-zunda-004": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-004.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 5.62, + "cues": [ + { + "start": 0, + "end": 0.11, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.11, + "end": 0.2, + "mouth": "i", + "source": "B" + }, + { + "start": 0.2, + "end": 0.27, + "mouth": "e", + "source": "C" + }, + { + "start": 0.27, + "end": 0.48, + "mouth": "i", + "source": "B" + }, + { + "start": 0.48, + "end": 0.56, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.56, + "end": 0.74, + "mouth": "i", + "source": "B" + }, + { + "start": 0.74, + "end": 0.81, + "mouth": "e", + "source": "C" + }, + { + "start": 0.81, + "end": 0.88, + "mouth": "o", + "source": "E" + }, + { + "start": 0.88, + "end": 0.99, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.99, + "end": 1.09, + "mouth": "e", + "source": "C" + }, + { + "start": 1.09, + "end": 1.23, + "mouth": "o", + "source": "E" + }, + { + "start": 1.23, + "end": 1.3, + "mouth": "u", + "source": "F" + }, + { + "start": 1.3, + "end": 1.44, + "mouth": "o", + "source": "E" + }, + { + "start": 1.44, + "end": 1.53, + "mouth": "u", + "source": "F" + }, + { + "start": 1.53, + "end": 1.57, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.57, + "end": 1.65, + "mouth": "i", + "source": "B" + }, + { + "start": 1.65, + "end": 1.78, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.78, + "end": 1.91, + "mouth": "i", + "source": "B" + }, + { + "start": 1.91, + "end": 2.19, + "mouth": "o", + "source": "E" + }, + { + "start": 2.19, + "end": 2.26, + "mouth": "u", + "source": "F" + }, + { + "start": 2.26, + "end": 2.4, + "mouth": "o", + "source": "E" + }, + { + "start": 2.4, + "end": 2.69, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.69, + "end": 2.79, + "mouth": "i", + "source": "B" + }, + { + "start": 2.79, + "end": 2.98, + "mouth": "rest", + "source": "X" + }, + { + "start": 2.98, + "end": 3.04, + "mouth": "i", + "source": "B" + }, + { + "start": 3.04, + "end": 3.1, + "mouth": "e", + "source": "C" + }, + { + "start": 3.1, + "end": 3.31, + "mouth": "u", + "source": "F" + }, + { + "start": 3.31, + "end": 3.73, + "mouth": "i", + "source": "B" + }, + { + "start": 3.73, + "end": 3.83, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.83, + "end": 3.92, + "mouth": "i", + "source": "B" + }, + { + "start": 3.92, + "end": 4.03, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.03, + "end": 4.31, + "mouth": "i", + "source": "B" + }, + { + "start": 4.31, + "end": 4.44, + "mouth": "rest", + "source": "X" + }, + { + "start": 4.44, + "end": 4.52, + "mouth": "i", + "source": "B" + }, + { + "start": 4.52, + "end": 4.66, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.66, + "end": 4.72, + "mouth": "u", + "source": "F" + }, + { + "start": 4.72, + "end": 5.05, + "mouth": "i", + "source": "B" + }, + { + "start": 5.05, + "end": 5.26, + "mouth": "e", + "source": "C" + }, + { + "start": 5.26, + "end": 5.37, + "mouth": "closed", + "source": "A" + }, + { + "start": 5.37, + "end": 5.45, + "mouth": "i", + "source": "B" + }, + { + "start": 5.45, + "end": 5.55, + "mouth": "closed", + "source": "A" + }, + { + "start": 5.55, + "end": 5.62, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-sayo-003": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-sayo-003.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 4.78, + "cues": [ + { + "start": 0, + "end": 0.03, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.03, + "end": 0.29, + "mouth": "i", + "source": "B" + }, + { + "start": 0.29, + "end": 0.45, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.45, + "end": 0.94, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.94, + "end": 0.99, + "mouth": "i", + "source": "B" + }, + { + "start": 0.99, + "end": 1.21, + "mouth": "e", + "source": "C" + }, + { + "start": 1.21, + "end": 1.23, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.23, + "end": 1.3, + "mouth": "i", + "source": "B" + }, + { + "start": 1.3, + "end": 1.36, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.36, + "end": 1.48, + "mouth": "e", + "source": "C" + }, + { + "start": 1.48, + "end": 1.83, + "mouth": "i", + "source": "B" + }, + { + "start": 1.83, + "end": 1.98, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.98, + "end": 2.4, + "mouth": "i", + "source": "B" + }, + { + "start": 2.4, + "end": 2.53, + "mouth": "o", + "source": "E" + }, + { + "start": 2.53, + "end": 2.61, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.61, + "end": 2.74, + "mouth": "e", + "source": "C" + }, + { + "start": 2.74, + "end": 2.82, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.82, + "end": 2.87, + "mouth": "u", + "source": "F" + }, + { + "start": 2.87, + "end": 2.91, + "mouth": "i", + "source": "B" + }, + { + "start": 2.91, + "end": 3.05, + "mouth": "u", + "source": "F" + }, + { + "start": 3.05, + "end": 3.26, + "mouth": "e", + "source": "C" + }, + { + "start": 3.26, + "end": 3.33, + "mouth": "i", + "source": "B" + }, + { + "start": 3.33, + "end": 3.54, + "mouth": "e", + "source": "C" + }, + { + "start": 3.54, + "end": 3.82, + "mouth": "o", + "source": "E" + }, + { + "start": 3.82, + "end": 3.95, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.95, + "end": 4.13, + "mouth": "e", + "source": "C" + }, + { + "start": 4.13, + "end": 4.24, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.24, + "end": 4.29, + "mouth": "e", + "source": "C" + }, + { + "start": 4.29, + "end": 4.33, + "mouth": "u", + "source": "F" + }, + { + "start": 4.33, + "end": 4.4, + "mouth": "e", + "source": "C" + }, + { + "start": 4.4, + "end": 4.51, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.51, + "end": 4.7, + "mouth": "i", + "source": "B" + }, + { + "start": 4.7, + "end": 4.78, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-zunda-005": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-005.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 1.71, + "cues": [ + { + "start": 0, + "end": 0.14, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.14, + "end": 0.17, + "mouth": "e", + "source": "C" + }, + { + "start": 0.17, + "end": 0.25, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.25, + "end": 0.66, + "mouth": "i", + "source": "B" + }, + { + "start": 0.66, + "end": 0.74, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.74, + "end": 1.31, + "mouth": "i", + "source": "B" + }, + { + "start": 1.31, + "end": 1.46, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.46, + "end": 1.54, + "mouth": "i", + "source": "B" + }, + { + "start": 1.54, + "end": 1.64, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.64, + "end": 1.71, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-zunda-006": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-006.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 6.82, + "cues": [ + { + "start": 0, + "end": 0.13, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.13, + "end": 0.51, + "mouth": "i", + "source": "B" + }, + { + "start": 0.51, + "end": 0.58, + "mouth": "o", + "source": "E" + }, + { + "start": 0.58, + "end": 0.66, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.66, + "end": 1.11, + "mouth": "i", + "source": "B" + }, + { + "start": 1.11, + "end": 1.44, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.44, + "end": 1.58, + "mouth": "i", + "source": "B" + }, + { + "start": 1.58, + "end": 1.72, + "mouth": "rest", + "source": "X" + }, + { + "start": 1.72, + "end": 1.84, + "mouth": "i", + "source": "B" + }, + { + "start": 1.84, + "end": 1.95, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.95, + "end": 2.05, + "mouth": "i", + "source": "B" + }, + { + "start": 2.05, + "end": 2.19, + "mouth": "u", + "source": "F" + }, + { + "start": 2.19, + "end": 2.47, + "mouth": "o", + "source": "E" + }, + { + "start": 2.47, + "end": 2.55, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.55, + "end": 3.1, + "mouth": "i", + "source": "B" + }, + { + "start": 3.1, + "end": 3.27, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.27, + "end": 3.46, + "mouth": "o", + "source": "E" + }, + { + "start": 3.46, + "end": 3.53, + "mouth": "u", + "source": "F" + }, + { + "start": 3.53, + "end": 3.6, + "mouth": "e", + "source": "C" + }, + { + "start": 3.6, + "end": 3.88, + "mouth": "i", + "source": "B" + }, + { + "start": 3.88, + "end": 3.96, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.96, + "end": 4.04, + "mouth": "u", + "source": "F" + }, + { + "start": 4.04, + "end": 4.32, + "mouth": "i", + "source": "B" + }, + { + "start": 4.32, + "end": 4.53, + "mouth": "e", + "source": "C" + }, + { + "start": 4.53, + "end": 4.61, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.61, + "end": 4.73, + "mouth": "e", + "source": "C" + }, + { + "start": 4.73, + "end": 5, + "mouth": "closed", + "source": "A" + }, + { + "start": 5, + "end": 5.27, + "mouth": "i", + "source": "B" + }, + { + "start": 5.27, + "end": 5.34, + "mouth": "closed", + "source": "A" + }, + { + "start": 5.34, + "end": 5.47, + "mouth": "i", + "source": "B" + }, + { + "start": 5.47, + "end": 5.54, + "mouth": "e", + "source": "C" + }, + { + "start": 5.54, + "end": 5.61, + "mouth": "o", + "source": "E" + }, + { + "start": 5.61, + "end": 5.69, + "mouth": "closed", + "source": "A" + }, + { + "start": 5.69, + "end": 5.84, + "mouth": "i", + "source": "B" + }, + { + "start": 5.84, + "end": 5.96, + "mouth": "closed", + "source": "A" + }, + { + "start": 5.96, + "end": 6, + "mouth": "u", + "source": "F" + }, + { + "start": 6, + "end": 6.08, + "mouth": "closed", + "source": "A" + }, + { + "start": 6.08, + "end": 6.21, + "mouth": "i", + "source": "B" + }, + { + "start": 6.21, + "end": 6.35, + "mouth": "e", + "source": "C" + }, + { + "start": 6.35, + "end": 6.42, + "mouth": "i", + "source": "B" + }, + { + "start": 6.42, + "end": 6.5, + "mouth": "closed", + "source": "A" + }, + { + "start": 6.5, + "end": 6.61, + "mouth": "o", + "source": "E" + }, + { + "start": 6.61, + "end": 6.69, + "mouth": "closed", + "source": "A" + }, + { + "start": 6.69, + "end": 6.76, + "mouth": "i", + "source": "B" + }, + { + "start": 6.76, + "end": 6.82, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-zunda-007": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-007.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 8.59, + "cues": [ + { + "start": 0, + "end": 0.13, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.13, + "end": 0.19, + "mouth": "i", + "source": "B" + }, + { + "start": 0.19, + "end": 0.31, + "mouth": "u", + "source": "F" + }, + { + "start": 0.31, + "end": 0.45, + "mouth": "i", + "source": "B" + }, + { + "start": 0.45, + "end": 0.53, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.53, + "end": 0.86, + "mouth": "u", + "source": "F" + }, + { + "start": 0.86, + "end": 0.93, + "mouth": "e", + "source": "C" + }, + { + "start": 0.93, + "end": 1.07, + "mouth": "i", + "source": "B" + }, + { + "start": 1.07, + "end": 1.15, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.15, + "end": 1.23, + "mouth": "i", + "source": "B" + }, + { + "start": 1.23, + "end": 1.44, + "mouth": "o", + "source": "E" + }, + { + "start": 1.44, + "end": 1.52, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.52, + "end": 1.63, + "mouth": "e", + "source": "C" + }, + { + "start": 1.63, + "end": 1.84, + "mouth": "i", + "source": "B" + }, + { + "start": 1.84, + "end": 1.91, + "mouth": "a", + "source": "D" + }, + { + "start": 1.91, + "end": 2.01, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.01, + "end": 2.14, + "mouth": "i", + "source": "B" + }, + { + "start": 2.14, + "end": 2.25, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.25, + "end": 2.82, + "mouth": "i", + "source": "B" + }, + { + "start": 2.82, + "end": 3.96, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.96, + "end": 4.04, + "mouth": "i", + "source": "B" + }, + { + "start": 4.04, + "end": 4.11, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.11, + "end": 4.18, + "mouth": "i", + "source": "B" + }, + { + "start": 4.18, + "end": 4.24, + "mouth": "u", + "source": "F" + }, + { + "start": 4.24, + "end": 4.3, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.3, + "end": 4.36, + "mouth": "e", + "source": "C" + }, + { + "start": 4.36, + "end": 4.44, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.44, + "end": 4.59, + "mouth": "i", + "source": "B" + }, + { + "start": 4.59, + "end": 4.67, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.67, + "end": 4.76, + "mouth": "u", + "source": "F" + }, + { + "start": 4.76, + "end": 4.95, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.95, + "end": 5.1, + "mouth": "e", + "source": "C" + }, + { + "start": 5.1, + "end": 5.18, + "mouth": "closed", + "source": "A" + }, + { + "start": 5.18, + "end": 5.72, + "mouth": "i", + "source": "B" + }, + { + "start": 5.72, + "end": 5.8, + "mouth": "closed", + "source": "A" + }, + { + "start": 5.8, + "end": 5.87, + "mouth": "e", + "source": "C" + }, + { + "start": 5.87, + "end": 6.35, + "mouth": "i", + "source": "B" + }, + { + "start": 6.35, + "end": 6.58, + "mouth": "closed", + "source": "A" + }, + { + "start": 6.58, + "end": 6.67, + "mouth": "i", + "source": "B" + }, + { + "start": 6.67, + "end": 6.79, + "mouth": "closed", + "source": "A" + }, + { + "start": 6.79, + "end": 6.83, + "mouth": "u", + "source": "F" + }, + { + "start": 6.83, + "end": 6.91, + "mouth": "closed", + "source": "A" + }, + { + "start": 6.91, + "end": 7.07, + "mouth": "i", + "source": "B" + }, + { + "start": 7.07, + "end": 7.21, + "mouth": "e", + "source": "C" + }, + { + "start": 7.21, + "end": 7.35, + "mouth": "i", + "source": "B" + }, + { + "start": 7.35, + "end": 7.65, + "mouth": "closed", + "source": "A" + }, + { + "start": 7.65, + "end": 7.82, + "mouth": "e", + "source": "C" + }, + { + "start": 7.82, + "end": 7.96, + "mouth": "i", + "source": "B" + }, + { + "start": 7.96, + "end": 8.1, + "mouth": "e", + "source": "C" + }, + { + "start": 8.1, + "end": 8.33, + "mouth": "closed", + "source": "A" + }, + { + "start": 8.33, + "end": 8.45, + "mouth": "u", + "source": "F" + }, + { + "start": 8.45, + "end": 8.52, + "mouth": "i", + "source": "B" + }, + { + "start": 8.52, + "end": 8.59, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-sayo-004": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-sayo-004.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 3.15, + "cues": [ + { + "start": 0, + "end": 0.06, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.06, + "end": 0.11, + "mouth": "i", + "source": "B" + }, + { + "start": 0.11, + "end": 0.23, + "mouth": "o", + "source": "E" + }, + { + "start": 0.23, + "end": 0.37, + "mouth": "u", + "source": "F" + }, + { + "start": 0.37, + "end": 0.51, + "mouth": "i", + "source": "B" + }, + { + "start": 0.51, + "end": 0.93, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.93, + "end": 1.09, + "mouth": "i", + "source": "B" + }, + { + "start": 1.09, + "end": 1.23, + "mouth": "a", + "source": "D" + }, + { + "start": 1.23, + "end": 1.37, + "mouth": "i", + "source": "B" + }, + { + "start": 1.37, + "end": 1.45, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.45, + "end": 1.62, + "mouth": "e", + "source": "C" + }, + { + "start": 1.62, + "end": 1.74, + "mouth": "i", + "source": "B" + }, + { + "start": 1.74, + "end": 1.76, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.76, + "end": 1.82, + "mouth": "i", + "source": "B" + }, + { + "start": 1.82, + "end": 1.87, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.87, + "end": 2.02, + "mouth": "e", + "source": "C" + }, + { + "start": 2.02, + "end": 2.16, + "mouth": "i", + "source": "B" + }, + { + "start": 2.16, + "end": 2.23, + "mouth": "e", + "source": "C" + }, + { + "start": 2.23, + "end": 2.58, + "mouth": "i", + "source": "B" + }, + { + "start": 2.58, + "end": 2.79, + "mouth": "e", + "source": "C" + }, + { + "start": 2.79, + "end": 3.07, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.07, + "end": 3.15, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-zunda-008": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-008.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 4.5, + "cues": [ + { + "start": 0, + "end": 0.1, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.1, + "end": 0.21, + "mouth": "i", + "source": "B" + }, + { + "start": 0.21, + "end": 0.31, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.31, + "end": 0.38, + "mouth": "u", + "source": "F" + }, + { + "start": 0.38, + "end": 0.51, + "mouth": "i", + "source": "B" + }, + { + "start": 0.51, + "end": 0.78, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.78, + "end": 0.93, + "mouth": "i", + "source": "B" + }, + { + "start": 0.93, + "end": 1.07, + "mouth": "rest", + "source": "X" + }, + { + "start": 1.07, + "end": 1.39, + "mouth": "i", + "source": "B" + }, + { + "start": 1.39, + "end": 1.6, + "mouth": "e", + "source": "C" + }, + { + "start": 1.6, + "end": 1.67, + "mouth": "i", + "source": "B" + }, + { + "start": 1.67, + "end": 1.88, + "mouth": "e", + "source": "C" + }, + { + "start": 1.88, + "end": 1.95, + "mouth": "a", + "source": "D" + }, + { + "start": 1.95, + "end": 2.21, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.21, + "end": 2.29, + "mouth": "i", + "source": "B" + }, + { + "start": 2.29, + "end": 2.38, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.38, + "end": 2.43, + "mouth": "i", + "source": "B" + }, + { + "start": 2.43, + "end": 2.54, + "mouth": "e", + "source": "C" + }, + { + "start": 2.54, + "end": 2.61, + "mouth": "o", + "source": "E" + }, + { + "start": 2.61, + "end": 2.82, + "mouth": "u", + "source": "F" + }, + { + "start": 2.82, + "end": 3.08, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.08, + "end": 3.17, + "mouth": "i", + "source": "B" + }, + { + "start": 3.17, + "end": 3.25, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.25, + "end": 3.38, + "mouth": "e", + "source": "C" + }, + { + "start": 3.38, + "end": 3.46, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.46, + "end": 3.57, + "mouth": "i", + "source": "B" + }, + { + "start": 3.57, + "end": 3.71, + "mouth": "u", + "source": "F" + }, + { + "start": 3.71, + "end": 3.92, + "mouth": "i", + "source": "B" + }, + { + "start": 3.92, + "end": 4.13, + "mouth": "u", + "source": "F" + }, + { + "start": 4.13, + "end": 4.27, + "mouth": "e", + "source": "C" + }, + { + "start": 4.27, + "end": 4.35, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.35, + "end": 4.43, + "mouth": "i", + "source": "B" + }, + { + "start": 4.43, + "end": 4.5, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-sayo-005": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-sayo-005.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 7.37, + "cues": [ + { + "start": 0, + "end": 0.09, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.09, + "end": 0.23, + "mouth": "e", + "source": "C" + }, + { + "start": 0.23, + "end": 0.35, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.35, + "end": 0.49, + "mouth": "e", + "source": "C" + }, + { + "start": 0.49, + "end": 0.98, + "mouth": "i", + "source": "B" + }, + { + "start": 0.98, + "end": 1.12, + "mouth": "e", + "source": "C" + }, + { + "start": 1.12, + "end": 1.2, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.2, + "end": 1.43, + "mouth": "e", + "source": "C" + }, + { + "start": 1.43, + "end": 1.51, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.51, + "end": 1.82, + "mouth": "i", + "source": "B" + }, + { + "start": 1.82, + "end": 2.1, + "mouth": "u", + "source": "F" + }, + { + "start": 2.1, + "end": 2.18, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.18, + "end": 2.24, + "mouth": "e", + "source": "C" + }, + { + "start": 2.24, + "end": 2.36, + "mouth": "i", + "source": "B" + }, + { + "start": 2.36, + "end": 2.44, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.44, + "end": 2.55, + "mouth": "e", + "source": "C" + }, + { + "start": 2.55, + "end": 2.7, + "mouth": "i", + "source": "B" + }, + { + "start": 2.7, + "end": 2.73, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.73, + "end": 2.81, + "mouth": "i", + "source": "B" + }, + { + "start": 2.81, + "end": 2.89, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.89, + "end": 3, + "mouth": "i", + "source": "B" + }, + { + "start": 3, + "end": 3.26, + "mouth": "rest", + "source": "X" + }, + { + "start": 3.26, + "end": 3.34, + "mouth": "i", + "source": "B" + }, + { + "start": 3.34, + "end": 3.42, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.42, + "end": 3.49, + "mouth": "e", + "source": "C" + }, + { + "start": 3.49, + "end": 3.63, + "mouth": "i", + "source": "B" + }, + { + "start": 3.63, + "end": 3.77, + "mouth": "e", + "source": "C" + }, + { + "start": 3.77, + "end": 3.91, + "mouth": "i", + "source": "B" + }, + { + "start": 3.91, + "end": 3.99, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.99, + "end": 4.08, + "mouth": "i", + "source": "B" + }, + { + "start": 4.08, + "end": 4.15, + "mouth": "u", + "source": "F" + }, + { + "start": 4.15, + "end": 4.29, + "mouth": "e", + "source": "C" + }, + { + "start": 4.29, + "end": 4.67, + "mouth": "i", + "source": "B" + }, + { + "start": 4.67, + "end": 4.91, + "mouth": "e", + "source": "C" + }, + { + "start": 4.91, + "end": 5.12, + "mouth": "i", + "source": "B" + }, + { + "start": 5.12, + "end": 5.2, + "mouth": "closed", + "source": "A" + }, + { + "start": 5.2, + "end": 5.29, + "mouth": "e", + "source": "C" + }, + { + "start": 5.29, + "end": 5.37, + "mouth": "closed", + "source": "A" + }, + { + "start": 5.37, + "end": 5.49, + "mouth": "u", + "source": "F" + }, + { + "start": 5.49, + "end": 5.67, + "mouth": "closed", + "source": "A" + }, + { + "start": 5.67, + "end": 6.03, + "mouth": "rest", + "source": "X" + }, + { + "start": 6.03, + "end": 6.08, + "mouth": "i", + "source": "B" + }, + { + "start": 6.08, + "end": 6.27, + "mouth": "o", + "source": "E" + }, + { + "start": 6.27, + "end": 6.41, + "mouth": "i", + "source": "B" + }, + { + "start": 6.41, + "end": 6.54, + "mouth": "closed", + "source": "A" + }, + { + "start": 6.54, + "end": 6.73, + "mouth": "e", + "source": "C" + }, + { + "start": 6.73, + "end": 6.8, + "mouth": "i", + "source": "B" + }, + { + "start": 6.8, + "end": 6.88, + "mouth": "closed", + "source": "A" + }, + { + "start": 6.88, + "end": 6.99, + "mouth": "i", + "source": "B" + }, + { + "start": 6.99, + "end": 7.2, + "mouth": "e", + "source": "C" + }, + { + "start": 7.2, + "end": 7.27, + "mouth": "i", + "source": "B" + }, + { + "start": 7.27, + "end": 7.37, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-zunda-009": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-009.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 0.55, + "cues": [ + { + "start": 0, + "end": 0.09, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.09, + "end": 0.14, + "mouth": "i", + "source": "B" + }, + { + "start": 0.14, + "end": 0.33, + "mouth": "u", + "source": "F" + }, + { + "start": 0.33, + "end": 0.55, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-zunda-010": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-010.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 2.99, + "cues": [ + { + "start": 0, + "end": 0.11, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.11, + "end": 0.32, + "mouth": "u", + "source": "F" + }, + { + "start": 0.32, + "end": 0.39, + "mouth": "i", + "source": "B" + }, + { + "start": 0.39, + "end": 0.53, + "mouth": "o", + "source": "E" + }, + { + "start": 0.53, + "end": 0.6, + "mouth": "i", + "source": "B" + }, + { + "start": 0.6, + "end": 0.9, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.9, + "end": 1.18, + "mouth": "i", + "source": "B" + }, + { + "start": 1.18, + "end": 1.24, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.24, + "end": 1.51, + "mouth": "i", + "source": "B" + }, + { + "start": 1.51, + "end": 1.59, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.59, + "end": 1.77, + "mouth": "i", + "source": "B" + }, + { + "start": 1.77, + "end": 1.84, + "mouth": "e", + "source": "C" + }, + { + "start": 1.84, + "end": 1.91, + "mouth": "i", + "source": "B" + }, + { + "start": 1.91, + "end": 1.99, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.99, + "end": 2.11, + "mouth": "u", + "source": "F" + }, + { + "start": 2.11, + "end": 2.25, + "mouth": "e", + "source": "C" + }, + { + "start": 2.25, + "end": 2.46, + "mouth": "i", + "source": "B" + }, + { + "start": 2.46, + "end": 2.56, + "mouth": "a", + "source": "D" + }, + { + "start": 2.56, + "end": 2.6, + "mouth": "e", + "source": "C" + }, + { + "start": 2.6, + "end": 2.79, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.79, + "end": 2.93, + "mouth": "i", + "source": "B" + }, + { + "start": 2.93, + "end": 2.99, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-sayo-006": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-sayo-006.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 0.88, + "cues": [ + { + "start": 0, + "end": 0.03, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.03, + "end": 0.11, + "mouth": "i", + "source": "B" + }, + { + "start": 0.11, + "end": 0.24, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.24, + "end": 0.32, + "mouth": "i", + "source": "B" + }, + { + "start": 0.32, + "end": 0.4, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.4, + "end": 0.7, + "mouth": "i", + "source": "B" + }, + { + "start": 0.7, + "end": 0.77, + "mouth": "e", + "source": "C" + }, + { + "start": 0.77, + "end": 0.84, + "mouth": "i", + "source": "B" + }, + { + "start": 0.84, + "end": 0.88, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-sayo-007": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-sayo-007.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 4.94, + "cues": [ + { + "start": 0, + "end": 0.1, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.1, + "end": 0.17, + "mouth": "e", + "source": "C" + }, + { + "start": 0.17, + "end": 0.23, + "mouth": "i", + "source": "B" + }, + { + "start": 0.23, + "end": 0.31, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.31, + "end": 0.57, + "mouth": "i", + "source": "B" + }, + { + "start": 0.57, + "end": 0.92, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.92, + "end": 0.99, + "mouth": "i", + "source": "B" + }, + { + "start": 0.99, + "end": 1.05, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.05, + "end": 1.09, + "mouth": "e", + "source": "C" + }, + { + "start": 1.09, + "end": 1.2, + "mouth": "i", + "source": "B" + }, + { + "start": 1.2, + "end": 1.26, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.26, + "end": 1.34, + "mouth": "u", + "source": "F" + }, + { + "start": 1.34, + "end": 1.42, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.42, + "end": 1.49, + "mouth": "e", + "source": "C" + }, + { + "start": 1.49, + "end": 1.83, + "mouth": "i", + "source": "B" + }, + { + "start": 1.83, + "end": 2.11, + "mouth": "e", + "source": "C" + }, + { + "start": 2.11, + "end": 2.53, + "mouth": "i", + "source": "B" + }, + { + "start": 2.53, + "end": 2.81, + "mouth": "e", + "source": "C" + }, + { + "start": 2.81, + "end": 2.88, + "mouth": "i", + "source": "B" + }, + { + "start": 2.88, + "end": 3.02, + "mouth": "e", + "source": "C" + }, + { + "start": 3.02, + "end": 3.1, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.1, + "end": 3.22, + "mouth": "i", + "source": "B" + }, + { + "start": 3.22, + "end": 3.33, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.33, + "end": 3.39, + "mouth": "e", + "source": "C" + }, + { + "start": 3.39, + "end": 3.51, + "mouth": "i", + "source": "B" + }, + { + "start": 3.51, + "end": 3.74, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.74, + "end": 4, + "mouth": "i", + "source": "B" + }, + { + "start": 4, + "end": 4.07, + "mouth": "e", + "source": "C" + }, + { + "start": 4.07, + "end": 4.14, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.14, + "end": 4.2, + "mouth": "i", + "source": "B" + }, + { + "start": 4.2, + "end": 4.26, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.26, + "end": 4.42, + "mouth": "e", + "source": "C" + }, + { + "start": 4.42, + "end": 4.84, + "mouth": "i", + "source": "B" + }, + { + "start": 4.84, + "end": 4.94, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-zunda-011": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-zunda-011.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 6.49, + "cues": [ + { + "start": 0, + "end": 0.12, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.12, + "end": 0.99, + "mouth": "i", + "source": "B" + }, + { + "start": 0.99, + "end": 1, + "mouth": "rest", + "source": "X" + }, + { + "start": 1, + "end": 1.06, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.06, + "end": 1.12, + "mouth": "e", + "source": "C" + }, + { + "start": 1.12, + "end": 1.2, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.2, + "end": 1.39, + "mouth": "e", + "source": "C" + }, + { + "start": 1.39, + "end": 1.44, + "mouth": "o", + "source": "E" + }, + { + "start": 1.44, + "end": 1.62, + "mouth": "u", + "source": "F" + }, + { + "start": 1.62, + "end": 1.7, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.7, + "end": 1.97, + "mouth": "i", + "source": "B" + }, + { + "start": 1.97, + "end": 2.05, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.05, + "end": 2.2, + "mouth": "e", + "source": "C" + }, + { + "start": 2.2, + "end": 2.24, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.24, + "end": 2.32, + "mouth": "i", + "source": "B" + }, + { + "start": 2.32, + "end": 2.58, + "mouth": "rest", + "source": "X" + }, + { + "start": 2.58, + "end": 2.64, + "mouth": "i", + "source": "B" + }, + { + "start": 2.64, + "end": 2.9, + "mouth": "o", + "source": "E" + }, + { + "start": 2.9, + "end": 2.97, + "mouth": "i", + "source": "B" + }, + { + "start": 2.97, + "end": 3.11, + "mouth": "e", + "source": "C" + }, + { + "start": 3.11, + "end": 3.25, + "mouth": "i", + "source": "B" + }, + { + "start": 3.25, + "end": 3.41, + "mouth": "rest", + "source": "X" + }, + { + "start": 3.41, + "end": 3.49, + "mouth": "i", + "source": "B" + }, + { + "start": 3.49, + "end": 3.63, + "mouth": "e", + "source": "C" + }, + { + "start": 3.63, + "end": 3.77, + "mouth": "i", + "source": "B" + }, + { + "start": 3.77, + "end": 4.09, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.09, + "end": 4.15, + "mouth": "e", + "source": "C" + }, + { + "start": 4.15, + "end": 4.27, + "mouth": "i", + "source": "B" + }, + { + "start": 4.27, + "end": 4.39, + "mouth": "closed", + "source": "A" + }, + { + "start": 4.39, + "end": 4.77, + "mouth": "i", + "source": "B" + }, + { + "start": 4.77, + "end": 4.84, + "mouth": "u", + "source": "F" + }, + { + "start": 4.84, + "end": 4.91, + "mouth": "i", + "source": "B" + }, + { + "start": 4.91, + "end": 5.06, + "mouth": "closed", + "source": "A" + }, + { + "start": 5.06, + "end": 5.34, + "mouth": "i", + "source": "B" + }, + { + "start": 5.34, + "end": 5.55, + "mouth": "e", + "source": "C" + }, + { + "start": 5.55, + "end": 5.63, + "mouth": "closed", + "source": "A" + }, + { + "start": 5.63, + "end": 5.72, + "mouth": "u", + "source": "F" + }, + { + "start": 5.72, + "end": 5.86, + "mouth": "o", + "source": "E" + }, + { + "start": 5.86, + "end": 5.93, + "mouth": "u", + "source": "F" + }, + { + "start": 5.93, + "end": 6, + "mouth": "e", + "source": "C" + }, + { + "start": 6, + "end": 6.07, + "mouth": "o", + "source": "E" + }, + { + "start": 6.07, + "end": 6.14, + "mouth": "i", + "source": "B" + }, + { + "start": 6.14, + "end": 6.35, + "mouth": "e", + "source": "C" + }, + { + "start": 6.35, + "end": 6.42, + "mouth": "i", + "source": "B" + }, + { + "start": 6.42, + "end": 6.49, + "mouth": "rest", + "source": "X" + } + ] + }, + "zundamon-jiron-sayo-008": { + "version": 1, + "source": { + "audio": "audio/zundamon-jiron/lines/zundamon-jiron-sayo-008.wav", + "engine": "rhubarb-lip-sync", + "recognizer": "phonetic" + }, + "duration": 4.05, + "cues": [ + { + "start": 0, + "end": 0.09, + "mouth": "rest", + "source": "X" + }, + { + "start": 0.09, + "end": 0.29, + "mouth": "o", + "source": "E" + }, + { + "start": 0.29, + "end": 0.36, + "mouth": "i", + "source": "B" + }, + { + "start": 0.36, + "end": 0.46, + "mouth": "a", + "source": "D" + }, + { + "start": 0.46, + "end": 0.5, + "mouth": "e", + "source": "C" + }, + { + "start": 0.5, + "end": 0.58, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.58, + "end": 0.76, + "mouth": "i", + "source": "B" + }, + { + "start": 0.76, + "end": 0.84, + "mouth": "closed", + "source": "A" + }, + { + "start": 0.84, + "end": 1.29, + "mouth": "i", + "source": "B" + }, + { + "start": 1.29, + "end": 1.66, + "mouth": "rest", + "source": "X" + }, + { + "start": 1.66, + "end": 1.74, + "mouth": "i", + "source": "B" + }, + { + "start": 1.74, + "end": 1.82, + "mouth": "closed", + "source": "A" + }, + { + "start": 1.82, + "end": 2.12, + "mouth": "e", + "source": "C" + }, + { + "start": 2.12, + "end": 2.2, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.2, + "end": 2.34, + "mouth": "i", + "source": "B" + }, + { + "start": 2.34, + "end": 2.42, + "mouth": "closed", + "source": "A" + }, + { + "start": 2.42, + "end": 2.64, + "mouth": "e", + "source": "C" + }, + { + "start": 2.64, + "end": 2.71, + "mouth": "i", + "source": "B" + }, + { + "start": 2.71, + "end": 2.92, + "mouth": "o", + "source": "E" + }, + { + "start": 2.92, + "end": 2.99, + "mouth": "u", + "source": "F" + }, + { + "start": 2.99, + "end": 3.15, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.15, + "end": 3.47, + "mouth": "i", + "source": "B" + }, + { + "start": 3.47, + "end": 3.55, + "mouth": "closed", + "source": "A" + }, + { + "start": 3.55, + "end": 3.97, + "mouth": "i", + "source": "B" + }, + { + "start": 3.97, + "end": 4.05, + "mouth": "rest", + "source": "X" + } + ] } } } diff --git a/voicevox-remotion-template/src/lib/VQRemotionLib/components/VQCharacterAvatar.tsx b/voicevox-remotion-template/src/lib/VQRemotionLib/components/VQCharacterAvatar.tsx index 9d25e82..9eb4a9c 100644 --- a/voicevox-remotion-template/src/lib/VQRemotionLib/components/VQCharacterAvatar.tsx +++ b/voicevox-remotion-template/src/lib/VQRemotionLib/components/VQCharacterAvatar.tsx @@ -1,6 +1,7 @@ import React from "react"; import {vqIdleAvatarAnimations, vqSpeakingAvatarAnimations} from "../avatarAnimations"; import type { + VQAvatarImageLayout, VQCharacterDefinition, VQMouthResolver, VQMouthShape, @@ -21,6 +22,8 @@ resolveMouth?: VQMouthResolver; fontFamily?: string; imageFilter?: string; + imageFlipX?: boolean; + imageTranslateY?: number; }>; const mouthForAvatar = ({ @@ -59,6 +62,27 @@ ); }; +// 用途: 立ち絵の頭幅メタデータから追加スケールを算出する。 +// 使用方法: VQCharacterAvatar 内で imageLayout と実表示幅を渡し、画像transformのscaleへ反映する。 +// オプションや引数詳細: sourceWidth/sourceHeadWidth/targetHeadWidth が揃わない場合は scale のみを使う。 +const imageScaleForLayout = ( + imageLayout: VQAvatarImageLayout | undefined, + imageWidth: number | string +) => { + const renderedHeadWidth = + typeof imageWidth === "number" && + imageLayout?.sourceWidth && + imageLayout?.sourceHeadWidth + ? (imageLayout.sourceHeadWidth * imageWidth) / imageLayout.sourceWidth + : undefined; + const headWidthScale = + renderedHeadWidth && imageLayout?.targetHeadWidth + ? imageLayout.targetHeadWidth / renderedHeadWidth + : 1; + + return (imageLayout?.scale ?? 1) * headWidthScale; +}; + export const VQCharacterAvatar = ({ characterId, character, @@ -72,6 +96,8 @@ resolveMouth, fontFamily = "system-ui, sans-serif", imageFilter = "drop-shadow(0 18px 40px rgba(31, 42, 68, 0.22))", + imageFlipX, + imageTranslateY, }: VQCharacterAvatarProps) => { const {avatar} = character; const scale = hasMultipleCharacters ? 0.82 : focused ? 0.94 : 0.9; @@ -91,9 +117,12 @@ focused, hasMultipleCharacters, }); - const imageTranslateY = avatar.imageLayout?.translateY ?? 0; - const imageScaleX = avatar.imageLayout?.flipX ? -1 : 1; - const imageTransform = `translateY(${imageTranslateY}px) scaleX(${imageScaleX})`; + const resolvedImageTranslateY = + imageTranslateY ?? avatar.imageLayout?.translateY ?? 0; + const imageScaleX = (imageFlipX ?? avatar.imageLayout?.flipX) ? -1 : 1; + const imageWidth = avatar.imageLayout?.width ?? 320; + const imageScale = imageScaleForLayout(avatar.imageLayout, imageWidth); + const imageTransform = `translateY(${resolvedImageTranslateY}px) scale(${imageScale}) scaleX(${imageScaleX})`; const nameplatePosition = avatar.nameplatePosition ?? "bottom"; const showNameplate = nameplatePosition !== "none"; const mouth = mouthForAvatar({ @@ -142,7 +171,7 @@ imagePath={avatar.imagePath} mouthImageDir={avatar.mouthImageDir} mouth={mouth} - width={avatar.imageLayout?.width ?? 320} + width={imageWidth} height={avatar.imageLayout?.height} maxHeight={avatar.imageLayout?.maxHeight ?? 360} filter={imageFilter} diff --git a/voicevox-remotion-template/src/lib/VQRemotionLib/components/VQCharacterStage.tsx b/voicevox-remotion-template/src/lib/VQRemotionLib/components/VQCharacterStage.tsx index 2d15440..dfe0d28 100644 --- a/voicevox-remotion-template/src/lib/VQRemotionLib/components/VQCharacterStage.tsx +++ b/voicevox-remotion-template/src/lib/VQRemotionLib/components/VQCharacterStage.tsx @@ -16,6 +16,8 @@ fontFamily?: string; stageStyle?: React.CSSProperties; avatarImageFilter?: string; + avatarFlipXByCharacter?: Partial>; + avatarTranslateYByCharacter?: Partial>; }>; export const VQCharacterStage = ({ @@ -31,6 +33,8 @@ fontFamily, stageStyle, avatarImageFilter, + avatarFlipXByCharacter, + avatarTranslateYByCharacter, }: VQCharacterStageProps) => { const hasMultipleCharacters = visibleCharacters.length > 1; @@ -62,6 +66,8 @@ resolveMouth={resolveMouth} fontFamily={fontFamily} imageFilter={avatarImageFilter} + imageFlipX={avatarFlipXByCharacter?.[characterId]} + imageTranslateY={avatarTranslateYByCharacter?.[characterId]} /> ))} diff --git a/voicevox-remotion-template/src/lib/VQRemotionLib/timeline.ts b/voicevox-remotion-template/src/lib/VQRemotionLib/timeline.ts index d66502b..7fb95fa 100644 --- a/voicevox-remotion-template/src/lib/VQRemotionLib/timeline.ts +++ b/voicevox-remotion-template/src/lib/VQRemotionLib/timeline.ts @@ -61,6 +61,26 @@ position: Position; }>; +export type VQStandeeFacingDirection = "left" | "right"; + +export type VQStandeeFacingDirectionEvent< + CharacterId extends string = string +> = Readonly<{ + type: "standeeFacingDirection"; + id: string; + character: CharacterId; + direction: VQStandeeFacingDirection; +}>; + +export type VQStandeeVerticalOffsetEvent< + CharacterId extends string = string +> = Readonly<{ + type: "standeeVerticalOffset"; + id: string; + character: CharacterId; + translateY: number; +}>; + export type VQWaitEvent = Readonly<{ type: "wait"; id: string; @@ -131,6 +151,8 @@ | VQClearStillEvent | VQClearVideoEvent | VQStandeePositionEvent + | VQStandeeFacingDirectionEvent + | VQStandeeVerticalOffsetEvent | VQWaitEvent | VQAudioEvent | VQVideoEvent @@ -146,6 +168,8 @@ | VQClearStillEvent | VQClearVideoEvent | VQStandeePositionEvent + | VQStandeeFacingDirectionEvent + | VQStandeeVerticalOffsetEvent | VQWaitEvent | VQWaitEventInput | VQAudioEvent @@ -243,6 +267,34 @@ position, }); +// 用途: キャラクター立ち絵の左右の向きを切り替えるタイムラインイベントとして定義する。 +// 使用方法: script.ts の timeline 内で standeeFacingDirection("id", "zundamon", "right") として呼び出す。 +// オプションや引数詳細: direction は "left" / "right" を指定し、描画側で imageLayout.flipX へ反映する。 +export const standeeFacingDirection = ( + id: string, + character: CharacterId, + direction: VQStandeeFacingDirection +): VQStandeeFacingDirectionEvent => ({ + type: "standeeFacingDirection", + id, + character, + direction, +}); + +// 用途: キャラクター立ち絵の縦方向オフセットを切り替えるタイムラインイベントとして定義する。 +// 使用方法: script.ts の timeline 内で standeeVerticalOffset("id", "zundamon", -120) として呼び出す。 +// オプションや引数詳細: translateY は描画側の imageLayout.translateY を上書きするpx値で、負数ほど上へ移動する。 +export const standeeVerticalOffset = ( + id: string, + character: CharacterId, + translateY: number +): VQStandeeVerticalOffsetEvent => ({ + type: "standeeVerticalOffset", + id, + character, + translateY, +}); + // 用途: 何も発話しない待機時間をタイムラインイベントとして定義する。 // 使用方法: script.ts の timeline 内で wait(1) のように秒数だけを指定し、defineVQTimelineでIDを確定する。 // オプションや引数詳細: durationSeconds は待機秒数で、IDはtimeline単位で wait-001 から自動採番される。 @@ -288,6 +340,8 @@ event.type !== "clearStill" && event.type !== "clearVideo" && event.type !== "standeePosition" && + event.type !== "standeeFacingDirection" && + event.type !== "standeeVerticalOffset" && event.type !== "audio" && (event.type !== "video" || event.placement === undefined || diff --git a/voicevox-remotion-template/src/lib/VQRemotionLib/types.ts b/voicevox-remotion-template/src/lib/VQRemotionLib/types.ts index eb1e455..6c420b4 100644 --- a/voicevox-remotion-template/src/lib/VQRemotionLib/types.ts +++ b/voicevox-remotion-template/src/lib/VQRemotionLib/types.ts @@ -30,6 +30,10 @@ width?: number | string; maxHeight?: number | string; height?: number | string; + sourceWidth?: number; + sourceHeadWidth?: number; + targetHeadWidth?: number; + scale?: number; translateY?: number; flipX?: boolean; }>; diff --git a/voicevox-remotion-template/src/root.tsx b/voicevox-remotion-template/src/root.tsx index 582937b..d16b078 100644 --- a/voicevox-remotion-template/src/root.tsx +++ b/voicevox-remotion-template/src/root.tsx @@ -20,6 +20,11 @@ totalZundamonReworkStandeeDemoDurationInFrames, ZUNDAMON_REWORK_STANDEE_DEMO_FPS, } from "./data/zundamon-rework-standee-demo/timing"; +import {ZundamonJiron} from "./zundamon-jiron"; +import { + ZUNDAMON_JIRON_FPS, + totalZundamonJironDurationInFrames, +} from "./data/zundamon-jiron/timing"; export const Root: React.FC = () => { return ( @@ -60,6 +65,16 @@ width={1280} height={720} /> + ); }; diff --git a/voicevox-remotion-template/src/standee-sets.ts b/voicevox-remotion-template/src/standee-sets.ts index be16009..a6e844b 100644 --- a/voicevox-remotion-template/src/standee-sets.ts +++ b/voicevox-remotion-template/src/standee-sets.ts @@ -8,6 +8,10 @@ export type StandeeImageLayout = Readonly<{ width?: number; maxHeight?: number; + sourceWidth?: number; + sourceHeadWidth?: number; + targetHeadWidth?: number; + scale?: number; translateY?: number; flipX?: boolean; }>; @@ -89,6 +93,17 @@ translateY: 0, }, }, + "sayo_by_sayonaka": { + kind: "sayo", + description: "小夜の立ち絵(SAYONAKA 素材)", + imagePath: "image/sayo_by_sayonaka/variants/sayo_001_neutral.png", + mouthImageDir: "image/sayo_by_sayonaka/rhubarb-mouths", + imageLayout: { + width: 520, + maxHeight: 720, + translateY: 0, + }, + }, } as const satisfies Record; export type StandeeSetId = keyof typeof standeeSets; diff --git a/voicevox-remotion-template/src/zundamon-jiron.tsx b/voicevox-remotion-template/src/zundamon-jiron.tsx new file mode 100644 index 0000000..b99c062 --- /dev/null +++ b/voicevox-remotion-template/src/zundamon-jiron.tsx @@ -0,0 +1,310 @@ +import React from "react"; +import { + AbsoluteFill, + interpolate, + Sequence, + spring, + useCurrentFrame, + useVideoConfig, +} from "remotion"; +import { + characters, + compositionTitle, + initialVisibleCharacters, + timeline, + type CharacterId, + type TimelineEvent, +} from "./data/zundamon-jiron/script"; +import { + ZUNDAMON_JIRON_GAP_FRAMES, + durationForTimelineEvent, + audioFileForSpeech, + hasAudioForSpeech, +} from "./data/zundamon-jiron/timing"; +import {roundedFontFamily} from "./fonts"; +import { + VQCaptionOverlay, + VQCharacterStage, + VQSpeechOverlay, + VQTimelineAudio, + VQWarmGradientBackground, + type VQAudioEvent, + type VQMouthResolver, +} from "./lib/VQRemotionLib"; +import {getMouthForSpeechFrame} from "./lipsync/manifest"; + +type ScheduledTimelineEvent = Readonly<{ + event: TimelineEvent; + from: number; + durationInFrames: number; + visibleCharacters: CharacterId[]; + focusedCharacter?: CharacterId; + avatarFlipXByCharacter: Partial>; + avatarTranslateYByCharacter: Partial>; +}>; + +const clampInterpolation = { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", +} as const; + +const subtitleOptions = { + fontFamily: roundedFontFamily, + fontSize: 36, + lineHeight: 1.4, + backgroundColor: "rgba(255, 255, 255, 0.88)", +} as const; + +const flipXForFacingDirection = (direction: "left" | "right") => + direction === "left"; + +const characterForEvent = (event: TimelineEvent) => + "character" in event ? event.character : undefined; + +const doesEventAdvanceTimeline = (event: TimelineEvent) => + event.type !== "audio" && + event.type !== "standeeFacingDirection" && + event.type !== "standeeVerticalOffset"; + +const audioSequenceDuration = ( + scheduledAudio: ScheduledTimelineEvent & Readonly<{event: VQAudioEvent}>, + timelineEndFrame: number +) => { + const hasExplicitDuration = + scheduledAudio.event.durationFrames !== undefined || + scheduledAudio.event.durationSeconds !== undefined; + + return scheduledAudio.event.playback === "loop" && !hasExplicitDuration + ? Math.max(0, timelineEndFrame - scheduledAudio.from) + : scheduledAudio.durationInFrames; +}; + +// 用途: script.ts の timeline をフレーム単位の表示スケジュールへ変換する。 +// 使用方法: Composition コンポーネント内で fps を渡して呼び出す。 +// オプションや引数詳細: character を持つイベントだけ表示対象とフォーカスに反映する。 +const scheduleTimeline = (fps: number): ScheduledTimelineEvent[] => { + let cursor = 0; + const visibleCharacters = new Set(initialVisibleCharacters); + const avatarFlipXByCharacter: Partial> = {}; + const avatarTranslateYByCharacter: Partial> = {}; + let focusedCharacter: CharacterId | undefined = initialVisibleCharacters[0]; + + return timeline.map((event, index) => { + const eventCharacter = characterForEvent(event); + if (eventCharacter) { + visibleCharacters.add(eventCharacter); + focusedCharacter = eventCharacter; + } + + if (event.type === "standeeFacingDirection") { + avatarFlipXByCharacter[event.character] = flipXForFacingDirection( + event.direction + ); + } + + if (event.type === "standeeVerticalOffset") { + avatarTranslateYByCharacter[event.character] = event.translateY; + } + + const durationInFrames = durationForTimelineEvent(event, fps); + const scheduledEvent = { + event, + from: cursor, + durationInFrames, + visibleCharacters: Array.from(visibleCharacters), + focusedCharacter, + avatarFlipXByCharacter: {...avatarFlipXByCharacter}, + avatarTranslateYByCharacter: {...avatarTranslateYByCharacter}, + }; + + if (doesEventAdvanceTimeline(event) && durationInFrames > 0) { + cursor += durationInFrames; + } + if ( + doesEventAdvanceTimeline(event) && + durationInFrames > 0 && + index < timeline.length - 1 + ) { + cursor += ZUNDAMON_JIRON_GAP_FRAMES; + } + + return scheduledEvent; + }); +}; + +// 用途: 現在フレームに対応するタイムライン区間を取得する。 +// 使用方法: Composition の毎フレーム描画で activeSegment を求める。 +// オプションや引数詳細: 最後に開始した区間を返すため、ギャップ中も直前の表示状態を維持する。 +const activeSegmentForFrame = ( + scheduledEvents: ScheduledTimelineEvent[], + frame: number +) => { + let activeSegment = scheduledEvents[0]; + + for (const scheduledEvent of scheduledEvents) { + if (frame >= scheduledEvent.from) { + activeSegment = scheduledEvent; + } else { + break; + } + } + + return activeSegment; +}; + +const resolveMouth: VQMouthResolver = ({ + speechId, + speakingLocalFrame, + fps, +}) => getMouthForSpeechFrame(speechId, speakingLocalFrame, fps); + +const Title: React.FC> = ({progress}) => { + const opacity = interpolate(progress, [0, 1], [0, 1], clampInterpolation); + const translateY = interpolate(progress, [0, 1], [-30, 0], clampInterpolation); + + return ( +
+ {compositionTitle} +
+ ); +}; + +const TimelineOverlay: React.FC> = ({ + event, +}) => { + if (event.type === "say") { + const character = characters[event.character]; + + return ( + + ); + } + + if (event.type === "audio") { + return ; + } + + if (event.type !== "show") { + return null; + } + + return ( + + ); +}; + +const keyForEvent = (event: TimelineEvent, index: number) => { + if ("id" in event) { + return event.id; + } + + return `${event.type}-${event.character}-${index}`; +}; + +export const ZundamonJiron: React.FC = () => { + const frame = useCurrentFrame(); + const {fps} = useVideoConfig(); + const scheduledEvents = scheduleTimeline(fps); + const activeSegment = activeSegmentForFrame(scheduledEvents, frame); + const isInsideActiveSegment = + frame < activeSegment.from + activeSegment.durationInFrames; + + const titleProgress = spring({ + frame, + fps, + config: {damping: 18, mass: 0.6}, + }); + const activeSpeech = + isInsideActiveSegment && activeSegment.event.type === "say" + ? activeSegment.event + : undefined; + const speakingCharacter = activeSpeech?.character; + const speakingLocalFrame = activeSpeech ? frame - activeSegment.from : 0; + const timelineEndFrame = scheduledEvents.reduce( + (endFrame, scheduledEvent) => + Math.max(endFrame, scheduledEvent.from + scheduledEvent.durationInFrames), + 0 + ); + + const sequences = scheduledEvents.map((scheduledEvent, index) => + ( + scheduledEvent.event.type === "audio" + ? audioSequenceDuration( + scheduledEvent as ScheduledTimelineEvent & + Readonly<{event: VQAudioEvent}>, + timelineEndFrame + ) + : scheduledEvent.durationInFrames + ) > 0 ? ( + , + timelineEndFrame + ) + : scheduledEvent.durationInFrames + } + premountFor={Math.min(fps, scheduledEvent.from)} + > + + + ) : null + ); + + return ( + + + + <VQCharacterStage + characters={characters} + visibleCharacters={activeSegment.visibleCharacters} + focusedCharacter={ + isInsideActiveSegment ? activeSegment.focusedCharacter : undefined + } + speakingCharacter={speakingCharacter} + speakingSpeechId={activeSpeech?.id} + speakingLocalFrame={speakingLocalFrame} + frame={frame} + fps={fps} + resolveMouth={resolveMouth} + fontFamily={roundedFontFamily} + stageStyle={{paddingBottom: 130}} + avatarFlipXByCharacter={activeSegment.avatarFlipXByCharacter} + avatarTranslateYByCharacter={activeSegment.avatarTranslateYByCharacter} + /> + {sequences} + </AbsoluteFill> + ); +};