import { TimeObject } from './utils/create-time-object';
import { Playable } from './interfaces/playable';
import { Connectable, Connection } from './interfaces/connectable';
import { ControlType, ParamController, RampType, RatioType } from './controllers/base-param-controller';
import { SoundEventMap } from './events/event-types';
import { Effect } from './effects';
import { Analyzer } from './analyzer';
/**
 * Configuration options for BaseSound and its subclasses.
 */
export interface BaseSoundOptions {
    /**
     * Optional name for identifying this sound instance.
     * Useful for debugging and when managing multiple sounds.
     */
    name?: string;
    /**
     * Custom setTimeout implementation.
     *
     * By default, an AudioContext-aware setTimeout is used that compensates
     * for browser throttling. Override this if you need different timing behavior.
     *
     * @param fn - Function to call after delay
     * @param delayMillis - Delay in milliseconds
     * @returns Timeout ID for cancellation
     */
    setTimeout?: (fn: () => void, delayMillis: number) => number;
}
/**
 * Abstract base class for all playable audio sources.
 *
 * BaseSound provides the core audio infrastructure that Sound, Track, and Oscillator
 * build upon. It handles:
 * - Audio node routing (gain, panner, effect chain, destination)
 * - Playback control (play, stop, timing methods)
 * - Parameter control via fluent API (update, onPlaySet, onPlayRamp)
 * - Event system for lifecycle events (play, stop, end)
 * - Effect chain management (addEffect, removeEffect)
 * - Analyzer attachment for visualization
 *
 * @example
 * ```typescript
 * // Inherited by Sound, Track, Oscillator
 * const sound = await createSound('click.mp3')
 *
 * // Immediate parameter update
 * sound.update('gain').to(0.5).from('ratio')
 *
 * // Schedule parameter for next play
 * sound.onPlaySet('gain').to(0).endingAt(1, 'exponential') // fade in
 *
 * // Ramp parameter during playback
 * sound.onPlayRamp('gain').from(1).to(0).in(2) // fade out over 2s
 *
 * // Add effects
 * const filter = createFilterEffect(ctx, 'lowpass', { frequency: 1000 })
 * sound.addEffect(filter)
 *
 * // Listen for events
 * sound.on('play', () => console.log('Started'))
 * sound.on('end', () => console.log('Finished'))
 * ```
 */
export declare abstract class BaseSound extends EventTarget implements Connectable, Playable {
    audioContext: AudioContext;
    protected _isPlaying: boolean;
    gainNode: GainNode;
    protected pannerNode: StereoPannerNode;
    protected setTimeout: (fn: () => void, delayMillis: number) => number;
    protected startedPlayingAt: number;
    /**
     * @property effects
     * An array of Effect instances that form the persistent effect chain.
     * Effects are wired once and persist across multiple play() calls - only the source reconnects.
     *
     * Use addEffect() and removeEffect() to manage the effect chain.
     * Chain order: source -> effectChainInput -> [effects] -> gainNode -> pannerNode -> destination
     */
    protected effects: Effect[];
    /**
     * @property effectChainInput
     * The entry point for the effect chain. The audio source connects to this node,
     * which then routes through any effects before reaching gain/panner/destination.
     */
    protected effectChainInput: GainNode;
    /**
     * @property _destination
     * The final destination node for audio output. Defaults to audioContext.destination.
     * Can be changed with setDestination() to route audio elsewhere (e.g., for sub-mixing).
     */
    protected _destination: AudioNode;
    /**
     * @property _analyzer
     * Optional Analyzer attached to the end of the signal chain for visualization.
     * Audio flows through the analyzer (passthrough) before reaching destination.
     */
    protected _analyzer: Analyzer | null;
    /**
     * @property connections
     * An array of connections that will be placed in between the `audioSourceNode` (where the audio comes from) and the gain/panner nodes.
     *
     * This is useful for adding effects to a sound. For example, to add a reverb effect, you can create a `ConvolverNode` and add it to this array.
     *
     * The `audioSourceNode` is mandatory and always first, and the gain/panner nodes are mandatory and always last, but the nodes in between can be in any order.
     *
     * You can use the `addConnection` and `removeConnection` methods to add and remove connections from this array, or you can set/mutate the array directly.
     *
     * The `wireConnections` method is called automatically when the sound is played, and it will connect all the nodes in this array in the correct order.
     *
     * @deprecated Use addEffect() instead for the new persistent effect chain system.
     *
     * @example
     * const sound = new Oscillator(audioContext, { type: 'sine', frequency: 440 })
     * const convolverNode = audioContext.createConvolver()
     * sound.connections = [convolverNode]
     * sound.play()
     *
     */
    connections: Connection[];
    /**
     * @property startOffset
     *
     * See Web Audio API documentation for this one, as it is just passed into the `start` method of the `audioSourceNode`.
     *
     * This is useful for starting a sound at a specific offset from the beginning of the sound. Manipulation of this value is used
     * extensively in the `Track` class to allow for starting the track at specific positions.
     *
     * @default 0
     * @see https://developer.mozilla.org/en-US/docs/Web/API/AudioScheduledSourceNode/start
     */
    startOffset: number;
    protected abstract controller: ParamController;
    protected abstract wireConnections(): void;
    protected abstract setup(): void;
    /**
     * @property audioSourceNode
     *
     * The audio source node that this sound is using. This is the first node in the chain and is the node that actually provides audio.
     */
    abstract audioSourceNode: OscillatorNode | AudioBufferSourceNode;
    /**
     * @property duration
     *
     * The duration of this sound. This is used to schedule the stop method to be called after the sound has finished playing. Not all
     * `Sound` types have a useful `duration`, such as `Oscillator`
     */
    abstract duration: TimeObject;
    /**
     * @property name
     *
     * A name for this sound. Optional. Useful for identification of a given sound and debugging.
     */
    name: string;
    /**
     * @property debug
     *
     * Per-sound debug override. Set to true to enable debug logging for this sound only,
     * or false to disable logging even when global debug is enabled.
     *
     * @default undefined (follows global debug mode)
     *
     * @example
     * sound.debug = true  // enable debug for this sound
     * sound.debug = false // silence this sound even when global debug is on
     */
    debug?: boolean;
    constructor(audioContext: AudioContext, opts?: BaseSoundOptions);
    /**
     * Wires the effect chain from effectChainInput through all non-bypassed effects
     * to gainNode -> pannerNode -> [analyzer] -> destination.
     *
     * If an analyzer is attached, audio flows through it before reaching destination.
     * The analyzer is a passthrough node that also provides visualization data.
     *
     * Called when effects are added/removed/reordered, destination changes, or analyzer changes.
     * NOT called on every play() - the chain persists.
     *
     * @private
     */
    private wireEffectChain;
    /**
     * Add an effect to the effect chain.
     * Effects persist across multiple play() calls.
     *
     * @param effect - The Effect instance to add
     * @param position - Optional index to insert at (defaults to end of chain)
     * @returns this for chaining
     *
     * @example
     * const filter = createFilterEffect(audioContext, 'lowpass', { frequency: 1000 })
     * sound.addEffect(filter)
     */
    addEffect(effect: Effect, position?: number): this;
    /**
     * Remove an effect from the effect chain.
     *
     * @param effect - The Effect instance to remove
     * @returns this for chaining
     *
     * @example
     * sound.removeEffect(filter)
     */
    removeEffect(effect: Effect): this;
    /**
     * Get a readonly copy of the current effects array.
     *
     * @returns Shallow copy of the effects array
     */
    getEffects(): readonly Effect[];
    /**
     * Set a custom destination for audio output instead of audioContext.destination.
     * Useful for routing to sub-mixes, analyzers, or other processing chains.
     *
     * @param node - The AudioNode to route output to
     * @returns this for chaining
     *
     * @example
     * const analyzer = audioContext.createAnalyser()
     * analyzer.connect(audioContext.destination)
     * sound.setDestination(analyzer)
     */
    setDestination(node: AudioNode): this;
    /**
     * Re-wire the effect chain. Call this after toggling effect.bypass
     * to update the audio routing.
     */
    rewireEffects(): void;
    /**
     * Attach an analyzer to this sound for visualization.
     * The analyzer is inserted at the end of the signal chain (after effects and panner,
     * before destination), showing the fully processed signal.
     *
     * The analyzer is a passthrough node - audio flows through it unchanged while
     * providing frequency and waveform data for visualization.
     *
     * @param analyzer - The Analyzer instance to attach, or null to detach
     * @returns this for chaining
     *
     * @example
     * const analyzer = createAnalyzer(audioContext, { fftSize: 2048 })
     * sound.setAnalyzer(analyzer)
     *
     * function draw() {
     *   const freqData = analyzer.getFrequencyData()
     *   // Draw frequency bars
     *   requestAnimationFrame(draw)
     * }
     */
    setAnalyzer(analyzer: Analyzer | null): this;
    /**
     * Get the currently attached analyzer, if any.
     *
     * @returns The attached Analyzer instance, or null if none attached
     */
    getAnalyzer(): Analyzer | null;
    /**
     * Add a typed event listener for sound lifecycle events.
     * Overloaded to provide type safety for known event types while remaining
     * compatible with EventTarget.
     *
     * @param type - The event type ('play', 'stop', 'end', etc.)
     * @param listener - The event handler function
     * @param options - Standard addEventListener options
     */
    addEventListener<K extends keyof SoundEventMap>(type: K, listener: (event: SoundEventMap[K]) => void, options?: boolean | AddEventListenerOptions): void;
    addEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | AddEventListenerOptions): void;
    /**
     * Remove a typed event listener for sound lifecycle events.
     * Overloaded to provide type safety for known event types while remaining
     * compatible with EventTarget.
     *
     * @param type - The event type ('play', 'stop', 'end', etc.)
     * @param listener - The event handler function to remove
     * @param options - Standard removeEventListener options
     */
    removeEventListener<K extends keyof SoundEventMap>(type: K, listener: (event: SoundEventMap[K]) => void, options?: boolean | EventListenerOptions): void;
    removeEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | EventListenerOptions): void;
    /**
     * Emit a typed event with the given detail.
     * @protected
     * @param type - The event type to emit
     * @param detail - The event detail object
     */
    protected emit<K extends keyof SoundEventMap>(type: K, detail: SoundEventMap[K]['detail']): void;
    /**
     * Subscribe to one or more events. Supports chaining.
     *
     * @example
     * ```typescript
     * sound.on('play', handlePlay).on('stop', handleStop);
     * sound.on(['play', 'stop'], handleBoth);
     * ```
     *
     * @param type - The event type(s) to subscribe to
     * @param listener - The event handler function
     * @returns this for chaining
     */
    on<K extends keyof SoundEventMap>(type: K | K[], listener: (event: SoundEventMap[K]) => void): this;
    /**
     * Subscribe to an event once. Handler is removed after first invocation.
     *
     * @example
     * ```typescript
     * sound.once('end', () => console.log('Playback finished'));
     * ```
     *
     * @param type - The event type to subscribe to
     * @param listener - The event handler function
     * @returns this for chaining
     */
    once<K extends keyof SoundEventMap>(type: K, listener: (event: SoundEventMap[K]) => void): this;
    /**
     * Unsubscribe from an event.
     *
     * Note: Due to native EventTarget limitations, you must provide the same
     * listener function reference that was used when subscribing. To remove
     * listeners, store the function reference when adding it.
     *
     * @example
     * ```typescript
     * const handler = (e) => console.log(e.detail);
     * sound.on('play', handler);
     * // later...
     * sound.off('play', handler);
     * ```
     *
     * @param type - The event type to unsubscribe from
     * @param listener - The event handler function to remove
     * @returns this for chaining
     */
    off<K extends keyof SoundEventMap>(type: K, listener: (event: SoundEventMap[K]) => void): this;
    addConnection(connection: Connection): this;
    removeConnection(name: string): this;
    getConnection(name: string): Connection | undefined;
    getNodeFrom<T extends AudioNode | StereoPannerNode>(connectionName: string): T | undefined;
    /**
     * Update an audio parameter immediately.
     *
     * Returns a fluent builder for setting the parameter value. Use `.to(value)`
     * to set the value, then `.from(unit)` for unit interpretation.
     *
     * @param type - The parameter to update ('gain' or 'pan')
     * @returns Fluent builder for setting the value
     *
     * @example
     * ```typescript
     * // Set gain to 50%
     * sound.update('gain').to(0.5).from('ratio')
     *
     * // Set pan to left
     * sound.update('pan').to(-1).from('ratio')
     * ```
     */
    update(type: ControlType): {
        to: (value: number) => {
            from: (method: RatioType) => void;
        };
    };
    /**
     * Set the pan position immediately.
     *
     * Convenience method for `update('pan').to(value).from('ratio')`.
     *
     * @param value - Pan position from -1 (left) to 1 (right), 0 is center
     * @returns this for chaining
     *
     * @example
     * ```typescript
     * sound.changePanTo(-1)  // Hard left
     * sound.changePanTo(0)   // Center
     * sound.changePanTo(1)   // Hard right
     * ```
     */
    changePanTo(value: number): this;
    /**
     * Set the gain (volume) immediately.
     *
     * Convenience method for `update('gain').to(value).from('ratio')`.
     *
     * @param value - Gain from 0 (silent) to 1 (full volume)
     * @returns this for chaining
     *
     * @example
     * ```typescript
     * sound.changeGainTo(0.5)  // Half volume
     * sound.changeGainTo(0)    // Muted
     * sound.changeGainTo(1)    // Full volume
     * ```
     */
    changeGainTo(value: number): this;
    /**
     * Schedule a parameter value to be set when play() is called.
     *
     * Use this for fade-ins, fade-outs, or precise parameter timing.
     * The value is applied relative to when play() is called.
     *
     * @param type - The parameter to control ('gain' or 'pan')
     * @returns Fluent builder for setting value and timing
     *
     * @example
     * ```typescript
     * // Fade in: start at 0, ramp to 1 over 0.5 seconds
     * sound.onPlaySet('gain').to(0).at(0)
     * sound.onPlaySet('gain').to(1).endingAt(0.5, 'linear')
     * sound.play()
     *
     * // Start panned left, move to center over 2 seconds
     * sound.onPlaySet('pan').to(-1).at(0)
     * sound.onPlaySet('pan').to(0).endingAt(2, 'linear')
     * sound.play()
     * ```
     */
    onPlaySet(type: ControlType): {
        to: (value: number) => {
            at: (time: number) => void;
            endingAt: (time: number, rampType?: RampType) => void;
        };
    };
    /**
     * Schedule a parameter ramp when play() is called.
     *
     * Use this for smooth transitions like vibrato, tremolo, or automation.
     *
     * @param type - The parameter to ramp ('gain' or 'pan')
     * @param rampType - Type of ramp curve ('linear' or 'exponential')
     * @returns Fluent builder for setting start value, end value, and duration
     *
     * @example
     * ```typescript
     * // Fade out over 2 seconds
     * sound.onPlayRamp('gain', 'linear').from(1).to(0).in(2)
     * sound.play()
     *
     * // Pan sweep from left to right over 4 seconds
     * sound.onPlayRamp('pan', 'linear').from(-1).to(1).in(4)
     * sound.play()
     * ```
     */
    onPlayRamp(type: ControlType, rampType?: RampType): {
        from: (startValue: number) => {
            to: (endValue: number) => {
                in: (endTime: number) => void;
            };
        };
    };
    play(): Promise<void>;
    playIn(when: number): void;
    playFor(duration: number): void;
    /**
     * Play after a delay, then stop after a duration.
     *
     * Combines playIn() and stopIn() for precise timed playback.
     *
     * @param playIn - Seconds from now until playback starts
     * @param stopAfter - Seconds of playback before stopping (from play start)
     *
     * @example
     * ```typescript
     * // Start in 1 second, play for 3 seconds
     * sound.playInAndStopAfter(1, 3)
     * ```
     */
    playInAndStopAfter(playIn: number, stopAfter: number): void;
    /**
     * Play the audio source at a specific time.
     *
     * This is the underlying method for all play variants. Time is measured in seconds
     * from when the AudioContext was created (audioContext.currentTime).
     *
     * @param time - The AudioContext time when playback should start
     *
     * @example
     * ```typescript
     * // Play immediately
     * sound.playAt(audioContext.currentTime)
     *
     * // Play in 2 seconds
     * sound.playAt(audioContext.currentTime + 2)
     *
     * // Sync multiple sounds
     * const startTime = audioContext.currentTime + 0.1
     * sound1.playAt(startTime)
     * sound2.playAt(startTime)
     * ```
     */
    playAt(time: number): Promise<void>;
    /**
     * Hook method called after playback starts.
     * Override in subclasses to add behavior that runs for all play variants.
     * @protected
     */
    protected _onPlaybackStarted(): void;
    /**
     * Stop the audio source after a delay.
     *
     * @param seconds - Seconds from now until playback stops
     *
     * @example
     * ```typescript
     * sound.play()
     * // Stop after 5 seconds
     * sound.stopIn(5)
     * ```
     */
    stopIn(seconds: number): Promise<void>;
    /**
     * Stop the audio source at a specific time.
     *
     * This is the underlying method for all stop variants. Time is measured in seconds
     * from when the AudioContext was created (audioContext.currentTime).
     *
     * @param time - The AudioContext time when playback should stop
     *
     * @example
     * ```typescript
     * // Stop immediately
     * sound.stopAt(audioContext.currentTime)
     *
     * // Stop in 5 seconds
     * sound.stopAt(audioContext.currentTime + 5)
     * ```
     */
    stopAt(time: number): Promise<void>;
    stop(): Promise<void>;
    get isPlaying(): boolean;
    get percentGain(): number;
    protected later(fn: () => void): void;
}
//# sourceMappingURL=base-sound.d.ts.map