@weasel-js/audio 1.10.1 → 1.11.1
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 +26 -0
- package/dist/index.d.ts +180 -5
- package/dist/index.js +1736 -600
- package/dist/index.js.map +1 -1
- package/dist/voiceProcessor.gen-KWFGBRBP.js +6 -0
- package/dist/voiceProcessor.gen-KWFGBRBP.js.map +1 -0
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -20,6 +20,11 @@ to `setTimeout`, and a hidden tab books late again.
|
|
|
20
20
|
The worker times the pass, but the pass itself runs on the main thread, so a
|
|
21
21
|
stall there longer than the lookahead starts whatever came due late. The
|
|
22
22
|
`onLate(lateMs, when)` option reports each one; if it fires, raise `lookahead`.
|
|
23
|
+
Or pass `synthVoices: 'worklet'`: `playNote` and `playNoise` voices are then
|
|
24
|
+
rendered in an `AudioWorkletProcessor` from events posted when they are played,
|
|
25
|
+
so one played ahead of a stall starts on time. The processor loads from a
|
|
26
|
+
`blob:` URL (CSP: `script-src blob:`); until it has, and where it cannot,
|
|
27
|
+
synth voices play on nodes.
|
|
23
28
|
|
|
24
29
|
A suspended `AudioContext` is a separate matter: its clock stops, and on resume
|
|
25
30
|
the engine drops what came due meanwhile rather than firing it all at once.
|
|
@@ -175,6 +180,27 @@ fade is `insertFadeMs`, default 15. A move is a splice out and a splice back in,
|
|
|
175
180
|
so the moved effect drops out for one fade. A removed effect's `dispose` runs
|
|
176
181
|
once it has left the graph.
|
|
177
182
|
|
|
183
|
+
## Patchable graph
|
|
184
|
+
|
|
185
|
+
`engine.graph(bus)` returns a graph of nodes feeding that bus's input (the
|
|
186
|
+
first bus by default), which can be edited while it plays. Every structural
|
|
187
|
+
edit crossfades — `add`, `remove`, `replace`, `connect`, `disconnect` — and
|
|
188
|
+
`set` ramps a param. Sources are `createFileSourceNode` (a looping buffer),
|
|
189
|
+
`createOscillatorNode` and `createNoiseNode`; `createGainNode`,
|
|
190
|
+
`createPanNode`, `createMixNode`, `filterNode`, `delayNode`, `reverbNode` and
|
|
191
|
+
`compressorNode` process, and `insertNode` wraps any insert effect. `tap(id)`
|
|
192
|
+
puts an analyser on a node. The graph is disposed with the engine.
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
const g = engine.graph('music');
|
|
196
|
+
g.add('osc', createOscillatorNode(engine.context, { frequency: 220 }));
|
|
197
|
+
g.add('echo', delayNode(engine.context, { time: 300, feedback: 0.4 }));
|
|
198
|
+
g.connect('osc', { node: 'echo', port: 'in' });
|
|
199
|
+
g.connect('echo', { output: true });
|
|
200
|
+
g.start();
|
|
201
|
+
g.replace('osc', createOscillatorNode(engine.context, { wave: 'square', frequency: 220 }));
|
|
202
|
+
```
|
|
203
|
+
|
|
178
204
|
## Streaming
|
|
179
205
|
|
|
180
206
|
`load` and `decode` hold a whole sound in memory, which is wrong for a long
|
package/dist/index.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ interface AnalyserTap {
|
|
|
24
24
|
}
|
|
25
25
|
/** Attach a new `AnalyserNode` to `source`. The tap only listens, adding no
|
|
26
26
|
* path to the destination; `dispose()` detaches it. */
|
|
27
|
-
declare function createAnalyserTap(ctx:
|
|
27
|
+
declare function createAnalyserTap(ctx: BaseAudioContext, source: AudioNode, opts?: AnalyserTapOptions): AnalyserTap;
|
|
28
28
|
|
|
29
29
|
/** A one-shot timer pair, as `AudioEngineOptions.setTimer` takes it. */
|
|
30
30
|
interface Timers {
|
|
@@ -127,6 +127,87 @@ type BusGraphOptions = InsertChainOptions;
|
|
|
127
127
|
*/
|
|
128
128
|
declare function createBusGraph(ctx: AudioContext, names: string[], opts?: BusGraphOptions): BusGraph;
|
|
129
129
|
|
|
130
|
+
/** What a node factory returns and the graph wraps. `inputs` is empty for a source. */
|
|
131
|
+
interface GraphNodeDef {
|
|
132
|
+
readonly inputs: Readonly<Record<string, AudioNode>>;
|
|
133
|
+
readonly output: AudioNode;
|
|
134
|
+
/** `rampMs` is always passed; the graph supplies its fade when the caller gives none. */
|
|
135
|
+
readonly params: Readonly<Record<string, (value: number, rampMs: number) => void>>;
|
|
136
|
+
/** Sources only. `when` is context seconds. Called when the graph starts, or on add/replace while it runs. A source must be startable again after `stop`. */
|
|
137
|
+
start?(when: number): void;
|
|
138
|
+
/** Sources only. Stop at `when` (context seconds). */
|
|
139
|
+
stop?(when: number): void;
|
|
140
|
+
/** Called once, after the node has faded out of the graph. */
|
|
141
|
+
dispose?(): void;
|
|
142
|
+
}
|
|
143
|
+
/** A connection's far end: a node's named input port, or the graph's output. */
|
|
144
|
+
type GraphTarget = {
|
|
145
|
+
node: string;
|
|
146
|
+
port: string;
|
|
147
|
+
} | {
|
|
148
|
+
output: true;
|
|
149
|
+
};
|
|
150
|
+
/** One edit to an `AudioGraph`, applied by its `apply` in order. */
|
|
151
|
+
type GraphPatch = {
|
|
152
|
+
kind: 'add';
|
|
153
|
+
id: string;
|
|
154
|
+
def: GraphNodeDef;
|
|
155
|
+
} | {
|
|
156
|
+
kind: 'remove';
|
|
157
|
+
id: string;
|
|
158
|
+
} | {
|
|
159
|
+
kind: 'replace';
|
|
160
|
+
id: string;
|
|
161
|
+
def: GraphNodeDef;
|
|
162
|
+
} | {
|
|
163
|
+
kind: 'connect';
|
|
164
|
+
from: string;
|
|
165
|
+
to: GraphTarget;
|
|
166
|
+
} | {
|
|
167
|
+
kind: 'disconnect';
|
|
168
|
+
from: string;
|
|
169
|
+
to: GraphTarget;
|
|
170
|
+
} | {
|
|
171
|
+
kind: 'set';
|
|
172
|
+
id: string;
|
|
173
|
+
param: string;
|
|
174
|
+
value: number;
|
|
175
|
+
rampMs?: number;
|
|
176
|
+
};
|
|
177
|
+
/** Options for `AudioGraph`: the crossfade length (`fadeMs`) and any timer overrides. */
|
|
178
|
+
interface AudioGraphOptions extends Partial<Timers> {
|
|
179
|
+
/** Crossfade for every structural edit, and the default ramp for `set`, in ms. Default 15. */
|
|
180
|
+
fadeMs?: number;
|
|
181
|
+
}
|
|
182
|
+
/** A patchable graph of nodes that can be added, wired and set while it plays. */
|
|
183
|
+
interface AudioGraph {
|
|
184
|
+
add(id: string, def: GraphNodeDef): void;
|
|
185
|
+
remove(id: string): void;
|
|
186
|
+
replace(id: string, def: GraphNodeDef): void;
|
|
187
|
+
connect(from: string, to: GraphTarget): void;
|
|
188
|
+
disconnect(from: string, to: GraphTarget): void;
|
|
189
|
+
set(id: string, param: string, value: number, rampMs?: number): void;
|
|
190
|
+
/** Applies in order; each patch calls the method of the same name.
|
|
191
|
+
* A structural error anywhere in the list throws before anything changes.
|
|
192
|
+
* A param write that throws is skipped; the rest apply, then its error is rethrown. */
|
|
193
|
+
apply(patches: readonly GraphPatch[]): void;
|
|
194
|
+
/** Start every source now, and every source added later, until `stop`. */
|
|
195
|
+
start(): void;
|
|
196
|
+
/** Fade the graph's output to silence over `fadeMs`, then stop every source. */
|
|
197
|
+
stop(): void;
|
|
198
|
+
playing(): boolean;
|
|
199
|
+
has(id: string): boolean;
|
|
200
|
+
ids(): string[];
|
|
201
|
+
/** Listens to a node's output; survives `replace`; disposed with the node. */
|
|
202
|
+
tap(id: string, opts?: AnalyserTapOptions): AnalyserTap;
|
|
203
|
+
/** Resolves once no fade is in flight. */
|
|
204
|
+
settled(): Promise<void>;
|
|
205
|
+
/** Cancels fades, stops sources, disposes every def and tap, disconnects from the destination.
|
|
206
|
+
* While it may be sounding on a running context, it first fades the output out over `fadeMs`;
|
|
207
|
+
* every mutator throws from the call on. */
|
|
208
|
+
dispose(): void;
|
|
209
|
+
}
|
|
210
|
+
|
|
130
211
|
/** Opaque reference to a decoded sound. Mirrors `TextureHandle` in core. */
|
|
131
212
|
interface SoundHandle {
|
|
132
213
|
readonly id: string;
|
|
@@ -448,12 +529,26 @@ interface AudioEngineOptions {
|
|
|
448
529
|
clearTimer?: (handle: unknown) => void;
|
|
449
530
|
/** A voice or an `engine.schedule` callback came due `lateMs` after its
|
|
450
531
|
* time `when`, both in engine ms: the main thread stalled for longer than
|
|
451
|
-
* the lookahead covers, and the voice sounded late by that much.
|
|
532
|
+
* the lookahead covers, and the voice sounded late by that much. A
|
|
533
|
+
* `synthVoices: 'worklet'` voice is never reported: it was not late. */
|
|
452
534
|
onLate?: (lateMs: number, when: number) => void;
|
|
453
535
|
/** Crossfade for every insert-chain edit and bypass, in ms. Default 15. */
|
|
454
536
|
insertFadeMs?: number;
|
|
455
537
|
/** Uniform in [0, 1), for where in its loop a noise voice starts. Default `Math.random`. */
|
|
456
538
|
random?: () => number;
|
|
539
|
+
/**
|
|
540
|
+
* Where `playNote` and `playNoise` make their samples. Default `'nodes'`:
|
|
541
|
+
* oscillator and buffer nodes, built when the scheduler fires, so a main
|
|
542
|
+
* thread stalled past the lookahead starts the voice late. `'worklet'`
|
|
543
|
+
* renders them in an `AudioWorkletProcessor` from events posted at the
|
|
544
|
+
* call, so a voice played ahead of a stall still starts on time. Its handle,
|
|
545
|
+
* bus pool, stealing, pan and position behave as a node voice's do.
|
|
546
|
+
*
|
|
547
|
+
* The processor loads from a `blob:` URL, which a CSP must allow under
|
|
548
|
+
* `script-src`. Until it has loaded — `unlock()` waits for it — and on a
|
|
549
|
+
* context with no `audioWorklet`, synth voices play on nodes.
|
|
550
|
+
*/
|
|
551
|
+
synthVoices?: 'nodes' | 'worklet';
|
|
457
552
|
}
|
|
458
553
|
|
|
459
554
|
/** A sound engine: named mix buses, a per-bus voice pool, lookahead
|
|
@@ -508,6 +603,8 @@ interface AudioEngine {
|
|
|
508
603
|
* for a future `when` is not counted until it starts. */
|
|
509
604
|
activeVoices(bus?: string): number;
|
|
510
605
|
analyser(busName?: string, opts?: AnalyserTapOptions): AnalyserTap;
|
|
606
|
+
/** A graph feeding `bus`'s input (default: the first bus). Disposed with the engine. */
|
|
607
|
+
graph(bus?: string): AudioGraph;
|
|
511
608
|
setListener(p: Vec2, opts?: SpatialOptions): void;
|
|
512
609
|
dispose(): void;
|
|
513
610
|
}
|
|
@@ -631,8 +728,12 @@ interface Scheduler {
|
|
|
631
728
|
* true time to `source.start()` rather than "now". It runs on the first
|
|
632
729
|
* pass whose lookahead window reaches it — or, when the window already
|
|
633
730
|
* covers it and the scheduler is running, at the end of the current task,
|
|
634
|
-
* in time order with everything else then due.
|
|
635
|
-
|
|
731
|
+
* in time order with everything else then due. `reportLate: false` keeps
|
|
732
|
+
* it from `onLate`, for a callback whose work no longer depends on when it
|
|
733
|
+
* runs. */
|
|
734
|
+
schedule(when: number, fire: (when: number) => void, key?: string, opts?: {
|
|
735
|
+
reportLate?: boolean;
|
|
736
|
+
}): void;
|
|
636
737
|
/** Drop queued events with this key. A pass takes its whole batch out of the
|
|
637
738
|
* queue before firing any of it, so a callback cannot cancel a sibling that
|
|
638
739
|
* came due alongside it. */
|
|
@@ -990,4 +1091,78 @@ declare function biquadCoefficients(type: BiquadFilterType, frequency: number, s
|
|
|
990
1091
|
/** How much a filter with coefficients `c` passes at `hz`, in dB. */
|
|
991
1092
|
declare function biquadResponseDb(c: BiquadCoefficients, hz: number, sampleRate: number): number;
|
|
992
1093
|
|
|
993
|
-
|
|
1094
|
+
/** Options for `impulseResponse`: the tail's decay, channel count and noise seed. */
|
|
1095
|
+
interface ImpulseResponseOptions {
|
|
1096
|
+
/** How far the tail falls by its end, dB. Default 60 (an RT60). */
|
|
1097
|
+
decayDb?: number;
|
|
1098
|
+
/** Default 2. */
|
|
1099
|
+
channels?: number;
|
|
1100
|
+
/** Seed for the noise, so the same call makes the same buffer. Default 1. */
|
|
1101
|
+
seed?: number;
|
|
1102
|
+
}
|
|
1103
|
+
/** Noise under an exponential decay that falls `decayDb` over `seconds`. */
|
|
1104
|
+
declare function impulseResponse(ctx: BaseAudioContext, seconds: number, opts?: ImpulseResponseOptions): AudioBuffer;
|
|
1105
|
+
|
|
1106
|
+
/**
|
|
1107
|
+
* A patchable graph of node defs feeding `destination`. Every structural edit
|
|
1108
|
+
* fades: a connection opens through its own gain and closes through it before
|
|
1109
|
+
* the cut, and a removed node leaves once its edges have closed.
|
|
1110
|
+
*/
|
|
1111
|
+
declare function createAudioGraph(ctx: BaseAudioContext, destination: AudioNode, opts?: AudioGraphOptions): AudioGraph;
|
|
1112
|
+
|
|
1113
|
+
/** The waves an oscillator node takes: any `Waveform` except an inharmonic one. */
|
|
1114
|
+
type OscillatorWave = Exclude<Waveform, Inharmonic>;
|
|
1115
|
+
/** Options for `createFileSourceNode`: the buffer to play and whether it loops. */
|
|
1116
|
+
interface FileSourceNodeOptions {
|
|
1117
|
+
buffer: AudioBuffer;
|
|
1118
|
+
/** Default true. */
|
|
1119
|
+
loop?: boolean;
|
|
1120
|
+
}
|
|
1121
|
+
/** Plays a buffer, picking up where `stop` left it. Params: `rate`, `level`. */
|
|
1122
|
+
declare function createFileSourceNode(ctx: BaseAudioContext, opts: FileSourceNodeOptions): GraphNodeDef;
|
|
1123
|
+
/** Options for `createOscillatorNode`: the wave shape and starting frequency. */
|
|
1124
|
+
interface OscillatorNodeOptions {
|
|
1125
|
+
/** Default 'sine'. An array is the amplitude of each whole-number partial. */
|
|
1126
|
+
wave?: OscillatorWave;
|
|
1127
|
+
/** Hz. Default 440. */
|
|
1128
|
+
frequency?: number;
|
|
1129
|
+
}
|
|
1130
|
+
/** A tone. Params: `frequency`, `detune`, `level`. */
|
|
1131
|
+
declare function createOscillatorNode(ctx: BaseAudioContext, opts: OscillatorNodeOptions): GraphNodeDef;
|
|
1132
|
+
/** Options for `createNoiseNode`: the noise color whose shared buffer it loops. */
|
|
1133
|
+
interface NoiseNodeOptions {
|
|
1134
|
+
color: NoiseColor;
|
|
1135
|
+
}
|
|
1136
|
+
/** A loop of the shared noise buffer for its color. Param: `level`. */
|
|
1137
|
+
declare function createNoiseNode(ctx: BaseAudioContext, opts: NoiseNodeOptions): GraphNodeDef;
|
|
1138
|
+
|
|
1139
|
+
/** A graph node that multiplies its input by a gain: port `in`; param `gain`. */
|
|
1140
|
+
declare function createGainNode(ctx: BaseAudioContext, opts?: {
|
|
1141
|
+
gain?: number;
|
|
1142
|
+
}): GraphNodeDef;
|
|
1143
|
+
/** A graph node that pans its input in stereo: port `in`; param `pan`. */
|
|
1144
|
+
declare function createPanNode(ctx: BaseAudioContext, opts?: {
|
|
1145
|
+
pan?: number;
|
|
1146
|
+
}): GraphNodeDef;
|
|
1147
|
+
/** One port and one level param per name in `inputs`, summed into `output`. */
|
|
1148
|
+
declare function createMixNode(ctx: BaseAudioContext, opts: {
|
|
1149
|
+
inputs: readonly string[];
|
|
1150
|
+
}): GraphNodeDef;
|
|
1151
|
+
/** Wrap an insert effect as a node with one port, `in`. */
|
|
1152
|
+
declare function insertNode(effect: InsertEffect, params: GraphNodeDef['params']): GraphNodeDef;
|
|
1153
|
+
/** A graph node over a filter insert: port `in`; params `frequency`, `Q`, `gain`. */
|
|
1154
|
+
declare function filterNode(ctx: BaseAudioContext, opts?: FilterEffectOptions): GraphNodeDef;
|
|
1155
|
+
/** A graph node over a delay insert: port `in`; params `time` (ms), `feedback`, `mix`. */
|
|
1156
|
+
declare function delayNode(ctx: BaseAudioContext, opts?: DelayEffectOptions): GraphNodeDef;
|
|
1157
|
+
/** Options for `reverbNode`: the reverb effect's options, plus an `impulse` or the `seconds` of a synthesized one. */
|
|
1158
|
+
type ReverbNodeOptions = Omit<ReverbEffectOptions, 'impulse'> & {
|
|
1159
|
+
impulse?: AudioBuffer;
|
|
1160
|
+
/** Length of the synthesized impulse when none is given. Default 2. */
|
|
1161
|
+
seconds?: number;
|
|
1162
|
+
};
|
|
1163
|
+
/** A graph node over a reverb insert: port `in`; param `mix`. */
|
|
1164
|
+
declare function reverbNode(ctx: BaseAudioContext, opts?: ReverbNodeOptions): GraphNodeDef;
|
|
1165
|
+
/** A graph node over a compressor insert: port `in`; params `threshold`, `knee`, `ratio`, `attack` (ms), `release` (ms), `makeup`. */
|
|
1166
|
+
declare function compressorNode(ctx: BaseAudioContext, opts?: CompressorEffectOptions): GraphNodeDef;
|
|
1167
|
+
|
|
1168
|
+
export { type Acquisition, type AnalyserTap, type AnalyserTapOptions, type AudioEngine, type AudioEngineOptions, type AudioGraph, type AudioGraphOptions, type BiquadCoefficients, type BusGraph, type BusGraphOptions, type BusHandle, type CompressorEffect, type CompressorEffectOptions, type DelayEffect, type DelayEffectOptions, type Envelope, type EnvelopePoint, type FileSourceNodeOptions, type FilterEffect, type FilterEffectOptions, type Glide, type GraphNodeDef, type GraphPatch, type GraphTarget, type ImpulseResponseOptions, type Inharmonic, type InsertAddOptions, type InsertChain, type InsertChainOptions, type InsertEffect, type InsertSlot, type KernelNoise, type KernelNote, type NoiseColor, type NoiseNodeOptions, type NoiseOptions, type NoisePatch, type NoteOptions, type OscillatorNodeOptions, type OscillatorWave, type PatternDetune, type PatternEvent, type PatternHit, type PatternLoadOptions, type PatternNoise, type PatternNote, type PatternPlayer, type PatternPlayerOptions, type Pitch, type PlayOptions, type ResolvedEnvelope, type ReverbEffect, type ReverbEffectOptions, type ReverbNodeOptions, SCORE_BAR_STEPS, type Scheduler, type SchedulerOptions, type Score, type ScoreBarOptions, type ScoreCue, type ScoreCueName, type ScoreCueReading, type ScoreMode, type ScoreMood, type ScoreOptions, type ScoreParams, type ScoreState, type SoundCache, type SoundHandle, type SpatialOptions, type StealPolicy, type StreamOptions, type SynthPartial, type SynthPatch, type TickTimer, type Vec2, type VoiceDropReason, type VoiceFilter, type VoiceHandle, type VoiceKernel, type VoicePool, type VoicePoolOptions, type VoiceRecord, type Waveform, type WetDryMix, biquadCoefficients, biquadResponseDb, compressorNode, createAnalyserTap, createAudioEngine, createAudioGraph, createBusGraph, createCompressorEffect, createDelayEffect, createFileSourceNode, createFilterEffect, createGainNode, createMixNode, createNoiseKernel, createNoiseNode, createNoteKernel, createOscillatorNode, createPanNode, createPatternPlayer, createReverbEffect, createScheduler, createScore, createSoundCache, createTickTimer, createVoicePool, delayNode, envelopeLevel, envelopePoints, filterNode, impulseResponse, insertNode, midiToFrequency, noiseSamples, noteToMidi, partialPresets, patternLength, patternVoicePeaks, readScoreCue, renderNoise, renderNote, resolveEnvelope, reverbNode, scoreBar, scoreChord, scoreCues, scoreModes, scoreParams, spatialize, toFrequency };
|