@weasel-js/audio 1.7.0 → 1.7.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 +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/README.md
CHANGED
|
@@ -29,3 +29,112 @@ engine.play(jump, { bus: 'sfx', position: { x: 40, y: 0 } });
|
|
|
29
29
|
Browsers start an `AudioContext` suspended until a user gesture. The engine
|
|
30
30
|
resumes on the first gesture automatically; `play()` before that drops the voice
|
|
31
31
|
with a dev warning rather than queueing it.
|
|
32
|
+
|
|
33
|
+
## Notes and patterns
|
|
34
|
+
|
|
35
|
+
`playNote` plays a synthesized voice — an `OscillatorNode` under an ADSR
|
|
36
|
+
envelope — with no buffer at all. It is a voice like a buffer voice: the same
|
|
37
|
+
handle, bus pool, stealing and `cancelKey`. Pitch is hertz, a name (`'C#4'`) or
|
|
38
|
+
`{ midi: 61 }`. `wave` is a built-in shape or the amplitudes of harmonic
|
|
39
|
+
partials, built once into a `PeriodicWave` and reused. A note with a `duration`
|
|
40
|
+
releases on its own; one without holds until `release()`, which runs the
|
|
41
|
+
envelope's release from wherever it has got to.
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
const pluck = { wave: [1, 0.5, 0.33], envelope: { attack: 4, decay: 120, sustain: 0.3, release: 80 } };
|
|
45
|
+
engine.playNote({ ...pluck, pitch: 'E4', duration: 150, bus: 'music' });
|
|
46
|
+
engine.playNote({ pitch: 260, duration: 80, glide: { to: 660 } });
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
A `wave` of `{ partials }` sums sine oscillators at any ratios of the pitch,
|
|
50
|
+
each with its own level and an optional exponential `decay` (a time constant in
|
|
51
|
+
ms) — the partials of a bell, a bar or struck metal, which sit off the harmonic
|
|
52
|
+
series where a `PeriodicWave` cannot reach. Levels are scaled to sum to 1.
|
|
53
|
+
`partialPresets` holds a `bell`, `marimba` and `metal` to start from.
|
|
54
|
+
|
|
55
|
+
`playNoise` is the same kind of voice with white, pink or brown noise as its
|
|
56
|
+
source, looped from a buffer made once per color per context. Either voice takes
|
|
57
|
+
a `filter`: a biquad ahead of the envelope, whose cutoff can follow an envelope
|
|
58
|
+
of its own, at `frequency + amount × level`.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
engine.playNote({ pitch: 'C5', wave: partialPresets.bell, duration: 1500, envelope: { release: 800 } });
|
|
62
|
+
engine.playNote({ pitch: 310, wave: { partials: [{ ratio: 1, decay: 110 }, { ratio: 1.71, gain: 0.5, decay: 80 }] } });
|
|
63
|
+
// A thump: lowpassed noise whose cutoff falls from 4 kHz to 80 Hz.
|
|
64
|
+
engine.playNoise({
|
|
65
|
+
noise: 'white', duration: 20, envelope: { release: 120 },
|
|
66
|
+
filter: { frequency: 80, amount: 4000, envelope: { attack: 0, decay: 60, sustain: 0 } },
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`createPatternPlayer` is a step sequencer on top. Events sit on steps — a note
|
|
71
|
+
or a noise with a `length` in steps, or a buffer `sound` — and each step is
|
|
72
|
+
booked through `engine.schedule` only when the lookahead window reaches it, so `setTempo` and
|
|
73
|
+
`setEvents` take effect from the next step. A step that fires more than a step
|
|
74
|
+
late skips ahead in phase instead of playing what it missed in a burst.
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
const player = createPatternPlayer(engine, {
|
|
78
|
+
tempo: 120, // four steps a beat by default
|
|
79
|
+
events: [
|
|
80
|
+
{ step: 0, pitch: 'C3', length: 8, wave: 'triangle' },
|
|
81
|
+
{ step: 4, sound: snare },
|
|
82
|
+
],
|
|
83
|
+
});
|
|
84
|
+
player.start();
|
|
85
|
+
player.setTempo(140);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Insert effects
|
|
89
|
+
|
|
90
|
+
Each bus has an ordered insert chain, `engine.bus(name).inserts`, between where
|
|
91
|
+
its voices connect and its fader, so mute, solo and the bus gain act on the
|
|
92
|
+
processed signal. An effect is anything with an `input` and an `output` node.
|
|
93
|
+
The built-ins are `createFilterEffect` (a biquad), `createReverbEffect`
|
|
94
|
+
(convolution from an impulse buffer, with a wet/dry mix), `createDelayEffect`
|
|
95
|
+
(a feedback delay with a mix) and `createCompressorEffect` (with a makeup gain).
|
|
96
|
+
Your own nodes go in the same way: `{ input: shaper, output: shaper }`.
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
const music = engine.bus('music').inserts;
|
|
100
|
+
const lowpass = music.add(createFilterEffect(engine.context, { frequency: 800 }));
|
|
101
|
+
const room = music.add(createReverbEffect(engine.context, { impulse, mix: 0.3 }), { bypassed: true });
|
|
102
|
+
room.bypass(false);
|
|
103
|
+
room.moveTo(0);
|
|
104
|
+
lowpass.remove();
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Edits never click. `bypass` crossfades the slot's wet and dry routes. `add`,
|
|
108
|
+
`remove` and `moveTo` build the new route beside the old one and crossfade
|
|
109
|
+
between them, one splice at a time, so `slots()` shows an edit at once while the
|
|
110
|
+
audio follows over a few fades; `settled()` resolves when it has caught up. The
|
|
111
|
+
fade is `insertFadeMs`, default 15. A move is a splice out and a splice back in,
|
|
112
|
+
so the moved effect drops out for one fade. A removed effect's `dispose` runs
|
|
113
|
+
once it has left the graph.
|
|
114
|
+
|
|
115
|
+
## Streaming
|
|
116
|
+
|
|
117
|
+
`load` and `decode` hold a whole sound in memory, which is wrong for a long
|
|
118
|
+
music track. `engine.stream` plays one from an `HTMLMediaElement`, or a URL it
|
|
119
|
+
makes one for, through a `MediaElementAudioSourceNode`:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
const bed = engine.stream('/music/theme.ogg', { bus: 'music', loop: true, gain: 0.6 });
|
|
123
|
+
bed.stop(800);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
It is a voice like the others: a `VoiceHandle`, the bus's inserts and voice
|
|
127
|
+
pool, `cancelKey`, `onDone`, position and pan. What does not carry over:
|
|
128
|
+
|
|
129
|
+
- **No `when`.** The element starts once it has buffered enough; the start is
|
|
130
|
+
not sample-accurate and cannot be booked ahead on the audio clock.
|
|
131
|
+
- **No `detune`.** `rate` is the element's `playbackRate`, which keeps pitch
|
|
132
|
+
unless you set the element's `preservesPitch` to false.
|
|
133
|
+
- **One stream per element.** An element can be routed into a graph once, and
|
|
134
|
+
has one playhead, so streaming an element that is already playing stops the
|
|
135
|
+
earlier voice.
|
|
136
|
+
- **Cross-origin media needs CORS.** Pass `crossOrigin: 'anonymous'` for a URL
|
|
137
|
+
on another origin, or the graph receives silence.
|
|
138
|
+
|
|
139
|
+
A stream counts toward its bus's voice limit and can be stolen, so give music a
|
|
140
|
+
bus of its own.
|