ビートシンク
scripts/ 内のスクリプト:
beats.py MUSIC --out beats.json [--start S --end E]— テンポ、ビート、ダウンビート予測、強いオンセット(ヒット)、1秒あたりのRMSエネルギーを出力します。timemap.py— 可変速度タイムマップ(スピードランプ)を構築し、ffmpeg でフレームごとにレンダリングします。
1. 楽曲を分析する
python beats.py song.mp3 --out edit/beats.json
出力: tempo、beats (秒)、downbeats (最強の位相から4拍ごと)、hits (強いオンセット: スネア/キック/ドロップ)、0.5秒ごとの energy。
結果の読み方:
- ビート周期
P = 60 / BPM。30fps の場合、120BPM の1ビートは15フレーム。 - ヒットは必ずしもビートではありません。 ボーカルのアクセント、ライザーの終わり、ドロップなど。トレンドの「編集オーディオ」では、カットポイントはメトロノームではなくヒットになります。
- librosa は倍または半分のテンポにロックすることがあります。簡単な確認: 一般的なポップス 90-130 BPM、トラップ 130-160(しばしばハーフタイムとして感じられる)、EDM 120-128、フォンク 130-150。フレームグラブテストで検出されたグリッドがおかしいと感じたら、倍または半分にしてください。
- エネルギー曲線はドロップ/サビを見つけます: 静かなセクションの後の最大のRMSジャンプです。
2. 楽曲ウィンドウを選ぶ
- ショートフォーム: 7-20秒。ウィンドウはダウンビートで開始し、ダウンビートまたはフレーズ(4小節または8小節)の終わりで終了します。
- 最も強い映像をドロップに置きます。ドロップが曲の12秒地点にあり、リール内で3秒目に表示したい場合、ウィンドウは9秒から始まります。
- Instagram/TikTok のトレンドサウンド: アプリ内サウンドは固定ポイントから始まります。最終投稿でアプリ内サウンドを使用する場合は、その開始ポイントに編集を合わせ、リファレンスオーディオをガイドとして含むバージョンをエクスポートします。
- 音楽のフェードインは10-50ミリ秒(クリック防止)で行います。ただし、ウィンドウがトランジェントで始まる場合は除きます。フェードアウトは0.3-1.5秒、またはハードヒットで終了します。
3. グリッドを構築しイベントを配置する
fps = 30
fr = lambda t: round(t * fps) # すべてのイベントを最も近いフレームにスナップ
U = beat_period / 2 # 曲が忙しい場合のサブビート単位(8分音符)
- カット: 流れのためにビート上、インパクトのためにヒット上。毎ビートカットは疲れるので、2または4ビートごとにカットし、ドロップ直前のバースト(2-4秒)にのみ毎ビートカットを使用します。
- アンチシペーション: ビートの1-2フレーム前に配置されたカットは、スマートフォン上でよりタイトに感じられます(音声レイテンシ+知覚のため)。両方をテストし、一貫して選択します。
- フラッシュ/シェイク/ズームパンチ: ヒット上、2-6フレーム、減衰。
- テキスト: 単語はボーカルの音節またはビート上に表示。単語の場合は1単語あたり最低0.4秒、リズムに合わせて読まれるラインの場合は約0.25秒/単語。
- 速度/エフェクトの変更: ダウンビートまたはドロップ上。
- すべてのイベントとその音楽的理由を記したテーブル(
timing.json)を保持します。ビルドスクリプトがこれを読み取ります。
4. スピードランプ(ベロシティ)
スピードランプはタイムマップ src_t = f(out_t) です。イージング付きセグメントから構築します:
# 速度曲線: 1x ... ヒット間で4xにランプ ... ヒット時に0.3xに戻る
segments = [(out_start, out_end, speed_start, speed_end, ease)]
- スムーズステップ/キュービックで速度をイージングイン/アウト。瞬間的な1x→4xジャンプはグリッチのように見えます。
- ヒット上ではスロー(0.2-0.5x)、ヒット間では高速(2-5x)。ヒットフレーム自体がキーアクション(インパクト、ターン、ルック)を示します。
- スローセクションは高フレームレートのソースまたはフレーム補間(
minterpolate、Resolve Speed Warp、RIFE)が必要です。 - 高速部分にはモーションブラーを追加(
tmix=frames=3またはタイムマップレンダラーでのフレームブレンド)して、ストロボを防ぎます。 - 実例: 頭上からのデスクショットで、カメラ移動部分は1x、静止部分は5.6x、すべての速度変更を強いヒット上で行いました。オプティカルフローパス(OpenCV LK + RANSAC類似度)でカメラ移動を自動検出しました(
motion-tracking参照)。
timemap.py は、出力フレームごとに適切なソースフレームを取得(オプションでフレームブレンドあり)してタイムマップをレンダリングします — setpts フィルタを連鎖するよりも正確でシンプルです。
5. 参照リールのタイミングに一致させる
- リファレンスをダウンロード/入手し、そのオーディオを抽出します。
- カットを検出:
ffmpeg -i ref.mp4 -vf "select='gt(scene,0.3)',showinfo" -f null - 2>&1 | grep pts_time。 - そのオーディオでビートを検出し、各カットをビートインデックスまたはオフセットとして表現 → これがリズムテンプレートです。
- フレームをステップ実行(1/30秒精度)してテキストのオン/オフ時間を変化の周辺で測定します。
- 同じグリッド上で自分のフッテージで再構築します(
reference-matching参照)。
6. 検証する
- レンダリングをシーンチェンジ検出。すべてのカット時間 × fps が計画されたフレームと一致する必要があります(±0)。
- デバッグレンダリングでビート上にクリックトラックをオーバーレイ(
aevalsrcサイン波バースト)し、ヒット時にフレームを取得します。 - 聞くことはできません: 数値(計画 vs 測定カットフレーム)を報告し、ユーザーに耳で確認するよう依頼します。