構成主義解説動画 · ワークフロー
1本の時間軸、3つの下流工程がすべてそこから派生する。
① 原稿 ② TTSで軸を固定 ③ BGM変奏 ④ 完成品
文章 ──→ 解説音声(時間軸)──→ LMMS音楽(階層)──→ Remotion映像 + 音声統合 + サムネイル
│ ▲
└──── 階層境界の時間ポイント ─────┘
- 映像のフレーム数はTTSの自然な話速から生成される(逆に話速を圧縮するのではない)
- BGMの変奏ポイントは解説の階層切り替えポイントに合わせる
- 完成品は解説音声を基準に同期し、BGMはあくまで背景として機能する
0. はじめに:まずパス契約を読む(このステップは必須)
このスキルはツールを自ら探しません。すべてのパスは環境設定スキルが書き出した契約から取得します。
$VW_CONTRACT (デフォルト ~/.config/ideaform/toolchain.json)
CONTRACT="${VW_CONTRACT:-$HOME/.config/ideaform/toolchain.json}"
source "$(dirname "$CONTRACT")/env.sh" # VW_NODE / VW_VENV / VW_FFMPEG / … をエクスポート
作業開始前に必ず3つ確認すること:
- 契約ファイルは存在するか?存在しない → ユーザーに
ideaform-setupスキルを先に実行してもらう。 schema_versionはこのスキルが認識するバージョンか?一致しない → 環境設定スキルの再実行を要求する。verified_atがnullか?はい → 「環境がスモークテストを通過していません」 と警告し、先に修正してから作業を進める。 さらにtools.lmms.stkdir_okがtrueである必要がある。そうでないとBGMは無音でエラーも出ない。
主要フィールドの早見表:
| 用途 | 契約フィールド |
|---|---|
| TTSスクリプトの実行(edge-ttsはvenv内) | tools.venv.python |
| 音声・映像の合成 | tools.ffmpeg.path / tools.ffprobe.path |
| 映像のレンダリング | tools.remotion.project / tools.remotion.browser.resolved |
| BGMのレンダリング | tools.lmms.apprun |
| 一時ファイルの置き場所(大容量ディスク必須) | tmpdir |
⚠️ 契約内の
tmpdirはオプションではありません。 ローカルの/tmpは10MBしかなく、Remotionのパッケージングやpipのインストールでそこに書き込むとENOSPCエラーになります。すべてのRemotionコマンドにはTMPDIRを指定してください。
⚠️ パスは一切
resolve(シンボリックリンク解決)しないでください。 管理されたツールチェーンのパスにはシンボリックリンクが含まれており、venvのインタプリタを解決するとvenvコンテキストが失われ、すべてのパッケージがimportできなくなります。詳細はリポジトリルートのdocs/03-パス契約.mdを参照。
1. 4段階の概要
| 段階 | 何をする | 入力 | 出力(すべてワークスペースに格納) | 参考 |
|---|---|---|---|---|
| ① 原稿 | ユーザーの入力を階層化された解説原稿に変換 | ユーザー入力 | script.md + 台词.txt(階層がマークされている) | references/stage1-script.md |
| ② TTSで軸を固定 | 自然な話速を計測 → タイムラインを作成 → ナレーション音声を生成 | 台词.txt | plan.json / timeline.ts / voice.wav | references/stage2-tts.md |
| ③ BGM変奏 | 階層境界 → 変奏計画 → LMMSで音声出力 | plan.json + 階層マーク | bgm-plan.json / bgm.wav | references/stage3-bgm.md |
| ④ 完成品 | Remotionで映像配置 + 音声統合 + レンダリング + サムネイル | timeline.ts + 音声 + bgm-plan.json | final.mp4 + cover.png | references/stage4-render.md · references/cover.md |
スタイルの統一は references/constructivist-style.md が管理し、
映像とサムネイルで同じカラーパレット・フォント・グリッドを共有するため、自然に統一感が生まれます。
2. フェーズ1 · 原稿(階層化は必須)
ユーザーが与えるのは入力(テーマ / 資料 / 口頭でのアイデア)です。出力は以下:
script.md—— 完全な原稿で、論述階層を明示的にマーク台词.txt—— 1行1文のナレーション台本。TTSが使用
階層化は見た目のためではなく、フェーズ3への入力です。 階層マークがなければ、BGM変奏の基点がありません。
## 階層1 · 導入
これは「原稿=時間軸」の第一層です。
解説音声こそが、このパイプライン全体の時間軸です。
## 階層2 · 展開
映像のフレーム数は、音声によって決まります。その逆ではありません。
...
- 1文 = 1つの意味単位。1文に2つのことを詰め込まない(TTSは文単位でタイムラインを組むため、詰め込むとスロットが長くなる)
- 1文は8~30文字を推奨。長すぎる場合は分割、短すぎる場合は結合
- 句読点は「。」「?」で文を区切り、TTSの区切りを助ける
詳細は references/stage1-script.md を参照。
3. フェーズ2 · TTSで軸を固定(順序は絶対)
"$VW_VENV/bin/python" skills/ideaform/scripts/make-tts-track.py \
--lines 台词.txt --fps 30 --audio \
--plan-out out/plan.json --ts-out out/timeline.ts
まず音声、その後に軸を固定。 タイムラインはTTSの実際の話速から生成されます:
① 台本を書く
② make-tts-track.py --lines 台词.txt --fps 30 --audio ← 自然な話速を計測し、タイムラインを出力
③ 出力された TOTAL_FRAMES を使ってRemotionのcompositionを記述 ← フレーム数はここで確定
④ レンダリング
逆順の代償(実測):先に絵コンテで43本のタイムコードを固定し、TTSの自然な話速の合計が212.90秒だったのに対し、動画は174.80秒しかなかった → 1.218倍に強制圧縮。この妥協は完全に回避できたはずです。
同じスクリプトで逆のことも可能(既存のSRTやタイムコードが固定されている場合、2段階の速度調整で対応)ですが、
それは救済措置であり、第一選択肢ではありません。詳細は references/stage2-tts.md を参照。
⚠️ 「完成品は何分ですか」とユーザーに尋ねることを禁止します。 尺 = 話速 × 台詞量 であり、出力であって入力ではありません。 作業開始時に「標準版X分」などの選択肢を尋ねたり提示したりしてはいけません。報告するのは「この原稿では、実測でX分Y秒です」のみ。 ユーザーが長く/短くしたい場合 → 変更するのは原稿であり、時間ではありません。(2026-10-15 桂花編で経験、E16に記録)
出力:plan.json 内の各エントリに start/end/dur/frameStart/frameEnd が含まれます。
timeline.ts はそのまま cp でRemotionプロジェクトの src/timeline.ts として使用可能。
4. フェーズ3 · BGM変奏(このワークフローの肝)
要点:論述が新しい階層に入るたびに、音楽の形態が変わる —— ただし曲は変わらない。
# ① 原稿の階層をCUE番号に変換し、各階層のナラティブ機能をマーク
"$VW_VENV/bin/python" skills/ideaform/scripts/bgm-plan.py \
--plan out/plan.json \
--layers "1:present,4:develop,6:resolve" \
--key "A minor" --out out/bgm-plan.json
# ② bgm-plan.json に従ってLMMSプロジェクトを生成しレンダリング(electronic-music-production の composer.py を使用)
# 契約内の tools.lmms.apprun でレンダリング。bpm と timeline lp1pos は mmp_hints に従って記述すること
6つのナラティブ機能(各階層から1つ選択):
| 機能 | 意図 | 変更内容 |
|---|---|---|
present | 提示・導入 | モチーフのみ、単声部、余白を残す |
develop | 展開・深化 | +ベース +グルーブ、モチーフの模倣進行 |
intensify | 強調・対立 | 全声部、1オクターブ上げ、借用和音、強度を上げる |
contrast | 対照・転換 | 音量を上げずに質感を変える:声部削除、音色変更、旋法転換 |
resolve | 収束・回帰 | 声部削減、主調に戻る、モチーフの拡大 |
sustain | 継続・遷移 | 変更なし(階層が変わっていないのに無闇に変奏すると、コントラストが希薄になる) |
鉄則:
- アイデンティティ要素は変えてはいけない —— モチーフの核、調の中心、基本テンポ。変えると「変奏」ではなく「曲替わり」になる。
- 各セクションで少なくとも1つの形態要素を変更すること。
bgm-plan.mdには各セクションの「変奏の証拠」を列挙し、 隣接する2セクションで証拠が空なら、階層マークが漏れていることを示す。 - 変奏ポイントは必ず解説の階層境界に合わせる(
--lead-inで音楽を少し早めに開始することも可能)。
完全な方法とLMMS側での実装は references/stage3-bgm.md を参照。
5. フェーズ4 · 完成品
5.1 映像:解説音声を時間軸として
cp out/timeline.ts <remotionプロジェクト>/src/timeline.ts # フレーム数/シーン境界はすべてCUESから導出
cd <remotionプロジェクト>
export TMPDIR="$VW_TMPDIR" # ★ 必須
npx remotion studio # まずユーザーにプレビューを見せる
映像側のシーン境界はフレーム番号を手書きせず、CUES から導出:
export const SCENES = {
s1: { from: 0, dur: CUES[2].start },
s2: { from: CUES[2].start, dur: CUES[4].start - CUES[2].start },
} as const;
構造式・シグナル伝達経路・メカニズム図 → すべて決定論的に生成し、絶対にAIで画像を生成しない。
AIが化学構造を描画すると、幻覚で結合を誤作し、しかもエラーが見つけにくい——こうした素材は再現可能で検証可能でなければなりません。
scripts/gen-structures.py を使用:SMILES → RDKitで決定論的な描写
→ フレーム駆動のRemotionコンポーネント。
python3 scripts/gen-structures.py --tsv structures.tsv --out structures
# → svg/*.svg + components/*.tsx(アニメーション付き) + manifest.json
# → 分子式のオフライン検証(RDKit CalcMolFormula)、不一致なら非ゼロ終了、直接 `&&` で連結可能
- 配色は RDKit公式オプション(
useBWAtomPalette/singleColourBonds/setSymbolColour)を使用し、 SVGの色を正規表現で後処理しない——それは車輪の再発明であり、RDKitのバージョンアップで壊れます(エラー集E20参照)。 - 生成されたコンポーネントはCSS変数
--mol-dash/--mol-textを使用して線のアニメーションを駆動するため、外側で必ずcolorを設定すること。 - グリッド図・骨格ハイライト・3D配座・タンパク質-リガンド相互作用図など、より広範な分子可視化が必要な場合は、 既存のスキルを探す(§7参照)。ビデオパイプライン内に無理に詰め込まないこと。
5.2 音声統合(2つの方法、デフォルトはA)
A. Remotion統合(推奨) —— composition内で <Audio> を使用。レンダリングされたmp4には音声が含まれます:
<Audio src={staticFile("voice.wav")} />
<Audio src={staticFile("bgm.wav")} volume={0.1694} />
volume の値は感覚で決めず、実測RMSから逆算:
bash scripts/mix-av.sh --plan-only --voice voice.wav --bgm bgm.wav --drop 9
# ナレーション RMS = -23.46 dBFS BGM RMS = -17.04 dBFS
# → BGMに -15.42 dB の減衰が必要 → volume = 0.1694
B. 後合成(動画はすでにレンダリング済 / ナレーションを再録したい場合):
bash scripts/mix-av.sh --video silent.mp4 --voice voice.wav --bgm bgm.wav --out final.mp4
⚠️ 方法Bでは明示的に
-map 0:v:0 -map 1:a:0を使用すること。 Remotionが出力するmp4には1本の無音AACトラックが含まれています。-mapを追加しないと、ffmpegは自動的にそのトラックを選択し、結果として終了コード0、尺は正しい、再生できるが、音声がない状態になります。mix-av.shにはすでにこの-mapが組み込まれています。
5.3 レンダリングとサムネイル
export TMPDIR="$VW_TMPDIR"
npx remotion render <CompositionId> out/video.mp4 --concurrency=3
npx remotion still Cover out/cover.png --props='{...}' # サムネイル:静的なcompositionを使用。アニメーションは避けること
- 長尺動画は分割レンダリング:7分の動画を1回でレンダリングすると、メモリ不足でタイムアウトする可能性があります。
--frames=A-Bで分割 →concat -c copyでロスレス結合。 - サムネイルは
durationInFrames={1}の静的なcompositionを使用。コンポーネント内では**useCurrentFrameを使用しないこと**(still時は常に0で、アニメーションの初期値により要素が非表示になります)。 - サムネイルのレイアウトは
references/cover.mdを、スタイル仕様はreferences/constructivist-style.mdを参照。
6. 目視なしでの納品:各フェーズでチェッカーを通過すること
モデルは画像を見られず、音声を聞けません。 したがって「レンダリング成功」≠「映像が正しい、音声が正しい」。 各ステップで数値による実測検証を行い、基準を満たさなければ納品を拒否します:
# 音声セルフチェック(TTS音声、BGM):ピーク/RMS/クリッピング/長さのずれ
# 映像セルフチェック:ピクセルカラー / 背景の純粋度 / テキストピクセル数
# --layout は正しく選択すること:cover=サムネイルレイアウト;scene=動画シーン(デフォルト)。
# サンプリングストリップはレイアウトに応じて決まるため、間違えると正しいサムネイルでも不合格になる(05-トラブルシューティング D2参照)。
python3 scripts/verify-video.py cover out/cover.png --layout cover
# 完成品セルフチェック:フレーム数 / 尺 / 音声ビットレート(無音トラックを防止)
# ffprobeの優先順位:VW_FFPROBE → パス契約 toolchain.json → システム
VW_FFPROBE="$VW_FFPROBE" python3 scripts/verify-video.py video out/final.mp4 --frames 732 --fps 30
スクリプトは非ゼロ終了コードで失敗を示すため、&& でパイプラインに直接組み込めます。
よく使う判定基準の早見表:
| 対象 | 判定基準 | しきい値 |
|---|---|---|
| 音声 | 無音でない | ピーク > 1000 かつ RMS > 100 |
| 音声 | クリッピングなし | フルスケールサンプル < 0.01% |
| 音声 | 尺の一致 | 計画とのずれ < 50 ms |
| サムネイル | サイズ | 1920×1080 |
| サムネイル | テキストレンダリング | テキスト領域のインクピクセル > 300 |
| サムネイル | 軸線上にテキストがない | タイトル領域内の赤ピクセル < 60 |
| 完成品 | フレーム数 | 計画値と完全一致 |
| 完成品 | 音声が無音でない | ビットレート > 64000 bps |
| BGM | 背景レベル | ナレーションより8–12 dB低い |
7. このスキルと連携する他の機能
| 必要なもの | 使用するもの |
|---|---|
| 環境検出 / 補完インストール / 契約 | ideaform-setup(1回限り) |
| 新しいオープンソースツールの既成スキル(取ってくる:GitHubから収穫 → セキュリティチェック → 保存) | ideaform-setup の scripts/harvest-skills.py <ツール名> |
| 化学構造式・経路図の決定論的なグラフィック | このスキルの scripts/gen-structures.py(RDKit → SVG → Remotionコンポーネント) |
| より広範な分子可視化(グリッド図 / 骨格ハイライト / 3D配座 / タンパク質-リガンド) | 外部の既成スキル:synthetic-sciences/openscience の molecule-visualization(Apache-2.0)、ghutchis/chem-skill(MIT) |
LMMSプロジェクトのフォーマット、プラグインパラメータ、eldata エンベロープ | electronic-music-production + その references/05 |
| 作曲法(和声、モチーフ、非聴覚作曲、創発) | electronic-music-production の references/06–10 |
| Remotionの文法とレンダリングの詳細 | remotion-best-practices |
| ピクセルレベルの目視なし検証の完全な方法 | headless-visual-verify |
| WSL環境の落とし穴(/mnt、ghの取得、音声パス) | wsl-pitfalls |
8. 納品チェックリスト
1本の動画が完成したら、ワークスペースに以下が存在する必要があります:
<ワークスペース>/
├── script.md 階層化された原稿
├── 台词.txt ナレーション台本
├── out/
│ ├── plan.json 時間軸(各CUEの秒/フレーム)
│ ├── timeline.ts Remotionにそのまま取り込めるタイムライン
│ ├── voice.wav ナレーション音声マスター
│ ├── bgm-plan.json 変奏計画 + bgm-plan.md(変奏の証拠)
│ ├── bgm.wav BGM音声
│ ├── video.mp4 無音映像(または音声統合済みの完成品)
│ ├── final.mp4 ★ 最終完成品
│ └── cover.png ★ サムネイル
└── .tts/ TTSキャッシュ(保持。再実行は数秒で完了)
納品前に必ずセクション6のすべてのチェックを実行し、どの項目が合格し、どの項目が未テストかを正直に報告すること。
9. 自己進化:問題が起きたら記録する
このワークフローは変更可能な複合スキルです——使ううちに、あなたの制作リズムに合うように進化していきます:
- 問題が発生し、修正が完了したら → リポジトリルートの
docs/06-エラー集.mdに記録(6カラム形式、連番)。 ⚠️skills/ideaform/の下に新たにdocs/を作成しない——それは2冊目の台帳となり、本家の連番と衝突します(2026-10-15に経験、E19に記録)。 - ある問題がスクリプトやドキュメントで恒久的に修正された(回避策ではなく)場合 → 該当エントリのステータスを
根治済みに変更し、修正方法を明記する。 - 同じ種類の問題が2回目に発生した場合 → 3つ目のエントリを追加するのではなく、防御策(チェッカー / デフォルト値 / 契約)を直接スクリプトに組み込む。
- 新しいオープンソースツールを導入する場合 → まず収穫してから作業を開始:
harvest-skills.py <ツール名>でコミュニティの成熟したスキルを探す (フォーク保護あり、ローカルで変更したものを上書きしない)。適切なものが見つからなければ、自分で作成する。収穫したスキルで問題が発生した場合も、同様に記録する。 - セッション終了前に確認:「修正したが記録していない」ものがないか?