/** * vocal-sync-plan.ts — the planner that makes a music-video edit feel "in sync": * it pins each PERFORMER to the moment their own vocal is heard, and keeps the * baked lip-sync locked to the muxed song. * * Given a VOCAL MAP (the song segmented by timestamp into rap / hook / * instrumental / outro, each vocal section carrying its lip-synced performer * clip) plus the beat grid and B-roll pools, it emits a frame-aligned segment * list (`clip | inSec | durationSec`) for {@link buildSegmentCutArgs}. PURE and * deterministic given an injected `rng` and `clipDuration` — no I/O, no ffmpeg. * * The three lessons it encodes (each learned by getting it wrong first): * 1. PERFORMER-ON-VOCAL: a music video needs the rapper on the rap and the * singer on the hook — assigning performers to GUESSED time blocks fights the * audio. Sections come from the transcribed vocal map, not a shot script. * 2. TIME-ALIGNED SCRUB: a performer cut shows its clip at in-point * `(songTime - sectionStart)`, so the baked lips stay locked to the song even * across B-roll cutaways (cut away and back → correct lip position). The * performer holds ~2 of every 3 cuts and OPENS each vocal section. * 3. FRAME-EXACT CONTINUOUS TIMELINE: walk one timeline from t=0 (no per-section * reset — that drops the intro and offsets every cue) and snap every cut to * the frame grid so the concatenated video-time == song-time. Paired with * `-frames:v` cutting, total drift is < 1 frame. * Plus DE-PATTERNING: B-roll draws from a shuffle-bag (never round-robin) with a * stepped in-point on reuse, so recurring footage doesn't read as a loop. */ export type VocalSectionType = 'rap' | 'hook' | 'instrumental' | 'outro'; export interface VocalSection { /** Section start in song seconds. */ start: number; /** Section end in song seconds. */ end: number; type: VocalSectionType; /** * The lip-synced performer clip id for this vocal section (rap → rapper clip, * hook → singer clip). Omitted for instrumental/outro (B-roll only). */ performerClip?: string; } /** B-roll clip ids grouped by role; performers come from the vocal map, not here. */ export interface BrollPools { action: string[]; atmo: string[]; trio: string[]; vanish: string[]; } export interface PlanVocalSyncInput { /** Contiguous, ordered vocal map covering [0, songEnd]. */ vocalMap: VocalSection[]; /** Beat timestamps (seconds), e.g. from aubiotrack. */ beats: number[]; /** Song length in seconds. */ songEnd: number; pools: BrollPools; /** Duration (seconds) of a clip id — injected so the planner stays pure. */ clipDuration: (clipId: string) => number; /** Output frame rate (default 24). */ fps?: number; /** Seconds-per-cut by section type. Verses cut fast; hooks hold longer. */ cutLengths?: Partial>; /** Injected RNG (default Math.random) — seed it for deterministic tests. */ rng?: () => number; } export interface PlannedSegment { clip: string; /** Frame-aligned input-seek point. */ inSec: number; /** Frame-aligned duration. */ durationSec: number; /** True when this cut is a lip-synced performer (vs B-roll). */ performer: boolean; } export declare function planVocalSync(input: PlanVocalSyncInput): PlannedSegment[]; /** Total planned duration (seconds) — feed to {@link assertNoDrift} against the built file. */ export declare function plannedDuration(segs: PlannedSegment[]): number; //# sourceMappingURL=vocal-sync-plan.d.ts.map