/**
 * Voiceover carve: find the bands a voice occupies and dip a music bed there,
 * so the voice sits in front without ducking the whole track.
 *
 * This is a relationship between two tracks, not an effect on one. The controls
 * live on the bed being processed and name the voice to listen to, the same way
 * a sidechain compressor works: you select the track that gets quieter and pick
 * what makes it quieter.
 *
 * The output is an ordinary FX chain of peaking filters, so a carve is just a
 * chain the studio generated rather than a separate rendering path.
 */
import { type HfAudioFxChain } from "./audioFx.js";
export declare const HF_AUDIO_CARVE_ATTR = "data-fx-carve";
export interface HfCarveBand {
    freq: number;
    gainDb: number;
    q: number;
}
/**
 * What the author sets: which voice to listen to, how hard to work, and whether
 * the work follows the voice moment to moment.
 *
 * One number for the strength of the effect, not six for its mechanism. The
 * mechanism has six numbers — how deep to cut, how many bands, how wide, how far
 * to favour intelligibility over raw energy, how far the level may drop, how far
 * under the voice to aim — and every one of them was a control nobody could set
 * without knowing what the analysis does with it. They move together anyway: a
 * gentle carve is a shallow cut in few bands with little ducking, a hard one is
 * deeper in more bands with more. `carveProfile` is that relationship, written
 * once.
 */
export interface HfCarveSettings {
    /**
     * Element ids of every voice track this bed makes room for.
     *
     * More than one because a bed usually runs under a whole sequence: a narrator, an
     * interview answer, a second presenter. Each occupies its own stretch of the bed,
     * and carving against only one of them leaves the others fighting it. They are
     * analysed together — see `mixCarveSources` — so the cuts follow whoever is
     * speaking rather than averaging strangers.
     */
    sources: string[];
    /** How hard to carve, 0..1. */
    strength: number;
    /**
     * Whether the carve is applied at all.
     *
     * A bed under a voice wants carving, so a track that has never been configured
     * is treated as on and carved without being asked. That default needs an off
     * switch that survives: with "off" represented by having no settings at all,
     * selecting the clip again would read it as never-configured and re-apply. So
     * switching it off writes `enabled: false` and the default stops applying.
     */
    enabled: boolean;
}
/** The numbers the analysis actually works in, all derived from `strength`. */
export interface HfCarveProfile {
    /** Deepest cut applied to the strongest band. */
    maxCutDb: number;
    /** How many bands to dip. */
    bands: number;
    q: number;
    /**
     * Weight band selection toward intelligibility rather than raw voice energy.
     *
     * Ranking purely by voice power lands on the fundamental almost every time,
     * because that is where a voice is loudest — but masking that actually hurts a
     * voiceover happens higher up, and dipping 160 Hz mostly just thins the bed.
     */
    intelligibilityBias: number;
    /** How far the bed's whole level may come down to make room, in dB. */
    duckDb: number;
    /** How far under the voice the bed should sit while the voice speaks, in dB. */
    headroomDb: number;
}
/**
 * What a track's name suggests it holds.
 *
 * Only ever a hint — a name is what the author called something, not what is in the
 * file — so this is used to order and to filter a list of candidates, never to
 * decide alone. `unknown` is deliberately common: a track called `a1` could be
 * anything, and treating an unrecognised name as "not a voice" would hide the one
 * track somebody needs to pick.
 */
export type HfAudioNameKind = "voice" | "music" | "sfx" | "unknown";
/**
 * Classify a track from its id and filename together.
 *
 * Both, because either can be the informative one: an author naming elements `a1`
 * and `a2` may still have `narration.mp3` and `bgm.mp3` as their sources, and one
 * naming them `voice` and `music` may have opaque hashes for filenames.
 *
 * Voice is tested first: a file called `voiceover-music-bed.wav` is more likely the
 * voiceover than the bed, and a track matching both hints is better offered than
 * hidden.
 */
export declare function classifyAudioName(...parts: readonly (string | null | undefined)[]): HfAudioNameKind;
/** A clip's place on the timeline. A duration that is not a number is unbounded. */
export interface HfClipSpan {
    start: number;
    duration?: number | null;
}
/**
 * Do these two clips share any time at all?
 *
 * A voice that never plays while the bed does cannot mask it, so it has no business
 * in the carve: it would contribute silence to the analysis and, worse, invite the
 * author to wonder why including it changed nothing.
 *
 * An unknown duration counts as unbounded rather than as zero. Refusing a track
 * because its length is not written down would drop the commonest case there is — a
 * clip whose duration the composition leaves to the media itself.
 */
export declare function clipsOverlap(a: HfClipSpan, b: HfClipSpan): boolean;
/**
 * Could this track be the voice a carve listens to?
 *
 * Music and SFX are out: a bed is the thing being carved, and a 200 ms whoosh has
 * no speech to make room for. Everything else stays in, including names that say
 * nothing — see `HfAudioNameKind`.
 */
export declare function couldBeCarveSource(...parts: readonly (string | null | undefined)[]): boolean;
export declare const DEFAULT_CARVE: HfCarveSettings;
/**
 * Strength as the six numbers the analysis needs.
 *
 * Each is a straight line from "barely there" to as far as the effect goes. The
 * slopes are twice what they first were: the top of the knob was not strong
 * enough to sit a bed under a loud voice, so what used to be full strength is now
 * the halfway point and everything above it is new range. Quarter strength is
 * therefore where the separate controls' own defaults land, which is what the
 * panel defaults to.
 */
export declare function carveProfile(strength: number): HfCarveProfile;
/**
 * Read carve settings from an attribute.
 *
 * Projects written before the collapse to one knob carry the six mechanism
 * numbers instead; their depth is the one that says most about intent, so it maps
 * back onto strength rather than being dropped. Everything else about such a
 * carve is re-derived, which is the point of having one control.
 */
export declare function normalizeCarveSettings(raw: Partial<HfCarveSettings & HfCarveProfile> | undefined): HfCarveSettings;
/**
 * Every voice as one signal on the BED's clock.
 *
 * The analysis asks one question — where and when is speech masking this bed — and
 * that question has one answer even when three people are talking at different
 * times. Summing them onto the bed's timeline first means the existing analysis
 * needs no notion of "which voice": bands come out of all the speech there is, and
 * the envelopes rise wherever any of it is happening.
 *
 * `offsetSeconds` is where each voice starts relative to the bed. Audio before the
 * bed begins is dropped rather than folded in at zero: it plays over nothing and
 * cannot mask anything, and shifting it would put a cut where there is no voice.
 *
 * Summed, not averaged. Two people speaking at once mask more than either alone,
 * which is exactly what the carve should answer to.
 */
export declare function mixCarveSources(parts: readonly {
    samples: Float32Array;
    offsetSeconds: number;
}[], sampleRate: number): Float32Array;
/**
 * Analyse a voice and return the bands to dip in the bed. Bands come back in
 * ascending frequency; the deepest cut lands on the strongest band and the
 * others scale with their relative weight, floored at half depth so a selected
 * band still does something audible.
 */
export declare function analyseCarveBands(voice: Float32Array, sampleRate: number, profile: HfCarveProfile): HfCarveBand[];
export interface HfCarveDynamics {
    /** The band this envelope drives, matching a band from `analyseCarveBands`. */
    freq: number;
    /** Gain in dB over time, in seconds from the start of the *voice* clip. */
    points: {
        t: number;
        v: number;
    }[];
}
/**
 * Turn each carved band into an envelope that follows the voice's level in that
 * same band.
 *
 * The depth from `analyseCarveBands` becomes the envelope's ceiling rather than a
 * constant: full depth where that band is at its loudest in the voice, flat where
 * the voice is silent, scaled in dB between the two. Written as automation on the
 * filters' gain, so playback and render both already know how to follow it.
 *
 * Times are relative to the voice clip, since that is what was measured. A caller
 * placing these on another element shifts them by the gap between the two clips'
 * starts. This assumes the voice plays from its own beginning at its `data-start`
 * — a clip with a media offset would need that added.
 */
export declare function analyseCarveDynamics(voice: Float32Array, sampleRate: number, bands: HfCarveBand[]): HfCarveDynamics[];
/**
 * The level envelope that keeps a bed under a voice.
 *
 * The spectral carve makes room in the frequency domain, which does nothing
 * about a bed that is simply louder than the voice: the notches sit in the right
 * places while the level goes on winning. This measures both tracks over the
 * same windows and returns the gain the bed needs to sit `headroomDb` under the
 * voice — no more than `duckDb`, and nothing at all where the voice is not
 * speaking, so pauses stay open rather than being held down.
 *
 * Times are in seconds from the start of the *voice* clip; `offsetSeconds` is
 * how far into the bed the voice's start falls, so the two are read at the same
 * moment of the composition even when the clips begin at different times.
 */
export declare function analyseCarveDuck(voice: Float32Array, bed: Float32Array, sampleRate: number, profile: HfCarveProfile, offsetSeconds: number): {
    t: number;
    v: number;
}[];
/** Carve bands as an ordinary FX chain of peaking filters. */
export declare function carveBandsToChain(bands: HfCarveBand[]): HfAudioFxChain;
//# sourceMappingURL=audioCarve.d.ts.map