import { type Logger } from 'zapo-js'; import { type AudioSender, type WaAudioEngineConfig } from '../types.js'; export interface WaAudioEngineOptions extends Partial { readonly logger?: Logger; /** Clock the pacers run on and stamp capture with, in ms; `performance.now` by default. */ readonly now?: () => number; } /** * The audio clock of a call carried in Node and its outbound source. Both pacers move every * chunk the elapsed time owes, so a late or coarse timer costs no audio. */ export declare class WaAudioEngine { private readonly logger; private readonly now; private audioSender; private audioBuffer; private audioPosition; private audioFinished; private onAudioFinished; private playbackInterval; private playbackStartMs; /** Playback time the clock has accounted for since it started: pulled or forgiven. */ private playbackSamples; private playbackStalls; private captureInterval; private captureStartMs; /** Capture time the clock has accounted for since it started: handed over or forgiven. */ private captureSamples; private captureChunks; private captureStalls; private readonly sampleRate; private readonly captureChunkSize; private readonly outputSize; private readonly intervalMs; private playoutSource; private playbackSink; private silenceMode; private externalMode; private liveWritePos; private extStarted; private readonly extPreBufferSize; private readonly extTargetBuffer; private readonly extTargetMs; private readonly extHighWater; private readonly extMaxBuffer; private extSkipCount; private extDropCount; private readonly captureChunkBuffer; private readonly silenceChunkBuffer; private readonly playbackOutputBuffer; constructor(config?: WaAudioEngineOptions); setAudioSender(sender: AudioSender): void; setOnAudioFinished(callback: (() => void) | null): void; setExternalMode(enabled: boolean): void; isExternalMode(): boolean; /** * Append live PCM to the external-mode buffer and return the buffered * level in milliseconds. Bounded: an oversized chunk keeps only its tail, * and overflow drops the oldest samples, so the buffer never grows past * its cap. */ feedExternalAudio(data: Float32Array): number; getLiveBufferMs(): number; /** * Backpressure watermarks for the live feed, in milliseconds: pause a * producer once the buffered level reaches `pauseMs`, resume once it drains * to `resumeMs`. Derived from the engine config, independent of any call. */ static feedWatermarksMs(): { pauseMs: number; resumeMs: number; }; isAudioFinished(): boolean; loadAudioFile(audioPath: string): Promise; private int16ToFloat32; private decodeWithFFmpeg; generateTestTone(frequency?: number, duration?: number, amplitude?: number): void; /** * Where the playback clock pulls decoded audio from: it fills the buffer * it is handed and returns how many samples were real audio. */ setPlayoutSource(source: ((out: Float32Array) => number) | null): void; startPlayback(): void; /** Pulls every block of playback time owed since the clock started, however late the timer. */ private tickPlayback; stopPlayback(): void; /** * Receive the paced playback audio. The callback is handed the engine's own * output buffer, which the next pull overwrites - within the same tick when * the clock catches up - so a consumer that keeps the samples has to copy * them. */ setPlaybackSink(sink: ((pcm: Float32Array) => void) | null): void; /** Starts the capture clock on warmup silence; {@link startCapture} later keeps this clock. */ startSilenceCapture(): void; /** * Switches capture to the real source (live feed, file from its start, or silence), keeping * a warmup clock so the stamps stay one timeline. A no-op once the real source runs. */ startCapture(): void; private startCaptureClock; /** Hands over every capture chunk owed since the clock started, stamped on that clock. */ private tickCapture; private sendCaptured; /** * Whole chunks of `chunkSize` a clock started at `startMs` owes: the time * elapsed since then, in samples, that `doneSamples` does not cover yet. */ private chunksOwed; stopCapture(): void; stop(): void; hasAudio(): boolean; private getNextChunk; }