export interface AudioAnalysis {
    hasAudio: boolean;
    hasMeaningfulAudio: boolean;
    avgVolume: number;
    peakVolume: number;
    silenceRatio: number;
}
/**
 * Analyse audio properties of a media file using ffprobe + ffmpeg.
 *
 * Three-step detection:
 *  1. Stream detection — does the file contain an audio stream?
 *  2. Volume detection — mean & peak volume via volumedetect filter
 *  3. Silence detection — ratio of silence (< -40 dB, min 0.3 s) to total duration
 */
export declare function analyzeAudio(filePath: string): Promise<AudioAnalysis>;
export interface MixTrack {
    source: string;
    type: "original" | "bgm" | "voiceover" | "sfx";
    volume: number;
    delay?: number;
    fadeIn?: number;
    fadeOut?: number;
    ducking?: {
        trigger: string;
        ratio: number;
        threshold?: number;
    };
}
export interface MixOptions {
    videoPath: string;
    tracks: MixTrack[];
    outputPath: string;
}
/**
 * Mix multiple audio tracks with volume, delay, fade, and sidechain ducking,
 * then mux with the original video (copy video codec, encode audio as AAC).
 */
export declare function mixAudioTracks(opts: MixOptions): Promise<void>;
export interface LoudnormOptions {
    /** Integrated-loudness target in LUFS. -14 for YouTube/TikTok/Bilibili,
     *  -16 for podcasts and 小红书/视频号. */
    target: number;
    /** True-peak ceiling in dBTP. -1.5 typical, -1.0 if downstream re-encoding
     *  is known to be lossy. */
    truePeak: number;
    /** Loudness range target. 11 is the EBU R128 default; smaller values mean
     *  more aggressive limiting. */
    lra: number;
}
export interface LoudnormMeasurement {
    input_i: string;
    input_tp: string;
    input_lra: string;
    input_thresh: string;
    output_i: string;
    output_tp: string;
    output_lra: string;
    output_thresh: string;
    normalization_type: string;
    target_offset: string;
}
/**
 * Pure helper: extract the loudnorm JSON block from ffmpeg's stderr.
 * Returns null when no loudnorm-shaped JSON block is found.
 *
 * The regex looks for a curly block containing the canonical "input_i"
 * key, which uniquely identifies a loudnorm measurement (no other ffmpeg
 * filter emits this key).
 */
export declare function parseLoudnormJson(stderr: string): LoudnormMeasurement | null;
/**
 * Two-pass EBU R128 loudness normalization.
 *
 * Pass 1: ffmpeg measures input_i / input_tp / input_lra / input_thresh
 *         via the loudnorm filter with print_format=json to stderr.
 * Pass 2: ffmpeg applies the actual normalization with measured values
 *         pinned, ensuring the output hits the target without dynamic-range
 *         pumping. Two-pass is required for ±0.5 LU accuracy on speech.
 */
export declare function normalizeLufs(inputPath: string, outputPath: string, opts?: LoudnormOptions): Promise<void>;
/**
 * Re-measure the integrated loudness of a file (for tests / verification).
 * Returns the integrated-loudness value as a number (e.g. -14.02).
 */
export declare function measureLufs(filePath: string): Promise<number>;
/**
 * Pure helper: walk a Composition's tracks and emit flat-list subtitle JSON
 * matching subtitle_burn.py's parse_json_subs() expected shape:
 *   [{ start: number, end: number, text: string }, ...]
 *
 * Returns the FIRST text track's clips, sorted by trackOffset. If the comp
 * has no text track, returns an empty array. Animations and styling are
 * dropped here — the burn renders static text per Phase 3 decision D2.
 */
export declare function compositionTextTrackToJson(comp: {
    tracks: Array<{
        kind: string;
        clips: Array<{
            kind: string;
            text?: string;
            trackOffset: number;
            duration?: number;
        }>;
    }>;
}): Array<{
    start: number;
    end: number;
    text: string;
}>;
/**
 * Phase 3.B font guard. subtitle_burn.py's font_manager import is dead
 * code (audit §11.11), so the script silently relies on the canonical
 * font being pre-installed. Asserting here BEFORE invoking the script gives
 * a "missing font" failure with clear remediation instead of a cryptic
 * moviepy traceback.
 */
export declare function assertFontInstalled(fontPath?: string): Promise<string>;
/**
 * Removed in agentic-terminal refactor (2026-05-14). The libass-style burn-in
 * path lived in skills/autoviral/modules/assembly/scripts/subtitle_burn.py
 * (preserved in git tag pre-skill-rewrite-snapshot). It was workstation
 * infrastructure mis-located in skills/; rather than relocate, we direct
 * callers to the overlay strategy that ships with the workstation:
 * set `composition.captionStrategy = "overlay"` + attach a `captions`
 * model, and the CaptionsLayer React component renders captions in-line
 * during the Remotion render — no Python, no MoviePy, no font-install dance.
 */
export declare function burnSubtitles(_opts: {
    inputVideo: string;
    comp: Parameters<typeof compositionTextTrackToJson>[0];
    outputVideo: string;
    fontPath?: string;
    style?: "modern" | "cinematic" | "bold" | "minimal" | "karaoke";
}): Promise<void>;
//# sourceMappingURL=audio-tools.d.ts.map