@weasel-js/audio 1.7.0 → 1.7.2
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 +109 -0
- package/dist/index.d.ts +441 -2
- package/dist/index.js +995 -109
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
/** Options for `createAnalyserTap` and `AudioEngine.analyser`. */
|
|
1
2
|
interface AnalyserTapOptions {
|
|
2
3
|
/** Power of two, 32..32768. Default 2048. */
|
|
3
4
|
fftSize?: number;
|
|
4
5
|
}
|
|
6
|
+
/** An `AnalyserNode` attached to a point in the graph, with readers for its
|
|
7
|
+
* spectrum, waveform and level. Each call reads the current window. */
|
|
5
8
|
interface AnalyserTap {
|
|
6
9
|
/** The underlying node, exposed for disposal assertions and advanced wiring. */
|
|
7
10
|
node: AnalyserNode;
|
|
@@ -19,8 +22,67 @@ interface AnalyserTap {
|
|
|
19
22
|
/** Detach from the tapped node. Idempotent. */
|
|
20
23
|
dispose(): void;
|
|
21
24
|
}
|
|
25
|
+
/** Attach a new `AnalyserNode` to `source`. The tap only listens, adding no
|
|
26
|
+
* path to the destination; `dispose()` detaches it. */
|
|
22
27
|
declare function createAnalyserTap(ctx: AudioContext, source: AudioNode, opts?: AnalyserTapOptions): AnalyserTap;
|
|
23
28
|
|
|
29
|
+
/** A one-shot timer pair, as `AudioEngineOptions.setTimer` takes it. */
|
|
30
|
+
interface Timers {
|
|
31
|
+
setTimer(cb: () => void, ms: number): unknown;
|
|
32
|
+
clearTimer(handle: unknown): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Anything with an input and an output node can sit in an insert slot: one of
|
|
37
|
+
* the built-ins (`createFilterEffect` and the rest), or a pair of nodes of your
|
|
38
|
+
* own — `{ input: shaper, output: shaper }` for a single node. Several nodes
|
|
39
|
+
* between the two are fine too. An effect goes in one slot, once.
|
|
40
|
+
*/
|
|
41
|
+
interface InsertEffect {
|
|
42
|
+
readonly input: AudioNode;
|
|
43
|
+
readonly output: AudioNode;
|
|
44
|
+
/** Called once the effect has left the graph after `remove()` or `clear()`. */
|
|
45
|
+
dispose?(): void;
|
|
46
|
+
}
|
|
47
|
+
/** One effect's place in a bus's insert chain. Every edit is click-free: a
|
|
48
|
+
* bypass crossfades the slot's wet and dry, and a structural edit crossfades
|
|
49
|
+
* the old route into the new one. */
|
|
50
|
+
interface InsertSlot {
|
|
51
|
+
readonly effect: InsertEffect;
|
|
52
|
+
/** Route around the effect (`true`) or back through it. */
|
|
53
|
+
bypass(on: boolean): void;
|
|
54
|
+
bypassed(): boolean;
|
|
55
|
+
/** Clamped to the chain. */
|
|
56
|
+
moveTo(index: number): void;
|
|
57
|
+
/** Take the effect out, then call its `dispose`. Idempotent. */
|
|
58
|
+
remove(): void;
|
|
59
|
+
/** Position in the chain, or -1 once removed. */
|
|
60
|
+
index(): number;
|
|
61
|
+
}
|
|
62
|
+
interface InsertAddOptions {
|
|
63
|
+
/** Default: the end of the chain. Clamped. */
|
|
64
|
+
index?: number;
|
|
65
|
+
/** Add it routed around, for a later `bypass(false)`. Default false. */
|
|
66
|
+
bypassed?: boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* A bus's ordered insert effects, between where voices connect and the bus's
|
|
70
|
+
* fader. `slots()` is the chain as edited; the audio graph follows one
|
|
71
|
+
* crossfade at a time, and `settled()` resolves once it has caught up.
|
|
72
|
+
*/
|
|
73
|
+
interface InsertChain {
|
|
74
|
+
add(effect: InsertEffect, opts?: InsertAddOptions): InsertSlot;
|
|
75
|
+
slots(): InsertSlot[];
|
|
76
|
+
clear(): void;
|
|
77
|
+
settled(): Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
interface InsertChainOptions extends Partial<Timers> {
|
|
80
|
+
/** Crossfade length for every edit, in ms. Default 15. */
|
|
81
|
+
fadeMs?: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Controls for one named bus. `rampMs` slews a gain change over that many
|
|
85
|
+
* ms instead of stepping it. */
|
|
24
86
|
interface BusHandle {
|
|
25
87
|
setGain(value: number, rampMs?: number): void;
|
|
26
88
|
mute(on: boolean): void;
|
|
@@ -34,13 +96,23 @@ interface BusHandle {
|
|
|
34
96
|
/** Whether the bus is being heard: unmuted, and soloed if any bus is soloed.
|
|
35
97
|
* Not derivable from `muted()` and `soloed()` alone. */
|
|
36
98
|
audible(): boolean;
|
|
99
|
+
/** Effects between where the bus's voices connect and its fader. */
|
|
100
|
+
readonly inserts: InsertChain;
|
|
37
101
|
}
|
|
102
|
+
/** The mix graph built by `createBusGraph`. `input(name)` is where a voice
|
|
103
|
+
* connects; it runs through the bus's inserts to `node(name)`, the fader,
|
|
104
|
+
* which feeds `master`, which feeds the destination. Unknown bus names throw. */
|
|
38
105
|
interface BusGraph {
|
|
39
106
|
master: GainNode;
|
|
107
|
+
input(name: string): GainNode;
|
|
40
108
|
node(name: string): GainNode;
|
|
41
109
|
bus(name: string): BusHandle;
|
|
42
110
|
names(): string[];
|
|
111
|
+
/** Cancel every insert transition in flight. The nodes stay wired. */
|
|
112
|
+
dispose(): void;
|
|
43
113
|
}
|
|
114
|
+
/** Options for `createBusGraph`: the insert chains' crossfade and timer. */
|
|
115
|
+
type BusGraphOptions = InsertChainOptions;
|
|
44
116
|
/**
|
|
45
117
|
* The mix graph: one `GainNode` per named bus, all routed to a master that
|
|
46
118
|
* routes to the destination.
|
|
@@ -51,12 +123,13 @@ interface BusGraph {
|
|
|
51
123
|
* `writeParam`, so `rampMs` survives instead of being overwritten by the
|
|
52
124
|
* recomputation that follows it.
|
|
53
125
|
*/
|
|
54
|
-
declare function createBusGraph(ctx: AudioContext, names: string[]): BusGraph;
|
|
126
|
+
declare function createBusGraph(ctx: AudioContext, names: string[], opts?: BusGraphOptions): BusGraph;
|
|
55
127
|
|
|
56
128
|
/** Opaque reference to a decoded sound. Mirrors `TextureHandle` in core. */
|
|
57
129
|
interface SoundHandle {
|
|
58
130
|
readonly id: string;
|
|
59
131
|
}
|
|
132
|
+
/** Decoded buffers behind opaque `SoundHandle`s. Nothing is ever evicted. */
|
|
60
133
|
interface SoundCache {
|
|
61
134
|
load(url: string): Promise<SoundHandle>;
|
|
62
135
|
loadAll(urls: Record<string, string>): Promise<Record<string, SoundHandle>>;
|
|
@@ -66,12 +139,18 @@ interface SoundCache {
|
|
|
66
139
|
register(buffer: AudioBuffer): SoundHandle;
|
|
67
140
|
buffer(handle: SoundHandle): AudioBuffer | null;
|
|
68
141
|
}
|
|
142
|
+
/** Create a cache that decodes through `ctx`. `load` fetches with `fetchFn`,
|
|
143
|
+
* returns the existing handle for a url already loaded, and shares one fetch
|
|
144
|
+
* between concurrent loads of it; a failed load is not cached. */
|
|
69
145
|
declare function createSoundCache(ctx: AudioContext, fetchFn?: typeof fetch): SoundCache;
|
|
70
146
|
|
|
147
|
+
/** A 2D world position, in the consumer's own units. */
|
|
71
148
|
interface Vec2 {
|
|
72
149
|
x: number;
|
|
73
150
|
y: number;
|
|
74
151
|
}
|
|
152
|
+
/** Distance and pan model for `spatialize`, in the same units as the
|
|
153
|
+
* positions. */
|
|
75
154
|
interface SpatialOptions {
|
|
76
155
|
/** Distance within which gain stays at 1. Default 1. The inverse model needs
|
|
77
156
|
* this above 0: at 0 it cliffs gain from 1 to 0 at any nonzero distance. */
|
|
@@ -96,19 +175,64 @@ declare function spatialize(source: Vec2, listener: Vec2, opts?: SpatialOptions)
|
|
|
96
175
|
pan: number;
|
|
97
176
|
};
|
|
98
177
|
|
|
178
|
+
/** An ADSR envelope. Times are ms; `sustain` is a level, 0..1, of the peak. */
|
|
179
|
+
interface Envelope {
|
|
180
|
+
/** Default 5 — long enough that the onset does not click. */
|
|
181
|
+
attack?: number;
|
|
182
|
+
/** Default 0. */
|
|
183
|
+
decay?: number;
|
|
184
|
+
/** Default 1. */
|
|
185
|
+
sustain?: number;
|
|
186
|
+
/** Default 30. */
|
|
187
|
+
release?: number;
|
|
188
|
+
}
|
|
189
|
+
type ResolvedEnvelope = Required<Envelope>;
|
|
190
|
+
interface EnvelopePoint {
|
|
191
|
+
/** ms from the note's start. */
|
|
192
|
+
at: number;
|
|
193
|
+
value: number;
|
|
194
|
+
}
|
|
195
|
+
declare function resolveEnvelope(env?: Envelope): ResolvedEnvelope;
|
|
196
|
+
/** The level `t` ms after the start. With `gate`, the release begins that many
|
|
197
|
+
* ms in, from whatever level the envelope had reached. */
|
|
198
|
+
declare function envelopeLevel(e: ResolvedEnvelope, t: number, gate?: number): number;
|
|
199
|
+
/**
|
|
200
|
+
* Breakpoints joined by linear ramps. Without `gate` they end at the sustain,
|
|
201
|
+
* held until something releases the note; with it they end at zero, `gate +
|
|
202
|
+
* release` ms in.
|
|
203
|
+
*/
|
|
204
|
+
declare function envelopePoints(e: ResolvedEnvelope, gate?: number): EnvelopePoint[];
|
|
205
|
+
|
|
206
|
+
/** A pitch: hertz as a number, a scientific pitch name (`'A4'`, `'C#3'`,
|
|
207
|
+
* `'Bb2'`), or a MIDI note number wrapped as `{ midi }` so it cannot be read
|
|
208
|
+
* as hertz. */
|
|
209
|
+
type Pitch = number | string | {
|
|
210
|
+
midi: number;
|
|
211
|
+
};
|
|
212
|
+
/** Equal temperament, A4 = MIDI 69 = 440 Hz. Fractional notes are cents. */
|
|
213
|
+
declare function midiToFrequency(midi: number): number;
|
|
214
|
+
/** MIDI number of a scientific pitch name: C4 is 60. Accepts any run of `#`
|
|
215
|
+
* or `b` after the letter. */
|
|
216
|
+
declare function noteToMidi(name: string): number;
|
|
217
|
+
declare function toFrequency(pitch: Pitch): number;
|
|
218
|
+
|
|
219
|
+
/** Which voice a full pool evicts: the earliest started, or the lowest gain. */
|
|
99
220
|
type StealPolicy = 'oldest' | 'quietest';
|
|
221
|
+
/** Options for `createVoicePool`. */
|
|
100
222
|
interface VoicePoolOptions {
|
|
101
223
|
/** Maximum concurrent voices, at least 1. Beyond this, `acquire` steals. */
|
|
102
224
|
limit: number;
|
|
103
225
|
/** Default 'oldest'. */
|
|
104
226
|
steal?: StealPolicy;
|
|
105
227
|
}
|
|
228
|
+
/** What the pool knows about a voice, for choosing one to steal. */
|
|
106
229
|
interface VoiceRecord {
|
|
107
230
|
/** Engine time the voice started, in ms. */
|
|
108
231
|
startedAt: number;
|
|
109
232
|
/** Current gain, consulted by the 'quietest' steal policy. */
|
|
110
233
|
gain: number;
|
|
111
234
|
}
|
|
235
|
+
/** The result of `VoicePool.acquire`. */
|
|
112
236
|
interface Acquisition {
|
|
113
237
|
slot: number;
|
|
114
238
|
/** Identifies this voice, not its slot: a stolen slot is reissued at once,
|
|
@@ -118,6 +242,7 @@ interface Acquisition {
|
|
|
118
242
|
* responsible for actually stopping that voice's nodes. */
|
|
119
243
|
stolen: number | null;
|
|
120
244
|
}
|
|
245
|
+
/** Slot accounting for concurrent voices. See `createVoicePool`. */
|
|
121
246
|
interface VoicePool {
|
|
122
247
|
acquire(record: VoiceRecord): Acquisition;
|
|
123
248
|
release(slot: number, token: number): void;
|
|
@@ -135,6 +260,7 @@ interface VoicePool {
|
|
|
135
260
|
*/
|
|
136
261
|
declare function createVoicePool(opts: VoicePoolOptions): VoicePool;
|
|
137
262
|
|
|
263
|
+
/** Options for `AudioEngine.play`. */
|
|
138
264
|
interface PlayOptions {
|
|
139
265
|
/** Default: the first configured bus. */
|
|
140
266
|
bus?: string;
|
|
@@ -155,10 +281,16 @@ interface PlayOptions {
|
|
|
155
281
|
cancelKey?: string;
|
|
156
282
|
onDone?: () => void;
|
|
157
283
|
}
|
|
284
|
+
/** Controls for one voice from `play()`. Safe to keep past the voice's end:
|
|
285
|
+
* every setter is then a no-op and `isPlaying()` is false. */
|
|
158
286
|
interface VoiceHandle {
|
|
159
287
|
id: number;
|
|
160
288
|
/** `fadeMs` ramps the voice out and stops it at the end of the ramp. */
|
|
161
289
|
stop(fadeMs?: number): void;
|
|
290
|
+
/** Note-off: a synth voice runs its envelope's release from whatever level it
|
|
291
|
+
* has reached. A buffer voice has no envelope, so this is `stop()`. Either
|
|
292
|
+
* one released before it starts is canceled. */
|
|
293
|
+
release(): void;
|
|
162
294
|
setGain(value: number, rampMs?: number): void;
|
|
163
295
|
/** Playback rate. Applies to a voice booked for a future `when` too. */
|
|
164
296
|
setRate(value: number): void;
|
|
@@ -169,6 +301,110 @@ interface VoiceHandle {
|
|
|
169
301
|
setPosition(p: Vec2): void;
|
|
170
302
|
isPlaying(): boolean;
|
|
171
303
|
}
|
|
304
|
+
/** One sine partial of an inharmonic voice. */
|
|
305
|
+
interface SynthPartial {
|
|
306
|
+
/** Frequency as a multiple of the note's pitch. Any positive number: this is
|
|
307
|
+
* what reaches off the harmonic series. */
|
|
308
|
+
ratio: number;
|
|
309
|
+
/** Relative level, default 1. The voice's partials are scaled so their levels
|
|
310
|
+
* sum to 1, so the peak cannot pass the note's gain. */
|
|
311
|
+
gain?: number;
|
|
312
|
+
/** Exponential decay time constant in ms: the partial falls to 1/e of its
|
|
313
|
+
* level every `decay` ms, under the note's envelope. Omit to hold it. */
|
|
314
|
+
decay?: number;
|
|
315
|
+
}
|
|
316
|
+
/** A voice built from sine oscillators at arbitrary ratios of the pitch, each
|
|
317
|
+
* with its own level and decay: a bell, a bar, struck metal. */
|
|
318
|
+
interface Inharmonic {
|
|
319
|
+
partials: readonly SynthPartial[];
|
|
320
|
+
}
|
|
321
|
+
/** An oscillator's waveform: one of Web Audio's built-in shapes; the
|
|
322
|
+
* amplitudes of harmonic partials — `[1, 0.5, 0.25]` is the fundamental at
|
|
323
|
+
* full level, the second harmonic at half and the third at a quarter, normalized
|
|
324
|
+
* so only their ratios matter; or `{ partials }`, sines at any ratio. */
|
|
325
|
+
type Waveform = 'sine' | 'square' | 'sawtooth' | 'triangle' | readonly number[] | Inharmonic;
|
|
326
|
+
/** A `BiquadFilterNode` inside a synth voice, ahead of its amplitude envelope,
|
|
327
|
+
* with an envelope of its own on the cutoff. */
|
|
328
|
+
interface VoiceFilter {
|
|
329
|
+
/** Default 'lowpass'. */
|
|
330
|
+
type?: BiquadFilterType;
|
|
331
|
+
/** Cutoff or center in Hz, where the frequency envelope starts and ends. */
|
|
332
|
+
frequency: number;
|
|
333
|
+
/** Default 1. */
|
|
334
|
+
Q?: number;
|
|
335
|
+
/** dB, for the shelf and peaking types. */
|
|
336
|
+
gain?: number;
|
|
337
|
+
/** Shapes the cutoff: it sits at `frequency + amount × level`, `level` being
|
|
338
|
+
* this envelope's 0..1. It shares the note's gate and is released with it.
|
|
339
|
+
* A thump is a lowpass with a positive `amount` and a short decay to
|
|
340
|
+
* `sustain: 0`. Default: the cutoff holds at `frequency`. */
|
|
341
|
+
envelope?: Envelope;
|
|
342
|
+
/** Hz added at the envelope's peak; negative sweeps the cutoff down. Default 0. */
|
|
343
|
+
amount?: number;
|
|
344
|
+
}
|
|
345
|
+
/** The three noise colors: white is flat, pink falls 3 dB an octave, brown 6. */
|
|
346
|
+
type NoiseColor = 'white' | 'pink' | 'brown';
|
|
347
|
+
/** A pitch sweep from the note's own pitch. */
|
|
348
|
+
interface Glide {
|
|
349
|
+
to: Pitch;
|
|
350
|
+
/** Default: the note's `duration`, or 100 ms for a held note. */
|
|
351
|
+
ms?: number;
|
|
352
|
+
/** Default 'exponential', which moves evenly in pitch; 'linear' moves evenly
|
|
353
|
+
* in hertz. */
|
|
354
|
+
curve?: 'linear' | 'exponential';
|
|
355
|
+
}
|
|
356
|
+
/** The timbre of a note, apart from its pitch — spread one into several
|
|
357
|
+
* `playNote` calls to reuse it. */
|
|
358
|
+
interface SynthPatch {
|
|
359
|
+
/** Default 'sine'. */
|
|
360
|
+
wave?: Waveform;
|
|
361
|
+
envelope?: Envelope;
|
|
362
|
+
glide?: Glide;
|
|
363
|
+
filter?: VoiceFilter;
|
|
364
|
+
}
|
|
365
|
+
/** Options for `AudioEngine.playNote`. Routing, level, position, timing and
|
|
366
|
+
* `cancelKey` mean what they mean for `play()`. */
|
|
367
|
+
interface NoteOptions extends SynthPatch, Omit<PlayOptions, 'rate' | 'loop'> {
|
|
368
|
+
pitch: Pitch;
|
|
369
|
+
/** Gate length in ms: how long the note sounds before its release begins.
|
|
370
|
+
* Omit to hold it until `release()`. */
|
|
371
|
+
duration?: number;
|
|
372
|
+
}
|
|
373
|
+
/** The timbre of a noise voice — spread one into several `playNoise` calls to
|
|
374
|
+
* reuse it. */
|
|
375
|
+
interface NoisePatch {
|
|
376
|
+
noise: NoiseColor;
|
|
377
|
+
envelope?: Envelope;
|
|
378
|
+
filter?: VoiceFilter;
|
|
379
|
+
}
|
|
380
|
+
/** Options for `AudioEngine.playNoise`. Everything but the source means what it
|
|
381
|
+
* means for `playNote`; `rate` and `detune` retune the looping noise buffer,
|
|
382
|
+
* which shifts a pink or brown noise's color, and leave the filter where it is. */
|
|
383
|
+
interface NoiseOptions extends NoisePatch, Omit<PlayOptions, 'loop'> {
|
|
384
|
+
/** Gate length in ms. Omit to hold the noise until `release()`. */
|
|
385
|
+
duration?: number;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Options for `AudioEngine.stream`. Routing, level, position, `rate`, `loop`,
|
|
389
|
+
* `cancelKey` and `onDone` mean what they mean for `play()`, with these
|
|
390
|
+
* differences, all from playing through a media element rather than a buffer:
|
|
391
|
+
*
|
|
392
|
+
* - No `when`: the element starts once it has buffered enough, which is not
|
|
393
|
+
* sample-accurate and cannot be booked against the audio clock.
|
|
394
|
+
* - No `detune`, and `setDetune` does nothing. `rate` is the element's
|
|
395
|
+
* `playbackRate`, which keeps pitch unless the element's `preservesPitch`
|
|
396
|
+
* is false.
|
|
397
|
+
* - The stream counts toward its bus's voice limit and can be stolen like any
|
|
398
|
+
* voice — give music a bus of its own.
|
|
399
|
+
*/
|
|
400
|
+
interface StreamOptions extends Omit<PlayOptions, 'when' | 'detune'> {
|
|
401
|
+
/** Where to start, in ms into the media. Default: wherever the element is. */
|
|
402
|
+
offset?: number;
|
|
403
|
+
/** Set on an element made from a URL. A cross-origin URL needs CORS and
|
|
404
|
+
* `'anonymous'`, or the graph receives silence. */
|
|
405
|
+
crossOrigin?: '' | 'anonymous' | 'use-credentials';
|
|
406
|
+
}
|
|
407
|
+
/** Options for `createAudioEngine`. */
|
|
172
408
|
interface AudioEngineOptions {
|
|
173
409
|
/** Injectable for tests and for consumers that own the context. */
|
|
174
410
|
context?: AudioContext;
|
|
@@ -188,8 +424,13 @@ interface AudioEngineOptions {
|
|
|
188
424
|
* replaces the default, and the missing half falls back to `setTimeout`'s. */
|
|
189
425
|
setTimer?: (cb: () => void, ms: number) => unknown;
|
|
190
426
|
clearTimer?: (handle: unknown) => void;
|
|
427
|
+
/** Crossfade for every insert-chain edit and bypass, in ms. Default 15. */
|
|
428
|
+
insertFadeMs?: number;
|
|
191
429
|
}
|
|
192
430
|
|
|
431
|
+
/** A sound engine: named mix buses, a per-bus voice pool, lookahead
|
|
432
|
+
* scheduling and 2D spatialization over one `AudioContext`. Times are engine
|
|
433
|
+
* ms (`now()`). */
|
|
193
434
|
interface AudioEngine {
|
|
194
435
|
/** The engine's `AudioContext` — the one passed as `options.context`, or the
|
|
195
436
|
* one the engine created. Use it for `createBuffer`, for analysis, or for a
|
|
@@ -211,6 +452,25 @@ interface AudioEngine {
|
|
|
211
452
|
* recording. `load` and `decode` both assume encoded bytes. */
|
|
212
453
|
register(buffer: AudioBuffer): SoundHandle;
|
|
213
454
|
play(sound: SoundHandle, opts?: PlayOptions): VoiceHandle;
|
|
455
|
+
/** Play a synthesized note: an oscillator with harmonic partials, or summed
|
|
456
|
+
* sines at any ratios, under an ADSR envelope and an optional filter. It is a voice like any other — same handle, same bus pool
|
|
457
|
+
* and stealing, same `cancelKey`. */
|
|
458
|
+
playNote(note: NoteOptions): VoiceHandle;
|
|
459
|
+
/** Play noise — white, pink or brown, from a looping buffer made once per
|
|
460
|
+
* context — under an envelope and an optional filter with its own cutoff
|
|
461
|
+
* envelope. A voice like `playNote`'s in every other respect. */
|
|
462
|
+
playNoise(noise: NoiseOptions): VoiceHandle;
|
|
463
|
+
/** Stream long audio — music, ambience — from a media element or a URL
|
|
464
|
+
* through a `MediaElementAudioSourceNode`, instead of decoding it whole. It
|
|
465
|
+
* is a voice on a bus like any other, with the exceptions `StreamOptions`
|
|
466
|
+
* lists. An element has one playhead, so streaming one that is already
|
|
467
|
+
* streaming stops the earlier voice. */
|
|
468
|
+
stream(media: HTMLMediaElement | string, opts?: StreamOptions): VoiceHandle;
|
|
469
|
+
/** Book a callback against the audio clock through the engine's lookahead
|
|
470
|
+
* scheduler; it receives its own `when` to hand on to `play` or `playNote`.
|
|
471
|
+
* `stopKey(key)` cancels it. One that came due during a suspension fires
|
|
472
|
+
* late on resume, with its original `when`, so compare against `now()`. */
|
|
473
|
+
schedule(when: number, fire: (when: number) => void, key?: string): void;
|
|
214
474
|
stopKey(key: string): void;
|
|
215
475
|
stopAll(): void;
|
|
216
476
|
bus(name: string): BusHandle;
|
|
@@ -223,8 +483,92 @@ interface AudioEngine {
|
|
|
223
483
|
setListener(p: Vec2, opts?: SpatialOptions): void;
|
|
224
484
|
dispose(): void;
|
|
225
485
|
}
|
|
486
|
+
/**
|
|
487
|
+
* Create an engine. Browsers start the context suspended: `play()` before it
|
|
488
|
+
* is unlocked drops the voice with a warning. The engine resumes it on the
|
|
489
|
+
* first pointer, key or touch gesture, or call `unlock()` from one. Call
|
|
490
|
+
* `dispose()` when done.
|
|
491
|
+
*/
|
|
226
492
|
declare function createAudioEngine(opts?: AudioEngineOptions): AudioEngine;
|
|
227
493
|
|
|
494
|
+
interface FilterEffectOptions {
|
|
495
|
+
/** Default 'lowpass'. */
|
|
496
|
+
type?: BiquadFilterType;
|
|
497
|
+
/** Hz. Default 350, the node's own. */
|
|
498
|
+
frequency?: number;
|
|
499
|
+
Q?: number;
|
|
500
|
+
/** dB, for the shelf and peaking types. */
|
|
501
|
+
gain?: number;
|
|
502
|
+
}
|
|
503
|
+
interface FilterEffect extends InsertEffect {
|
|
504
|
+
readonly filter: BiquadFilterNode;
|
|
505
|
+
}
|
|
506
|
+
/** A `BiquadFilterNode`. Write its params through `filter` to sweep it. */
|
|
507
|
+
declare function createFilterEffect(ctx: BaseAudioContext, opts?: FilterEffectOptions): FilterEffect;
|
|
508
|
+
/** A wet/dry mix around an effect, for the effects whose output replaces
|
|
509
|
+
* rather than colors the signal. */
|
|
510
|
+
interface WetDryMix {
|
|
511
|
+
/** 0 is all dry, 1 all wet. */
|
|
512
|
+
mix(): number;
|
|
513
|
+
setMix(mix: number, rampMs?: number): void;
|
|
514
|
+
}
|
|
515
|
+
interface ReverbEffectOptions {
|
|
516
|
+
/** The room: an impulse response, loaded or synthesized like any buffer. */
|
|
517
|
+
impulse: AudioBuffer;
|
|
518
|
+
/** Default 0.3. */
|
|
519
|
+
mix?: number;
|
|
520
|
+
/** Scale the impulse to a consistent level. Default true, the node's own. */
|
|
521
|
+
normalize?: boolean;
|
|
522
|
+
}
|
|
523
|
+
interface ReverbEffect extends InsertEffect, WetDryMix {
|
|
524
|
+
readonly convolver: ConvolverNode;
|
|
525
|
+
}
|
|
526
|
+
/** Convolution reverb from an impulse buffer, with a wet/dry mix. */
|
|
527
|
+
declare function createReverbEffect(ctx: BaseAudioContext, opts: ReverbEffectOptions): ReverbEffect;
|
|
528
|
+
interface DelayEffectOptions {
|
|
529
|
+
/** ms. Default 250. */
|
|
530
|
+
time?: number;
|
|
531
|
+
/** How much of each repeat feeds the next, -1..1 exclusive. Default 0.35. */
|
|
532
|
+
feedback?: number;
|
|
533
|
+
/** Default 0.35. */
|
|
534
|
+
mix?: number;
|
|
535
|
+
/** The longest `time` the line can be set to later, ms. Default the larger
|
|
536
|
+
* of 1000 and `time`. */
|
|
537
|
+
maxTime?: number;
|
|
538
|
+
}
|
|
539
|
+
interface DelayEffect extends InsertEffect, WetDryMix {
|
|
540
|
+
readonly delay: DelayNode;
|
|
541
|
+
setTime(ms: number, rampMs?: number): void;
|
|
542
|
+
feedback(): number;
|
|
543
|
+
setFeedback(value: number, rampMs?: number): void;
|
|
544
|
+
/** Break the feedback loop. Called for you when the slot is removed. */
|
|
545
|
+
dispose(): void;
|
|
546
|
+
}
|
|
547
|
+
/** A feedback delay with a wet/dry mix. Ramping `setTime` bends the pitch of
|
|
548
|
+
* the repeats while it moves, as a tape delay does. */
|
|
549
|
+
declare function createDelayEffect(ctx: BaseAudioContext, opts?: DelayEffectOptions): DelayEffect;
|
|
550
|
+
interface CompressorEffectOptions {
|
|
551
|
+
/** dB. */
|
|
552
|
+
threshold?: number;
|
|
553
|
+
/** dB. */
|
|
554
|
+
knee?: number;
|
|
555
|
+
ratio?: number;
|
|
556
|
+
/** ms. */
|
|
557
|
+
attack?: number;
|
|
558
|
+
/** ms. */
|
|
559
|
+
release?: number;
|
|
560
|
+
/** Linear gain after the compressor, to win back the level it takes. Default 1. */
|
|
561
|
+
makeup?: number;
|
|
562
|
+
}
|
|
563
|
+
interface CompressorEffect extends InsertEffect {
|
|
564
|
+
readonly compressor: DynamicsCompressorNode;
|
|
565
|
+
readonly makeup: GainNode;
|
|
566
|
+
}
|
|
567
|
+
/** A `DynamicsCompressorNode` into a makeup gain. Unset options keep the
|
|
568
|
+
* node's defaults. */
|
|
569
|
+
declare function createCompressorEffect(ctx: BaseAudioContext, opts?: CompressorEffectOptions): CompressorEffect;
|
|
570
|
+
|
|
571
|
+
/** Clock and timer for `createScheduler`. */
|
|
228
572
|
interface SchedulerOptions {
|
|
229
573
|
/** Engine time in ms. Backed by `AudioContext.currentTime * 1000` in production. */
|
|
230
574
|
now: () => number;
|
|
@@ -237,6 +581,7 @@ interface SchedulerOptions {
|
|
|
237
581
|
/** Time between passes, in ms. Default 25. */
|
|
238
582
|
interval?: number;
|
|
239
583
|
}
|
|
584
|
+
/** A queue of callbacks keyed to engine time. See `createScheduler`. */
|
|
240
585
|
interface Scheduler {
|
|
241
586
|
start(): void;
|
|
242
587
|
/** Stop the timer. The queue survives; `clear()` empties it. */
|
|
@@ -270,6 +615,7 @@ interface Scheduler {
|
|
|
270
615
|
*/
|
|
271
616
|
declare function createScheduler(opts: SchedulerOptions): Scheduler;
|
|
272
617
|
|
|
618
|
+
/** The timer pair `createTickTimer` returns, shaped for `SchedulerOptions`. */
|
|
273
619
|
interface TickTimer {
|
|
274
620
|
/** One-shot, as `createScheduler` expects. */
|
|
275
621
|
setTimer(cb: () => void, ms: number): unknown;
|
|
@@ -286,4 +632,97 @@ interface TickTimer {
|
|
|
286
632
|
*/
|
|
287
633
|
declare function createTickTimer(): TickTimer;
|
|
288
634
|
|
|
289
|
-
|
|
635
|
+
/** A synth note on a step. `length` is its gate in steps, default 1. */
|
|
636
|
+
interface PatternNote extends Omit<NoteOptions, 'when' | 'duration'> {
|
|
637
|
+
step: number;
|
|
638
|
+
length?: number;
|
|
639
|
+
}
|
|
640
|
+
/** Noise on a step: a hi-hat, a snare's rattle. `length` is its gate in steps,
|
|
641
|
+
* default 1. */
|
|
642
|
+
interface PatternNoise extends Omit<NoiseOptions, 'when' | 'duration'> {
|
|
643
|
+
step: number;
|
|
644
|
+
length?: number;
|
|
645
|
+
}
|
|
646
|
+
/** A buffer played on a step: a drum hit, a sample. */
|
|
647
|
+
interface PatternHit extends Omit<PlayOptions, 'when'> {
|
|
648
|
+
step: number;
|
|
649
|
+
sound: SoundHandle;
|
|
650
|
+
}
|
|
651
|
+
type PatternEvent = PatternNote | PatternNoise | PatternHit;
|
|
652
|
+
/** Options for `createPatternPlayer`. */
|
|
653
|
+
interface PatternPlayerOptions {
|
|
654
|
+
/** Beats per minute. */
|
|
655
|
+
tempo: number;
|
|
656
|
+
/** Steps per beat. Default 4, so a step is a sixteenth note in 4/4. */
|
|
657
|
+
stepsPerBeat?: number;
|
|
658
|
+
events?: readonly PatternEvent[];
|
|
659
|
+
/** Steps in one pass. Default: through the last event, rounded up to a whole
|
|
660
|
+
* beat. */
|
|
661
|
+
length?: number;
|
|
662
|
+
/** Default true. */
|
|
663
|
+
loop?: boolean;
|
|
664
|
+
/** Called as each step is booked — up to a lookahead ahead of it sounding —
|
|
665
|
+
* with its index in the pattern and its engine time in ms. */
|
|
666
|
+
onStep?: (step: number, when: number) => void;
|
|
667
|
+
}
|
|
668
|
+
/** A running sequence of steps. See `createPatternPlayer`. */
|
|
669
|
+
interface PatternPlayer {
|
|
670
|
+
/** Start from step 0 at engine time `when`, default now. No-op while playing. */
|
|
671
|
+
start(when?: number): void;
|
|
672
|
+
/** Book nothing further, and release every voice the player started. */
|
|
673
|
+
stop(): void;
|
|
674
|
+
playing(): boolean;
|
|
675
|
+
tempo(): number;
|
|
676
|
+
/** Takes effect from the next step not yet booked. */
|
|
677
|
+
setTempo(bpm: number): void;
|
|
678
|
+
/** Takes effect from the next step not yet booked. */
|
|
679
|
+
setEvents(events: readonly PatternEvent[], length?: number): void;
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* A step sequencer over `engine`. Each step is one `engine.schedule` booking;
|
|
683
|
+
* when it fires, it books that step's events at the step's exact time and then
|
|
684
|
+
* books the step after it. So nothing is booked further ahead than the
|
|
685
|
+
* scheduler's lookahead, which is what lets a tempo change or new events land
|
|
686
|
+
* on the very next step.
|
|
687
|
+
*
|
|
688
|
+
* A step that fires more than a step late — a stalled timer, a suspended
|
|
689
|
+
* context — skips to the next step still ahead of the clock, keeping phase,
|
|
690
|
+
* rather than playing the missed ones at once.
|
|
691
|
+
*/
|
|
692
|
+
declare function createPatternPlayer(engine: AudioEngine, opts: PatternPlayerOptions): PatternPlayer;
|
|
693
|
+
|
|
694
|
+
/** Samples of `color` noise, seeded so every context gets the same loop. The
|
|
695
|
+
* end is pulled onto the start, so the loop point does not click. */
|
|
696
|
+
declare function noiseSamples(color: NoiseColor, length: number): Float32Array;
|
|
697
|
+
/**
|
|
698
|
+
* Ready-made inharmonic waves. The ratios are the textbook modes of each
|
|
699
|
+
* object; the levels and decays are a starting point to spread and adjust.
|
|
700
|
+
*/
|
|
701
|
+
declare const partialPresets: {
|
|
702
|
+
/** A church bell's hum, prime, tierce, quint and nominal, and above. */
|
|
703
|
+
bell: {
|
|
704
|
+
partials: {
|
|
705
|
+
ratio: number;
|
|
706
|
+
gain: number;
|
|
707
|
+
decay: number;
|
|
708
|
+
}[];
|
|
709
|
+
};
|
|
710
|
+
/** A tuned bar: its overtones sit near 4 and 10 times the fundamental and die fast. */
|
|
711
|
+
marimba: {
|
|
712
|
+
partials: {
|
|
713
|
+
ratio: number;
|
|
714
|
+
gain: number;
|
|
715
|
+
decay: number;
|
|
716
|
+
}[];
|
|
717
|
+
};
|
|
718
|
+
/** Struck sheet metal: modes nowhere near the harmonic series. */
|
|
719
|
+
metal: {
|
|
720
|
+
partials: {
|
|
721
|
+
ratio: number;
|
|
722
|
+
gain: number;
|
|
723
|
+
decay: number;
|
|
724
|
+
}[];
|
|
725
|
+
};
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
export { type Acquisition, type AnalyserTap, type AnalyserTapOptions, type AudioEngine, type AudioEngineOptions, type BusGraph, type BusGraphOptions, type BusHandle, type CompressorEffect, type CompressorEffectOptions, type DelayEffect, type DelayEffectOptions, type Envelope, type EnvelopePoint, type FilterEffect, type FilterEffectOptions, type Glide, type Inharmonic, type InsertAddOptions, type InsertChain, type InsertChainOptions, type InsertEffect, type InsertSlot, type NoiseColor, type NoiseOptions, type NoisePatch, type NoteOptions, type PatternEvent, type PatternHit, type PatternNoise, type PatternNote, type PatternPlayer, type PatternPlayerOptions, type Pitch, type PlayOptions, type ResolvedEnvelope, type ReverbEffect, type ReverbEffectOptions, type Scheduler, type SchedulerOptions, type SoundCache, type SoundHandle, type SpatialOptions, type StealPolicy, type StreamOptions, type SynthPartial, type SynthPatch, type TickTimer, type Vec2, type VoiceFilter, type VoiceHandle, type VoicePool, type VoicePoolOptions, type VoiceRecord, type Waveform, type WetDryMix, createAnalyserTap, createAudioEngine, createBusGraph, createCompressorEffect, createDelayEffect, createFilterEffect, createPatternPlayer, createReverbEffect, createScheduler, createSoundCache, createTickTimer, createVoicePool, envelopeLevel, envelopePoints, midiToFrequency, noiseSamples, noteToMidi, partialPresets, resolveEnvelope, spatialize, toFrequency };
|