/** * FCPXML 1.10 emitter. * * FCPXML preserves what EDL drops: * - Per-clip names (not just reel comments) * - Frame-rational time (no rounding loss for 23.976/29.97/59.94) * - Multi-source media in one timeline * - Color/audio metadata can be added later as extensions * * Premiere Pro imports FCPXML natively as a Sequence. DaVinci Resolve * imports it as a Timeline. Both treat it as the "richer" interchange * compared to EDL, which is why we expose write_fcpxml as the preferred * format for Premiere and an alternative for Resolve. * * v2 scope: * - One or more source assets (each unique `reel` becomes an asset) * - Multi-track / multi-lane composition (lane="N" on asset-clip) * - with static OR keyframed position/scale/rotation/anchor * - + for fade ramps * - + + for * audio gain ramps * - elements (Basic Title effect ref) with <text-style-def> * - Integer or rational frame rates (24, 25, 29.97, 30, 50, 59.94, 60) * * Real-world references: * - mifi/lossless-cut FCPXML 1.9 fixtures: <adjust-transform position scale anchor> * - mazsola2k/ai-video-editor: <filter-audio>/<keyframe> for volume ramps * - hysmichael/srt_fcpxml_converter: <title>/<text-style-def> shape * - subtitleedit FinalCutProXmlGap.cs: lane= on title * - eoyilmaz/anima conformer: emitter shape verified */ /** Single keyframe at a clip-relative frame. */ export interface Keyframe<T> { /** Time within the clip, in frames (0-based, frame 0 = start of clip). */ frame: number; value: T; /** * Interpolation between this keyframe and the next. Per FCPXML spec, * position keyframes ignore curve/interp attributes — the emitter omits * them automatically for position. "linear" is the most portable choice. */ interp?: "linear" | "easeIn" | "easeOut" | "smooth"; } /** Animated value driven by 2+ keyframes. Single-frame = static. */ export interface KeyframedValue<T> { keyframes: Keyframe<T>[]; } /** Static or animated transform on a clip. */ export interface TransformSpec { /** [x, y] in FCPXML normalized units. (0,0) = centred. Animatable. */ position?: [number, number] | KeyframedValue<[number, number]>; /** [sx, sy] uniform-or-anisotropic scale. 1 = native. Animatable. */ scale?: [number, number] | KeyframedValue<[number, number]>; /** [ax, ay] anchor point. Static only (FCPXML doesn't keyframe anchor). */ anchor?: [number, number]; /** Rotation in degrees. Animatable. */ rotation?: number | KeyframedValue<number>; } export interface FcpxmlEvent { /** Source identifier — multiple events with the same reel share an asset. */ reel: string; /** Absolute path or file:// URL to the source media. */ sourcePath: string; /** Source IN (frames into the source clip). */ sourceInFrame: number; /** Source OUT (exclusive). */ sourceOutFrame: number; /** Optional clip name on the timeline. */ clipName?: string; /** Optional source duration in frames. Used to declare the asset's total length. */ sourceDurationFrames?: number; /** * Lane number for multi-track composition. 0 (or undefined) = main spine. * 1, 2, … = stacked above (B-roll, overlays, lower-thirds). * * IMPORTANT: lane clips use absolute timeline offset — they do NOT advance * the spine cursor. Two events with lane=1 at offset 0 both start at 0. * If you omit recordOffsetFrame on a lane clip, it inherits the cursor * position at emit time (rare; usually you want to specify it). */ lane?: number; /** * Override the timeline offset where this clip starts. Required for lane * clips so they sit at the right time. Spine clips (lane undefined / 0) * normally leave this unset and accept contiguous placement. */ recordOffsetFrame?: number; /** Static or animated transform applied via <adjust-transform>. */ transform?: TransformSpec; /** * Static or animated opacity 0..1. Static emits a single keyframe, animated * emits the full ramp. Backed by <param name="opacity">. */ opacity?: number | KeyframedValue<number>; /** * Static or animated audio gain in dB. Static emits <adjust-volume>; * animated emits <filter-audio name="Volume"> + <keyframe time= value=> ramp. * Convention: 0 dB = unchanged, -inf approximated as -60 dB. */ volumeDb?: number | KeyframedValue<number>; } /** A standalone <title> element (lower-third / title card / lyric). */ export interface FcpxmlTitle { /** Text content. Newlines emit as . */ text: string; /** Lane to render on. 0 = main spine (rare). 1+ = above. */ lane?: number; /** Timeline offset in frames. */ startFrame: number; /** Duration in frames. */ durationFrames: number; /** Optional name in the timeline panel. */ name?: string; fontName?: string; /** Font size in points (FCPXML coords). 63 ≈ medium HD title. */ fontSize?: number; /** Hex RRGGBB; converted to FCPXML's 0..1 RGB. White by default. */ fontColor?: string; /** "left" | "center" | "right" — passed through to text-style alignment. */ alignment?: "left" | "center" | "right"; } export interface FcpxmlOptions { title: string; /** Frame rate as a number. 29.97 / 23.976 are auto-mapped to NTSC fractions. */ frameRate: number; width?: number; height?: number; events: FcpxmlEvent[]; /** * Standalone titles to emit on the spine alongside asset-clips. Useful for * lower-thirds / chapter cards. If you only want titles, leave events empty * is NOT valid — pass at least one event (use a black gap clip if needed). */ titles?: FcpxmlTitle[]; /** * Audio metadata applied to every emitted asset. These attributes are * standard in real-world Resolve / Premiere / FCP exports and let * importers route audio without guessing. The defaults match the most * common content-creator setup (stereo 48kHz). */ audioChannels?: number; audioRate?: number; } interface FrameRational { /** Numerator of frame duration (seconds). */ num: number; /** Denominator of frame duration (seconds). */ den: number; } /** * Map a frame rate to FCPXML rational frame duration. * * 30/1001 NDF maps to 1001/30000s, etc. Integer rates use 1/N. */ export declare function frameRateToRational(fps: number): FrameRational; /** Convert a frame count to FCPXML time string e.g. "1001/30000s × 30 = 30030/30000s". */ export declare function framesToTime(frames: number, fr: FrameRational): string; /** * Build an FCPXML 1.10 string. Lays spine events out contiguously * (event N starts where N-1 ended) — matches our EDL emitter's contract. * Lane events (lane>=1) and titles use absolute offsets and do NOT advance * the cursor. */ export declare function buildFcpxml(opts: FcpxmlOptions): string; export declare function totalRecordFramesFcpxml(events: FcpxmlEvent[]): number; /** * Convert hex RRGGBB to FCPXML's space-separated 0..1 RGBA color literal. * "FFFFFF" → "1 1 1 1". "808080" → "0.501961 0.501961 0.501961 1". */ export declare function hexToFcpColor(hex: string): string; export {}; //# sourceMappingURL=fcpxml.d.ts.map