/**
 * Applies an audio FX chain to a WAV at render time.
 *
 * The processing runs in an OfflineAudioContext inside the headless browser the
 * engine already drives, using the same graph builders the studio previews
 * with. There is one implementation of each effect, so the render matching the
 * preview is a property of the architecture rather than a tolerance to police.
 *
 * The alternative — reimplementing every effect as an FFmpeg filter — means two
 * implementations that have to be kept in agreement, and four of them (the
 * dynamics processors and the modulated delays) have no filter that behaves the
 * same way, so preview would quietly stop predicting the render.
 */
import { type HfAudioFxChain } from "@hyperframes/core/audio-fx";
import { type HfAutomation } from "@hyperframes/core/audio-automation";
import type { AudioVolumeKeyframe } from "./audioMixer.types.js";
export declare class AudioFxRenderError extends Error {
    constructor(message: string);
}
interface WavData {
    samples: Float32Array;
    sampleRate: number;
    channels: number;
}
export declare function readWav(path: string): WavData;
/**
 * Write 16-bit PCM, interleaved, preserving the channel count.
 *
 * 16-bit rather than the float32 this used to emit: the very next step in the
 * mixer bakes the volume envelope into the samples, and that baker accepts only
 * 16-bit PCM. Emitting float meant enabling any effect silently downgraded a
 * track's volume automation to the ffmpeg expression path, which is capped at 32
 * straight segments — so a curved envelope was quantised and a dense one could
 * fall back to rendering at base volume.
 */
export declare function writeWav(path: string, samples: Float32Array, sampleRate: number, channels?: number): void;
/**
 * Run a chain over `inputWav`, writing `outputWav`. Resolves to the path to use
 * downstream — `outputWav` when the chain did something, `inputWav` untouched
 * when the chain was empty — plus whether the volume envelope was baked here.
 *
 * The envelope is applied to the float samples the chain produced, BEFORE
 * `writeWav` quantises them. Leaving it to the mixer's second pass meant a
 * chain that overshoots full scale was destructively clipped to ±1 and only
 * then ducked, so a track the lane pulls 12 dB down still rendered the
 * distortion — which preview, working in float throughout, never had.
 * `writeWav`'s clamp stays: it is the correct last resort for a signal that is
 * still hot after the duck.
 *
 * Failure is fatal to the caller rather than a soft per-track warning: quietly
 * rendering the dry signal ships a mix that sounds plausible and is not what
 * the author set up.
 */
export declare function applyAudioFxChain(inputWav: string, chain: HfAudioFxChain, outputWav: string, options: {
    trackId: string;
    signal?: AbortSignal;
    automation?: HfAutomation;
    envelope?: {
        keyframes: AudioVolumeKeyframe[];
        trackStart: number;
        baseVolume: number;
    };
}): Promise<{
    path: string;
    envelopeBaked: boolean;
}>;
export type { HfAudioFxChain, HfAutomation };
//# sourceMappingURL=audioFxRender.d.ts.map