/** * 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' | 'allpass' 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 /** Container tags: title/artist/album/year/pictures/... + raw format-specific blocks. Writable; persists through save. */ meta: Meta /** Structural markers in output seconds, projected through the edit plan. Writable. */ markers: Marker[] /** Structural regions in output seconds, projected through the edit plan. Writable. */ regions: Region[] // ── 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)` uses default range. */ [Symbol.asyncIterator](): AsyncGenerator /** Ensure stats are fresh, return stats + block range */ stat(name: 'db' | 'rms' | 'loudness' | 'peak' | 'crest', opts?: { at?: Time, duration?: Time, channel?: number | number[] }): 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: 'correlation', 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: 'centroid', opts?: { at?: Time, duration?: Time }): Promise stat(name: 'flatness', opts?: { at?: Time, duration?: Time }): Promise stat(name: 'bpm', opts?: { at?: Time, duration?: Time, minBpm?: number, maxBpm?: number, delta?: number, minConfidence?: number, channel?: number | number[] }): Promise stat(name: 'beats' | 'onsets', opts?: { at?: Time, duration?: Time, minBpm?: number, maxBpm?: number, delta?: number, channel?: number | number[] }): Promise stat(name: 'notes', opts?: { at?: Time, duration?: Time, frameSize?: number, hopSize?: number, threshold?: number, minClarity?: number }): Promise<{ time: number, duration: number, freq: number, midi: number, note: string, clarity: number }[]> stat(name: 'chords', opts?: { at?: Time, duration?: Time, frameSize?: number, hopSize?: number, method?: string, selfProb?: number }): Promise<{ time: number, duration: number, root: number, quality: 'maj' | 'min' | 'N', label: string, confidence: number }[]> stat(name: 'key', opts?: { at?: Time, duration?: Time, frameSize?: number, method?: string }): Promise<{ tonic: number, mode: 'major' | 'minor', label: string, confidence: number, scores?: { label: string, score: 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 }[]> centroid(opts?: { at?: Time, duration?: Time }): Promise flatness(opts?: { at?: Time, duration?: Time }): Promise /** High-fidelity beat/tempo detection via spectral flux (single streaming pass). More precise than stat('bpm'). */ detect(opts?: { at?: Time, duration?: Time, frameSize?: number, hopSize?: number, minBpm?: number, maxBpm?: number, delta?: number }): Promise<{ bpm: number, confidence: number, beats: Float64Array, onsets: Float64Array }> /** 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 /** Time-stretch preserving pitch. Factor may be a fn or curve of source-time seconds — sliding stretch (continuous tempo envelope), duration = ∫factor dt */ stretch(factor: number | ((t: number) => number) | { t: number[], v: number[] }, opts?: { at?: Time, duration?: Time }): 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 in (positive) / out (negative). Adjustable: start/end gain levels (0..1) and mid — position of the half-amplitude point within the fade */ fade(duration: Time, curve?: 'linear' | 'exp' | 'log' | 'cos', opts?: { at?: Time, start?: number, end?: number, mid?: number }): this reverse(opts?: { at?: Time, duration?: Time }): this mix(other: AudioSource, opts?: { at?: Time, duration?: Time }): this crossfade(other: AudioSource, duration?: Time, curve?: 'linear' | 'exp' | 'log' | 'cos'): 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) => void, 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 allpass(freq: number, Q?: number): this // ── Effects ───────────────────────────────────────────────── vocals(mode?: 'isolate' | 'remove'): this dither(bits?: number, opts?: { shape?: boolean }): this crossfeed(freq?: number, level?: number): this /** Band-splitting crossover (LR4, allpass-aligned flat sum) — N split freqs → N+1 bands × channels, band-major */ crossover(...freqs: (number | number[])[]): this resample(targetRate: number, opts?: { type?: 'linear' | 'sinc' }): this // ── Smart ops ─────────────────────────────────────────────── trim(threshold?: number): this /** Compress silent pauses to a target gap (seconds, default 0.3) throughout, or within {at, duration} */ shrink(gap?: number, threshold?: number): this shrink(opts: { gap?: number, threshold?: number, at?: Time, duration?: Time }): 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: (input: Float32Array[], output: Float32Array[], ctx: any) => void): this play(opts?: { at?: Time, duration?: Time, volume?: number, rate?: number, loop?: boolean, paused?: boolean }): this pause(): void resume(): void stop(): this /** Live stats during playback. Listener-gated (zero cost when nothing subscribes). Omit cb for pull-style via probe.value. */ meter(what: string | string[] | MeterOpts, cb?: (value: any) => void): MeterProbe save(target: string | FileSystemWritableFileStream, opts?: { format?: string, at?: Time, duration?: Time, meta?: Meta | false, markers?: Marker[], regions?: Region[] }): Promise encode(format?: string, opts?: { at?: Time, duration?: Time, meta?: Meta | false, markers?: Marker[], regions?: Region[] }): Promise encode(opts?: { at?: Time, duration?: Time, meta?: Meta | false, markers?: Marker[], regions?: Region[] }): Promise clone(): AudioInstance } export interface AudioStats { blockSize: number min: Float32Array[] max: Float32Array[] ms: Float32Array[] energy: Float32Array[] [field: string]: number | Float32Array[] } export type EditOp = [type: string, opts?: Record] /** Normalized container tags. Unknown fields kept under `.raw`. Pass `meta: false` to save() to strip. */ export interface Meta { title?: string artist?: string album?: string albumartist?: string composer?: string genre?: string year?: string track?: string disc?: string bpm?: string key?: string comment?: string copyright?: string isrc?: string publisher?: string software?: string lyrics?: string pictures?: Picture[] /** Format-specific untouched blocks (WAV bext/iXML, ID3v2 frames, FLAC blocks) */ raw?: Record [key: string]: any } /** Cover art / picture. `data` is raw image bytes; `.url` lazy-creates a Blob URL (browser) or data URL (Node). */ export interface Picture { mime: string /** ID3 picture type (3 = cover, 4 = back, ...). Default 3. */ type?: number description?: string data: Uint8Array /** Lazy getter — Blob URL in browser, data URL in Node. */ readonly url: string } /** Point marker, in output seconds. */ export interface Marker { time: number label?: string } /** Time-span region, in output seconds. */ export interface Region { at: number duration: number label?: string } export interface AudioOpts { sampleRate?: number channels?: number storage?: 'memory' | 'persistent' | 'auto' decode?: 'worker' | 'main' /** Host the engine in a Worker (requires `import 'audio/worker'`); pass a Worker instance for a custom entry */ worker?: boolean | Worker } export interface ProgressDelta { fromBlock: number min: Float32Array[] max: Float32Array[] energy: Float32Array[] } export interface MeterProbe { /** Last computed value; undefined until first playback block fires. */ value: any /** Unsubscribe. Safe to call multiple times. */ stop(): void } export interface MeterOpts { /** Stat name(s) — any registered stat, or 'spectrum' for FFT bins. Omit for all block stats. */ type?: string | string[] /** Channel selector — omit = avg, number = that channel, array = per-channel. */ channel?: number | number[] /** One-pole EMA time constant τ in seconds (per-listener smoothing state). */ smoothing?: number /** Peak-hold decay time constant τ in seconds (classic analyzer look). */ hold?: number /** Spectrum: mel bin count. */ bins?: number /** Spectrum: min frequency (Hz). */ fMin?: number /** Spectrum: max frequency (Hz). */ fMax?: number /** Spectrum: apply A-weighting. */ weight?: boolean /** Spectrum: return dB instead of linear magnitude. */ db?: boolean /** Spectrum: FFT size (power of 2, default 1024). */ N?: number } export interface OpDescriptor { params?: string[] process?: (input: Float32Array[], output: Float32Array[], ctx: Record) => void plan?: (segs: any[], ctx: Record) => any[] resolve?: (ctx: Record) => EditOp | EditOp[] | false | null ch?: (channels: number, ctx: Record) => number /** Sample-rate transform (e.g. resample). Return the new rate, or falsy to leave it unchanged. */ sr?: (sampleRate: number, ctx: Record) => number | undefined /** Structural output length for whole-render ops (time-stretch class): input frames → output frames */ frames?: (frames: number, ctx: Record, sampleRate: number) => number /** Hosted contract plugin (audio.js manifest factory), when this op wraps one */ plugin?: Function /** Pure per-sample transform (output depends only on input value) — engine auto-derives min/max/clipping stats by probing `process` with edge values. */ pointwise?: boolean /** Algebraic stats update for advanced cases pointwise can't cover (e.g. rms/dc/energy) — mutate `stats` in place, or return `false` to bail to a full recompute. */ deriveStats?: (stats: AudioStats, opts: Record) => void | false hidden?: boolean } /** 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 /** Page budget from navigator.storage.estimate() — quota/4 clamped 64MB..2GB; null when unavailable */ function detectBudget(): Promise /** 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: (blockValues, from, to) → number */ reduce?: (blockValues: 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 /** Plugin registry — name → package specifier (e.g. 'freeverb' → '@audio/reverb-freeverb/audio'), resolved by use(name) via dynamic import */ const plugins: Record /** @deprecated ≤2.5 name — alias of `plugins` (same object) */ const atoms: Record /** Register plugins: contract factories (audio.js manifests with own `params`), `(audio) => {}` plugin functions, or registry names. String names dynamic-import — returns a promise; direct values register synchronously. */ /** Stat plugin — whole-signal analyzer registered as a.stat(name) */ interface StatPlugin { stat: string, compute(channels: Float32Array[], opts: { sampleRate: number, [k: string]: unknown }): unknown } /** @deprecated ≤2.5 name — alias of StatPlugin */ type StatAtom = StatPlugin /** Codec plugin — extends audio()'s openable formats and save()/encode() targets */ interface CodecPlugin { codec: string, test?(bytes: Uint8Array): boolean, decode?(bytes: Uint8Array): { channelData: Float32Array[], sampleRate: number } | Promise<{ channelData: Float32Array[], sampleRate: number }>, encode?(opts: { sampleRate: number, channels: number }): ((chunk?: Float32Array[]) => Uint8Array) | Promise<(chunk?: Float32Array[]) => Uint8Array> } /** @deprecated ≤2.5 name — alias of CodecPlugin */ type CodecAtom = CodecPlugin /** Registered codec plugins by format name */ const codecs: Record function use(...plugins: (string | Function | StatPlugin | CodecPlugin)[]): typeof audio & Promise /** Audio instance prototype — extensible (like $.fn) */ const fn: Record } export default audio