/** * audio — paged audio instance with declarative ops. */ /** Time value: seconds as number, or parseable string ('1.5s', '500ms', '1:30') */ type Time = number | string type AudioSource = AudioInstance | AudioBuffer | Float32Array[] | number type FilterType = 'highpass' | 'lowpass' | 'bandpass' | 'notch' | 'eq' | 'lowshelf' | 'highshelf' export interface AudioInstance { /** Decoded PCM pages */ pages: Float32Array[][] /** Per-channel, per-block stats (min/max/energy + registered fields) */ stats: AudioStats /** Sample rate in Hz */ sampleRate: number /** Effective channel count (reflects remix edits) */ readonly channels: number /** Effective sample count (reflects structural edits) */ readonly length: number /** Effective duration in seconds */ readonly duration: number /** Original source reference (URL/path string, or null for PCM-backed) */ source: string | null /** Storage mode */ storage: string /** Promise — resolves to true when ready (decoded, mic active, etc.) */ ready: Promise /** Edit list (inspectable) */ edits: EditOp[] /** Monotonic counter, increments on edit/undo */ version: number /** Current position in seconds (read/write, or use seek()) */ currentTime: number /** True when playing */ playing: boolean /** True when paused */ paused: boolean /** Playback volume, 0 (silent) to 1 (full). Clamped. */ volume: number /** Whether playback is muted (independent of volume) */ muted: boolean /** Playback speed ratio: 1 = normal, 2 = double speed, 0.5 = half. Clamped 0.0625–16. */ playbackRate: number /** Whether playback loops */ loop: boolean /** True when playback ended naturally (not via stop) */ ended: boolean /** True during a seek operation */ seeking: boolean /** Promise — resolves when playback actually starts (speaker opens), rejects on failure */ played: Promise /** Current playback block for visualization */ block: Float32Array | null // ── Events ────────────────────────────────────────────────────── /** Subscribe to instance event */ on(event: 'change', fn: () => void): this on(event: 'metadata', fn: (event: { sampleRate: number, channels: number }) => void): this on(event: 'data', fn: (event: { delta: ProgressDelta, offset: number, sampleRate: number, channels: number }) => void): this on(event: 'progress', fn: (event: { offset: number, total: number }) => void): this on(event: 'timeupdate', fn: (time: number) => void): this on(event: 'ended', fn: () => void): this on(event: 'play', fn: () => void): this on(event: 'pause', fn: () => void): this on(event: 'volumechange', fn: () => void): this on(event: 'ratechange', fn: () => void): this on(event: 'error', fn: (err: Error) => void): this on(event: string, fn: (...args: any[]) => void): this /** Unsubscribe from instance event */ off(event: string, fn: (...args: any[]) => void): this /** Dispose — stop playback/recording, clear listeners, release caches */ dispose(): void // ── Core I/O ──────────────────────────────────────────────────── /** Move playhead — preloads nearby pages, triggers seek if playing */ seek(t: number): this /** Read audio data. Channel option returns single Float32Array. */ read(opts?: { at?: Time, duration?: Time, channel?: number, format?: string, meta?: Record }): Promise /** Async-iterable over materialized blocks. `for await (let block of a)` */ [Symbol.asyncIterator](): AsyncGenerator /** Ensure stats are fresh, return stats + block range */ stat(name: 'db' | 'rms' | 'loudness', opts?: { at?: Time, duration?: Time }): Promise stat(name: 'clipping', opts?: { at?: Time, duration?: Time }): Promise stat(name: 'clipping', opts: { bins: number, at?: Time, duration?: Time }): Promise stat(name: 'dc', opts?: { at?: Time, duration?: Time }): Promise stat(name: 'min' | 'max', opts?: { at?: Time, duration?: Time }): Promise stat(name: 'min' | 'max', opts: { bins: number, at?: Time, duration?: Time, channel?: number }): Promise stat(name: 'min' | 'max', opts: { bins: number, at?: Time, duration?: Time, channel: number[] }): Promise stat(name: 'spectrum', opts?: { bins?: number, at?: Time, duration?: Time, fMin?: number, fMax?: number, weight?: boolean }): Promise stat(name: 'cepstrum', opts?: { bins?: number, at?: Time, duration?: Time }): Promise stat(name: 'silence', opts?: { threshold?: number, minDuration?: number, at?: Time, duration?: Time }): Promise<{ at: number, duration: number }[]> stat(name: T, opts?: { at?: Time, duration?: Time, bins?: number, channel?: number | number[] }): Promise<{ [K in keyof T]: number | Float32Array | Float32Array[] }> stat(name: string, opts?: { at?: Time, duration?: Time, bins?: number, channel?: number | number[] }): Promise spectrum(opts?: { bins?: number, at?: Time, duration?: Time, fMin?: number, fMax?: number, weight?: boolean }): Promise cepstrum(opts?: { bins?: number, at?: Time, duration?: Time }): Promise silence(opts?: { threshold?: number, minDuration?: number, at?: Time, duration?: Time }): Promise<{ at: number, duration: number }[]> /** Serialize to JSON */ toJSON(): { source: string | null, edits: EditOp[], sampleRate: number, channels: number, duration: number } // ── Structural ops ─────────────────────────────────────────── crop(opts?: { at?: Time, duration?: Time }): this insert(other: AudioSource, opts?: { at?: Time }): this remove(opts?: { at?: Time, duration?: Time }): this repeat(times: number, opts?: { at?: Time, duration?: Time }): this pad(before: number, after?: number): this speed(rate: number): this stretch(factor: number): this pitch(semitones: number): this // ── Sample ops ────────────────────────────────────────────── gain(value: number | ((t: number) => number), opts?: { at?: Time, duration?: Time, channel?: number | number[], unit?: 'db' | 'linear' }): this fade(duration: Time, curve?: 'linear' | 'exp' | 'log' | 'cos', opts?: { at?: Time }): this reverse(opts?: { at?: Time, duration?: Time }): this mix(other: AudioSource, opts?: { at?: Time, duration?: Time }): this write(data: Float32Array[] | Float32Array, opts?: { at?: Time }): this remix(channels: number | (number | null)[]): this pan(value: number | ((t: number) => number), opts?: { at?: Time, duration?: Time, channel?: number | number[] }): this // ── Filters ────────────────────────────────────────────────── filter(type: FilterType, ...params: number[]): this filter(fn: (data: Float32Array, params: Record) => Float32Array, opts?: Record): this highpass(freq: number): this lowpass(freq: number): this bandpass(freq: number, Q?: number): this notch(freq: number, Q?: number): this eq(freq: number, gain?: number, Q?: number): this lowshelf(freq: number, gain?: number, Q?: number): this highshelf(freq: number, gain?: number, Q?: number): this // ── Smart ops ─────────────────────────────────────────────── trim(threshold?: number): this normalize(): this normalize(preset: 'streaming' | 'podcast' | 'broadcast'): this normalize(targetDb: number, opts?: 'lufs' | { mode?: 'peak' | 'lufs' | 'rms', at?: Time, duration?: Time, channel?: number | number[] }): this normalize(opts: { target?: number, mode?: 'peak' | 'lufs' | 'rms', at?: Time, duration?: Time, channel?: number | number[], dc?: boolean, ceiling?: number }): this // ── Fns (registered via audio.fn) ─────────────────────────── clip(opts?: { at?: Time, duration?: Time }): AudioInstance split(...offsets: Time[]): AudioInstance[] undo(n?: number): EditOp | EditOp[] | null run(...edits: EditOp[]): this transform(fn: (channels: Float32Array[], ctx: any) => Float32Array[] | false | null): this play(opts?: { at?: Time, duration?: Time, volume?: number, rate?: number, loop?: boolean, paused?: boolean }): this pause(): void resume(): void stop(): this save(target: string | FileSystemWritableFileStream, opts?: { format?: string, at?: Time, duration?: Time, meta?: Record }): Promise encode(format?: string, opts?: { at?: Time, duration?: Time, meta?: Record }): Promise encode(opts?: { at?: Time, duration?: Time, meta?: Record }): Promise clone(): AudioInstance } export interface AudioStats { blockSize: number min: Float32Array[] max: Float32Array[] energy: Float32Array[] [field: string]: number | Float32Array[] } export interface EditOp { type: string [key: string]: any } export interface AudioOpts { sampleRate?: number channels?: number storage?: 'memory' | 'persistent' | 'auto' decode?: 'worker' | 'main' } export interface ProgressDelta { fromBlock: number min: Float32Array[] max: Float32Array[] energy: Float32Array[] } export interface OpDescriptor { process?: (chs: Float32Array[], ctx: Record) => Float32Array[] | false | null plan?: (segs: any[], ctx: Record) => any[] resolve?: (args: any[], ctx: Record) => EditOp | EditOp[] | false | null call?: (std: Function, ...args: any[]) => any ch?: Function } /** Serialized audio instance (from toJSON) */ export interface AudioDocument { source: string | null edits: EditOp[] sampleRate: number channels: number duration: number } /** No source — returns pushable instance. Use .push() to feed PCM, .record() for mic, .stop() to finalize. */ declare function audio(source?: null, opts?: AudioOpts): AudioInstance & { push(data: Float32Array[] | Float32Array | ArrayBufferView, format?: string | { format?: string, channels?: number, sampleRate?: number }): AudioInstance record(opts?: Record): AudioInstance recording: boolean } /** Async entry — decode from file/URL/bytes, wrap PCM/silence, concat from array, or restore from JSON */ /** Sync entry — returns instance immediately. Thenable: `await audio(src)` waits for full decode. */ declare function audio(source: string | URL | ArrayBuffer | Uint8Array | AudioBuffer | Float32Array[] | number | AudioDocument | (AudioInstance | string | URL | ArrayBuffer)[], opts?: AudioOpts): AudioInstance & PromiseLike declare namespace audio { /** Package version */ const version: string /** Samples per PCM page chunk (default 1024 * BLOCK_SIZE). Set before creating instances. */ let PAGE_SIZE: number /** Samples per stat block (default 1024). Set before creating instances. */ let BLOCK_SIZE: number /** OPFS-backed cache backend for large files (browser only) */ function opfsCache(dirName?: string): Promise<{ read(i: number): Promise write(i: number, data: Float32Array[]): Promise has(i: number): Promise evict(i: number): Promise clear(): Promise }> /** Sync entry — from PCM data, AudioBuffer, audio instance (structural copy), silence, function source, or typed array with format */ function from(source: Float32Array[] | AudioBuffer | AudioInstance | number, opts?: AudioOpts): AudioInstance function from(fn: (t: number, i: number) => number | number[], opts: AudioOpts & { duration: number }): AudioInstance function from(source: Int16Array | Int8Array | Uint8Array | Uint16Array, opts: AudioOpts & { format: string }): AudioInstance /** Op registration and query */ function op(): Record function op(name: string): OpDescriptor | undefined function op(name: string, descriptor: OpDescriptor | Function): void /** Stat registration and query */ interface StatDescriptor { /** Per-block computation during decode */ block?: (chs: Float32Array[], ctx: { sampleRate: number, [k: string]: unknown }) => number | number[] /** Reducer for scalar/binned queries: (src, from, to) → number */ reduce?: (src: Float32Array, from: number, to: number) => number /** Derived aggregation from block stats */ query?: (stats: AudioStats, chs: number[], from: number, to: number, sr: number) => any } function stat(): Record function stat(name: string): StatDescriptor | undefined function stat(name: string, descriptor: StatDescriptor | ((chs: Float32Array[], ctx: { sampleRate: number, [k: string]: unknown }) => number | number[])): void /** Audio instance prototype — extensible (like $.fn) */ const fn: Record } export default audio