audio 2.0.0-0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +647 -52
- package/audio.d.ts +232 -0
- package/audio.js +54 -0
- package/bin/cli.js +983 -0
- package/cache.js +89 -0
- package/core.js +602 -0
- package/dist/audio.all.js +105369 -0
- package/dist/audio.js +3490 -0
- package/dist/audio.min.js +14 -0
- package/fn/cepstrum.js +55 -0
- package/fn/clip.js +11 -0
- package/fn/crop.js +19 -0
- package/fn/fade.js +47 -0
- package/fn/filter.js +67 -0
- package/fn/gain.js +16 -0
- package/fn/insert.js +31 -0
- package/fn/loudness.js +102 -0
- package/fn/mix.js +21 -0
- package/fn/normalize.js +80 -0
- package/fn/pad.js +19 -0
- package/fn/pan.js +35 -0
- package/fn/play.js +112 -0
- package/fn/remix.js +25 -0
- package/fn/remove.js +25 -0
- package/fn/repeat.js +35 -0
- package/fn/reverse.js +25 -0
- package/fn/save.js +55 -0
- package/fn/silence.js +34 -0
- package/fn/spectrum.js +104 -0
- package/fn/speed.js +23 -0
- package/fn/split.js +9 -0
- package/fn/stat.js +77 -0
- package/fn/transform.js +6 -0
- package/fn/trim.js +68 -0
- package/fn/write.js +15 -0
- package/{LICENSE → license.md} +21 -21
- package/package.json +64 -19
- package/plan.js +456 -0
- package/stats.js +255 -0
- package/index.js +0 -107
package/audio.d.ts
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* audio — paged audio instance with declarative ops.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/** Time value: seconds as number, or parseable string ('1.5s', '500ms', '1:30') */
|
|
6
|
+
type Time = number | string
|
|
7
|
+
|
|
8
|
+
type AudioSource = AudioInstance | AudioBuffer | Float32Array[] | number
|
|
9
|
+
type FilterType = 'highpass' | 'lowpass' | 'bandpass' | 'notch' | 'eq' | 'lowshelf' | 'highshelf'
|
|
10
|
+
|
|
11
|
+
export interface AudioInstance {
|
|
12
|
+
/** Decoded PCM pages */
|
|
13
|
+
pages: Float32Array[][]
|
|
14
|
+
/** Per-channel, per-block stats (min/max/energy + registered fields) */
|
|
15
|
+
stats: AudioStats
|
|
16
|
+
/** Sample rate in Hz */
|
|
17
|
+
sampleRate: number
|
|
18
|
+
/** Effective channel count (reflects remix edits) */
|
|
19
|
+
readonly channels: number
|
|
20
|
+
/** Effective sample count (reflects structural edits) */
|
|
21
|
+
readonly length: number
|
|
22
|
+
/** Effective duration in seconds */
|
|
23
|
+
readonly duration: number
|
|
24
|
+
/** Original source reference (URL/path string, or null for PCM-backed) */
|
|
25
|
+
source: string | null
|
|
26
|
+
/** Storage mode */
|
|
27
|
+
storage: string
|
|
28
|
+
/** Promise — resolves to true when ready (decoded, mic active, etc.) */
|
|
29
|
+
ready: Promise<true>
|
|
30
|
+
/** Edit list (inspectable) */
|
|
31
|
+
edits: EditOp[]
|
|
32
|
+
/** Monotonic counter, increments on edit/undo */
|
|
33
|
+
version: number
|
|
34
|
+
/** Current position in seconds (read/write, or use seek()) */
|
|
35
|
+
currentTime: number
|
|
36
|
+
/** True when playing */
|
|
37
|
+
playing: boolean
|
|
38
|
+
/** True when paused */
|
|
39
|
+
paused: boolean
|
|
40
|
+
/** Playback volume in dB (0 = unity) */
|
|
41
|
+
volume: number
|
|
42
|
+
/** Whether playback loops */
|
|
43
|
+
loop: boolean
|
|
44
|
+
/** Current playback block for visualization */
|
|
45
|
+
block: Float32Array | null
|
|
46
|
+
|
|
47
|
+
// ── Events ──────────────────────────────────────────────────────
|
|
48
|
+
/** Subscribe to instance event */
|
|
49
|
+
on(event: 'change', fn: () => void): this
|
|
50
|
+
on(event: 'metadata', fn: (event: { sampleRate: number, channels: number }) => void): this
|
|
51
|
+
on(event: 'data', fn: (event: { delta: ProgressDelta, offset: number, sampleRate: number, channels: number }) => void): this
|
|
52
|
+
on(event: 'progress', fn: (event: { offset: number, total: number }) => void): this
|
|
53
|
+
on(event: 'timeupdate', fn: (time: number) => void): this
|
|
54
|
+
on(event: 'ended', fn: () => void): this
|
|
55
|
+
on(event: string, fn: (...args: any[]) => void): this
|
|
56
|
+
/** Unsubscribe from instance event */
|
|
57
|
+
off(event: string, fn: (...args: any[]) => void): this
|
|
58
|
+
/** Dispose — stop playback/recording, clear listeners, release caches */
|
|
59
|
+
dispose(): void
|
|
60
|
+
|
|
61
|
+
// ── Core I/O ────────────────────────────────────────────────────
|
|
62
|
+
/** Move playhead — preloads nearby pages, triggers seek if playing */
|
|
63
|
+
seek(t: number): this
|
|
64
|
+
/** Read audio data. Channel option returns single Float32Array. */
|
|
65
|
+
read(opts?: { at?: Time, duration?: Time, channel?: number, format?: string, meta?: Record<string, any> }): Promise<Float32Array[] | Float32Array | Int16Array[] | Uint8Array[] | Uint8Array>
|
|
66
|
+
/** Async-iterable over materialized blocks. `for await (let block of a)` */
|
|
67
|
+
[Symbol.asyncIterator](): AsyncGenerator<Float32Array[], void, unknown>
|
|
68
|
+
/** Ensure stats are fresh, return stats + block range */
|
|
69
|
+
stat(name: 'db' | 'rms' | 'loudness', opts?: { at?: Time, duration?: Time }): Promise<number>
|
|
70
|
+
stat(name: 'clipping', opts?: { at?: Time, duration?: Time }): Promise<Float32Array>
|
|
71
|
+
stat(name: 'clipping', opts: { bins: number, at?: Time, duration?: Time }): Promise<Float32Array>
|
|
72
|
+
stat(name: 'dc', opts?: { at?: Time, duration?: Time }): Promise<number>
|
|
73
|
+
stat(name: 'min' | 'max', opts?: { at?: Time, duration?: Time }): Promise<number>
|
|
74
|
+
stat(name: 'min' | 'max', opts: { bins: number, at?: Time, duration?: Time, channel?: number }): Promise<Float32Array>
|
|
75
|
+
stat(name: 'min' | 'max', opts: { bins: number, at?: Time, duration?: Time, channel: number[] }): Promise<Float32Array[]>
|
|
76
|
+
stat(name: 'spectrum', opts?: { bins?: number, at?: Time, duration?: Time, fMin?: number, fMax?: number, weight?: boolean }): Promise<Float32Array>
|
|
77
|
+
stat(name: 'cepstrum', opts?: { bins?: number, at?: Time, duration?: Time }): Promise<Float32Array>
|
|
78
|
+
stat(name: 'silence', opts?: { threshold?: number, minDuration?: number, at?: Time, duration?: Time }): Promise<{ at: number, duration: number }[]>
|
|
79
|
+
stat<T extends string[]>(name: T, opts?: { at?: Time, duration?: Time, bins?: number, channel?: number | number[] }): Promise<{ [K in keyof T]: number | Float32Array | Float32Array[] }>
|
|
80
|
+
stat(name: string, opts?: { at?: Time, duration?: Time, bins?: number, channel?: number | number[] }): Promise<number | Float32Array | Float32Array[]>
|
|
81
|
+
spectrum(opts?: { bins?: number, at?: Time, duration?: Time, fMin?: number, fMax?: number, weight?: boolean }): Promise<Float32Array>
|
|
82
|
+
cepstrum(opts?: { bins?: number, at?: Time, duration?: Time }): Promise<Float32Array>
|
|
83
|
+
silence(opts?: { threshold?: number, minDuration?: number, at?: Time, duration?: Time }): Promise<{ at: number, duration: number }[]>
|
|
84
|
+
/** Serialize to JSON */
|
|
85
|
+
toJSON(): { source: string | null, edits: EditOp[], sampleRate: number, channels: number, duration: number }
|
|
86
|
+
|
|
87
|
+
// ── Structural ops ───────────────────────────────────────────
|
|
88
|
+
crop(opts?: { at?: Time, duration?: Time }): this
|
|
89
|
+
insert(other: AudioSource, opts?: { at?: Time }): this
|
|
90
|
+
remove(opts?: { at?: Time, duration?: Time }): this
|
|
91
|
+
repeat(times: number, opts?: { at?: Time, duration?: Time }): this
|
|
92
|
+
pad(before: number, after?: number): this
|
|
93
|
+
speed(rate: number): this
|
|
94
|
+
|
|
95
|
+
// ── Sample ops ──────────────────────────────────────────────
|
|
96
|
+
gain(value: number | ((t: number) => number), opts?: { at?: Time, duration?: Time, channel?: number | number[], unit?: 'db' | 'linear' }): this
|
|
97
|
+
fade(duration: Time, curve?: 'linear' | 'exp' | 'log' | 'cos', opts?: { at?: Time }): this
|
|
98
|
+
reverse(opts?: { at?: Time, duration?: Time }): this
|
|
99
|
+
mix(other: AudioSource, opts?: { at?: Time, duration?: Time }): this
|
|
100
|
+
write(data: Float32Array[] | Float32Array, opts?: { at?: Time }): this
|
|
101
|
+
remix(channels: number | (number | null)[]): this
|
|
102
|
+
pan(value: number | ((t: number) => number), opts?: { at?: Time, duration?: Time, channel?: number | number[] }): this
|
|
103
|
+
|
|
104
|
+
// ── Filters ──────────────────────────────────────────────────
|
|
105
|
+
filter(type: FilterType, ...params: number[]): this
|
|
106
|
+
filter(fn: (data: Float32Array, params: Record<string, unknown>) => Float32Array, opts?: Record<string, unknown>): this
|
|
107
|
+
highpass(freq: number): this
|
|
108
|
+
lowpass(freq: number): this
|
|
109
|
+
bandpass(freq: number, Q?: number): this
|
|
110
|
+
notch(freq: number, Q?: number): this
|
|
111
|
+
eq(freq: number, gain?: number, Q?: number): this
|
|
112
|
+
lowshelf(freq: number, gain?: number, Q?: number): this
|
|
113
|
+
highshelf(freq: number, gain?: number, Q?: number): this
|
|
114
|
+
|
|
115
|
+
// ── Smart ops ───────────────────────────────────────────────
|
|
116
|
+
trim(threshold?: number): this
|
|
117
|
+
normalize(): this
|
|
118
|
+
normalize(preset: 'streaming' | 'podcast' | 'broadcast'): this
|
|
119
|
+
normalize(targetDb: number, opts?: 'lufs' | { mode?: 'peak' | 'lufs' | 'rms', at?: Time, duration?: Time, channel?: number | number[] }): this
|
|
120
|
+
normalize(opts: { target?: number, mode?: 'peak' | 'lufs' | 'rms', at?: Time, duration?: Time, channel?: number | number[], dc?: boolean, ceiling?: number }): this
|
|
121
|
+
|
|
122
|
+
// ── Fns (registered via audio.fn) ───────────────────────────
|
|
123
|
+
clip(opts?: { at?: Time, duration?: Time }): AudioInstance
|
|
124
|
+
split(...offsets: Time[]): AudioInstance[]
|
|
125
|
+
undo(n?: number): EditOp | EditOp[] | null
|
|
126
|
+
run(...edits: EditOp[]): this
|
|
127
|
+
transform(fn: (channels: Float32Array[], ctx: any) => Float32Array[] | false | null): this
|
|
128
|
+
play(opts?: { at?: Time, duration?: Time, volume?: number, loop?: boolean, paused?: boolean }): this
|
|
129
|
+
pause(): void
|
|
130
|
+
resume(): void
|
|
131
|
+
stop(): this
|
|
132
|
+
save(target: string | FileSystemWritableFileStream, opts?: { format?: string, at?: Time, duration?: Time, meta?: Record<string, any> }): Promise<void>
|
|
133
|
+
encode(format?: string, opts?: { at?: Time, duration?: Time, meta?: Record<string, any> }): Promise<Uint8Array>
|
|
134
|
+
encode(opts?: { at?: Time, duration?: Time, meta?: Record<string, any> }): Promise<Uint8Array>
|
|
135
|
+
clone(): AudioInstance
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface AudioStats {
|
|
139
|
+
blockSize: number
|
|
140
|
+
min: Float32Array[]
|
|
141
|
+
max: Float32Array[]
|
|
142
|
+
energy: Float32Array[]
|
|
143
|
+
[field: string]: number | Float32Array[]
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface EditOp {
|
|
147
|
+
type: string
|
|
148
|
+
[key: string]: any
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
export interface AudioOpts {
|
|
153
|
+
sampleRate?: number
|
|
154
|
+
channels?: number
|
|
155
|
+
storage?: 'memory' | 'persistent' | 'auto'
|
|
156
|
+
decode?: 'worker' | 'main'
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface ProgressDelta {
|
|
160
|
+
fromBlock: number
|
|
161
|
+
min: Float32Array[]
|
|
162
|
+
max: Float32Array[]
|
|
163
|
+
energy: Float32Array[]
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface OpDescriptor {
|
|
167
|
+
process?: (chs: Float32Array[], ctx: Record<string, any>) => Float32Array[] | false | null
|
|
168
|
+
plan?: (segs: any[], ctx: Record<string, any>) => any[]
|
|
169
|
+
resolve?: (args: any[], ctx: Record<string, any>) => EditOp | EditOp[] | false | null
|
|
170
|
+
call?: (std: Function, ...args: any[]) => any
|
|
171
|
+
ch?: Function
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Serialized audio instance (from toJSON) */
|
|
175
|
+
export interface AudioDocument {
|
|
176
|
+
source: string | null
|
|
177
|
+
edits: EditOp[]
|
|
178
|
+
sampleRate: number
|
|
179
|
+
channels: number
|
|
180
|
+
duration: number
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** No source — returns pushable instance. Use .push() to feed PCM, .record() for mic, .stop() to finalize. */
|
|
184
|
+
declare function audio(source?: null, opts?: AudioOpts): AudioInstance & {
|
|
185
|
+
push(data: Float32Array[] | Float32Array | ArrayBufferView, format?: string | { format?: string, channels?: number, sampleRate?: number }): AudioInstance
|
|
186
|
+
record(opts?: Record<string, any>): AudioInstance
|
|
187
|
+
recording: boolean
|
|
188
|
+
}
|
|
189
|
+
/** Async entry — decode from file/URL/bytes, wrap PCM/silence, concat from array, or restore from JSON */
|
|
190
|
+
/** Sync entry — returns instance immediately. Thenable: `await audio(src)` waits for full decode. */
|
|
191
|
+
declare function audio(source: string | URL | ArrayBuffer | Uint8Array | AudioBuffer | Float32Array[] | number | AudioDocument | (AudioInstance | string | URL | ArrayBuffer)[], opts?: AudioOpts): AudioInstance & PromiseLike<AudioInstance>
|
|
192
|
+
|
|
193
|
+
declare namespace audio {
|
|
194
|
+
/** Package version */
|
|
195
|
+
const version: string
|
|
196
|
+
/** Samples per PCM page chunk (default 1024 * BLOCK_SIZE). Set before creating instances. */
|
|
197
|
+
let PAGE_SIZE: number
|
|
198
|
+
/** Samples per stat block (default 1024). Set before creating instances. */
|
|
199
|
+
let BLOCK_SIZE: number
|
|
200
|
+
/** OPFS-backed cache backend for large files (browser only) */
|
|
201
|
+
function opfsCache(dirName?: string): Promise<{
|
|
202
|
+
read(i: number): Promise<Float32Array[]>
|
|
203
|
+
write(i: number, data: Float32Array[]): Promise<void>
|
|
204
|
+
has(i: number): Promise<boolean>
|
|
205
|
+
evict(i: number): Promise<void>
|
|
206
|
+
clear(): Promise<void>
|
|
207
|
+
}>
|
|
208
|
+
/** Sync entry — from PCM data, AudioBuffer, audio instance (structural copy), silence, function source, or typed array with format */
|
|
209
|
+
function from(source: Float32Array[] | AudioBuffer | AudioInstance | number, opts?: AudioOpts): AudioInstance
|
|
210
|
+
function from(fn: (t: number, i: number) => number | number[], opts: AudioOpts & { duration: number }): AudioInstance
|
|
211
|
+
function from(source: Int16Array | Int8Array | Uint8Array | Uint16Array, opts: AudioOpts & { format: string }): AudioInstance
|
|
212
|
+
/** Op registration and query */
|
|
213
|
+
function op(): Record<string, OpDescriptor>
|
|
214
|
+
function op(name: string): OpDescriptor | undefined
|
|
215
|
+
function op(name: string, descriptor: OpDescriptor | Function): void
|
|
216
|
+
/** Stat registration and query */
|
|
217
|
+
interface StatDescriptor {
|
|
218
|
+
/** Per-block computation during decode */
|
|
219
|
+
block?: (chs: Float32Array[], ctx: { sampleRate: number, [k: string]: unknown }) => number | number[]
|
|
220
|
+
/** Reducer for scalar/binned queries: (src, from, to) → number */
|
|
221
|
+
reduce?: (src: Float32Array, from: number, to: number) => number
|
|
222
|
+
/** Derived aggregation from block stats */
|
|
223
|
+
query?: (stats: AudioStats, chs: number[], from: number, to: number, sr: number) => any
|
|
224
|
+
}
|
|
225
|
+
function stat(): Record<string, StatDescriptor>
|
|
226
|
+
function stat(name: string): StatDescriptor | undefined
|
|
227
|
+
function stat(name: string, descriptor: StatDescriptor | ((chs: Float32Array[], ctx: { sampleRate: number, [k: string]: unknown }) => number | number[])): void
|
|
228
|
+
/** Audio instance prototype — extensible (like $.fn) */
|
|
229
|
+
const fn: Record<string, any>
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export default audio
|
package/audio.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* audio — full bundle with all built-in ops, stats, and methods.
|
|
3
|
+
*
|
|
4
|
+
* import audio from 'audio'
|
|
5
|
+
* let a = await audio('file.mp3')
|
|
6
|
+
* a.gain(-3).trim().normalize()
|
|
7
|
+
* await a.save('out.wav')
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import audio from './core.js'
|
|
11
|
+
export { default } from './core.js'
|
|
12
|
+
export { parseTime } from './core.js'
|
|
13
|
+
export { render } from './plan.js'
|
|
14
|
+
|
|
15
|
+
// ── Infrastructure (self-register on import) ────────────────────────────
|
|
16
|
+
|
|
17
|
+
import './cache.js'
|
|
18
|
+
import './stats.js'
|
|
19
|
+
import './plan.js'
|
|
20
|
+
|
|
21
|
+
// ── Methods ─────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
import './fn/clip.js'
|
|
24
|
+
import './fn/split.js'
|
|
25
|
+
import './fn/play.js'
|
|
26
|
+
import './fn/save.js'
|
|
27
|
+
|
|
28
|
+
// ── Ops ─────────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
import './fn/crop.js'
|
|
31
|
+
import './fn/remove.js'
|
|
32
|
+
import './fn/insert.js'
|
|
33
|
+
import './fn/repeat.js'
|
|
34
|
+
import './fn/gain.js'
|
|
35
|
+
import './fn/fade.js'
|
|
36
|
+
import './fn/reverse.js'
|
|
37
|
+
import './fn/mix.js'
|
|
38
|
+
import './fn/write.js'
|
|
39
|
+
import './fn/remix.js'
|
|
40
|
+
import './fn/trim.js'
|
|
41
|
+
import './fn/normalize.js'
|
|
42
|
+
import './fn/filter.js'
|
|
43
|
+
import './fn/pan.js'
|
|
44
|
+
import './fn/pad.js'
|
|
45
|
+
import './fn/speed.js'
|
|
46
|
+
import './fn/transform.js'
|
|
47
|
+
|
|
48
|
+
// ── Stats ───────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
import './fn/stat.js'
|
|
51
|
+
import './fn/loudness.js'
|
|
52
|
+
import './fn/spectrum.js'
|
|
53
|
+
import './fn/cepstrum.js'
|
|
54
|
+
import './fn/silence.js'
|