@logue/reverb 1.2.16 → 1.3.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 +6 -0
- package/dist/Reverb.d.ts +1 -1
- package/dist/Reverb.es.js +303 -19
- package/dist/Reverb.iife.js +575 -287
- package/dist/Reverb.umd.js +576 -288
- package/package.json +16 -13
package/README.md
CHANGED
|
@@ -87,6 +87,12 @@ sourceNode.connect(ctx.destination);
|
|
|
87
87
|
sourceNode.play();
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
+
### CDN Usage
|
|
91
|
+
|
|
92
|
+
Not really intended for use with a CDN.
|
|
93
|
+
|
|
94
|
+
The dependent libraries [@thi.ng/colored-noise](https://www.jsdelivr.com/package/npm/@thi.ng/colored-noise), [@thi.ng/random](https://www.jsdelivr.com/package/npm/@thi.ng/random) and [@thi.ng/transducers](https://www.jsdelivr.com/package/npm/@thi.ng/transducers) need to be loaded separately.
|
|
95
|
+
|
|
90
96
|
## Reference
|
|
91
97
|
|
|
92
98
|
- [Web Audio API](https://www.w3.org/TR/webaudio/)
|
package/dist/Reverb.d.ts
CHANGED
|
@@ -131,7 +131,7 @@ export default class Reverb {
|
|
|
131
131
|
* @param min - Minimum value
|
|
132
132
|
* @param max - Maximum value
|
|
133
133
|
*/
|
|
134
|
-
private inRange;
|
|
134
|
+
private static inRange;
|
|
135
135
|
/** Utility function for building an impulse response from the module parameters. */
|
|
136
136
|
private buildImpulse;
|
|
137
137
|
/**
|
package/dist/Reverb.es.js
CHANGED
|
@@ -5,13 +5,59 @@
|
|
|
5
5
|
* @author Logue <logue@hotmail.co.jp>
|
|
6
6
|
* @copyright 2019-2023 By Masashi Yoshikawa All rights reserved.
|
|
7
7
|
* @license MIT
|
|
8
|
-
* @version 1.
|
|
8
|
+
* @version 1.3.0
|
|
9
9
|
* @see {@link https://github.com/logue/Reverb.js}
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const INV_MAX = 1 / 2 ** 32;
|
|
13
|
+
class ARandom {
|
|
14
|
+
float(norm = 1) {
|
|
15
|
+
return this.int() * INV_MAX * norm;
|
|
16
|
+
}
|
|
17
|
+
probability(p) {
|
|
18
|
+
return this.float() < p;
|
|
19
|
+
}
|
|
20
|
+
norm(norm = 1) {
|
|
21
|
+
return (this.int() * INV_MAX - 0.5) * 2 * norm;
|
|
22
|
+
}
|
|
23
|
+
normMinMax(min, max) {
|
|
24
|
+
const x = this.minmax(min, max);
|
|
25
|
+
return this.float() < 0.5 ? x : -x;
|
|
26
|
+
}
|
|
27
|
+
minmax(min, max) {
|
|
28
|
+
return this.float() * (max - min) + min;
|
|
29
|
+
}
|
|
30
|
+
minmaxInt(min, max) {
|
|
31
|
+
min |= 0;
|
|
32
|
+
return min + (this.int() % ((max | 0) - min));
|
|
33
|
+
}
|
|
34
|
+
minmaxUint(min, max) {
|
|
35
|
+
min >>>= 0;
|
|
36
|
+
return min + (this.int() % ((max >>> 0) - min));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const random = Math.random;
|
|
41
|
+
/**
|
|
42
|
+
* A `Math.random()` based {@link IRandom} implementation. Also @see
|
|
43
|
+
* {@link SYSTEM}.
|
|
44
|
+
*/
|
|
45
|
+
class SystemRandom extends ARandom {
|
|
46
|
+
int() {
|
|
47
|
+
return (random() * 4294967296) /* 2**32 */ >>> 0;
|
|
48
|
+
}
|
|
49
|
+
float(norm = 1) {
|
|
50
|
+
return random() * norm;
|
|
51
|
+
}
|
|
52
|
+
norm(norm = 1) {
|
|
53
|
+
return (random() - 0.5) * 2 * norm;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Used as default PRNG throughout most other thi.ng projects, though usually is
|
|
58
|
+
* configurable.
|
|
59
|
+
*/
|
|
60
|
+
const SYSTEM = new SystemRandom();
|
|
15
61
|
|
|
16
62
|
const defaults = {
|
|
17
63
|
noise: "white",
|
|
@@ -29,9 +75,9 @@ const defaults = {
|
|
|
29
75
|
once: false
|
|
30
76
|
};
|
|
31
77
|
|
|
32
|
-
const
|
|
33
|
-
version: "1.
|
|
34
|
-
date: "2023-
|
|
78
|
+
const Meta = {
|
|
79
|
+
version: "1.3.0",
|
|
80
|
+
date: "2023-08-16T23:12:28.774Z"
|
|
35
81
|
};
|
|
36
82
|
|
|
37
83
|
const Noise = {
|
|
@@ -51,11 +97,252 @@ const Noise = {
|
|
|
51
97
|
white: "white"
|
|
52
98
|
};
|
|
53
99
|
|
|
100
|
+
const DEFAULT_OPTS = {
|
|
101
|
+
bins: 2,
|
|
102
|
+
scale: 1,
|
|
103
|
+
rnd: SYSTEM,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const preseed = (n, scale, rnd) => {
|
|
107
|
+
const state = new Array(n);
|
|
108
|
+
for (let i = 0; i < n; i++) {
|
|
109
|
+
state[i] = rnd.norm(scale);
|
|
110
|
+
}
|
|
111
|
+
return state;
|
|
112
|
+
};
|
|
113
|
+
const sum = (src) => src.reduce((sum, x) => sum + x, 0);
|
|
114
|
+
function* interleave(a, b) {
|
|
115
|
+
const src = [a[Symbol.iterator](), b[Symbol.iterator]()];
|
|
116
|
+
for (let i = 0; true; i ^= 1) {
|
|
117
|
+
const next = src[i].next();
|
|
118
|
+
if (next.done)
|
|
119
|
+
return;
|
|
120
|
+
yield next.value;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* High-pass filtered noise. Opposite of {@link red}.
|
|
126
|
+
*
|
|
127
|
+
* @param opts -
|
|
128
|
+
*/
|
|
129
|
+
function* blue(opts) {
|
|
130
|
+
const { bins, scale, rnd } = {
|
|
131
|
+
...DEFAULT_OPTS,
|
|
132
|
+
...opts,
|
|
133
|
+
};
|
|
134
|
+
const state = preseed(bins, scale, rnd);
|
|
135
|
+
state.forEach((x, i) => (state[i] = i & 1 ? x : -x));
|
|
136
|
+
const invN = 1 / bins;
|
|
137
|
+
let acc = sum(state);
|
|
138
|
+
for (let i = 0, sign = -1; true; ++i >= bins && (i = 0)) {
|
|
139
|
+
acc -= state[i];
|
|
140
|
+
acc += state[i] = sign * rnd.norm(scale);
|
|
141
|
+
sign ^= 0xfffffffe;
|
|
142
|
+
yield sign * acc * invN;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Band-pass filtered noise (interleaved blue noise). Opposite of
|
|
148
|
+
* {@link violet}.
|
|
149
|
+
*
|
|
150
|
+
* @param opts -
|
|
151
|
+
*/
|
|
152
|
+
const green = (opts) => interleave(blue(opts), blue(opts));
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Returns number of 1 bits in `x`.
|
|
156
|
+
*
|
|
157
|
+
* @param x -
|
|
158
|
+
*/
|
|
159
|
+
const ctz32 = (x) => {
|
|
160
|
+
let c = 32;
|
|
161
|
+
x &= -x;
|
|
162
|
+
x && c--;
|
|
163
|
+
x & 0x0000ffff && (c -= 16);
|
|
164
|
+
x & 0x00ff00ff && (c -= 8);
|
|
165
|
+
x & 0x0f0f0f0f && (c -= 4);
|
|
166
|
+
x & 0x33333333 && (c -= 2);
|
|
167
|
+
x & 0x55555555 && (c -= 1);
|
|
168
|
+
return c;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Exponential decay (1/f) noise, based on Voss-McCarthy algorithm.
|
|
173
|
+
*
|
|
174
|
+
* @remarks
|
|
175
|
+
* The number of internal states should be in the [4..32] range (default: 8).
|
|
176
|
+
* Due to JS integer limitations, `n` > 32 are meaningless.
|
|
177
|
+
*
|
|
178
|
+
* References:
|
|
179
|
+
*
|
|
180
|
+
* - https://www.dsprelated.com/showarticle/908.php
|
|
181
|
+
* - https://www.firstpr.com.au/dsp/pink-noise/#Voss-McCartney
|
|
182
|
+
*
|
|
183
|
+
* @param opts -
|
|
184
|
+
*/
|
|
185
|
+
function* pink(opts) {
|
|
186
|
+
const { bins, scale, rnd } = {
|
|
187
|
+
...DEFAULT_OPTS,
|
|
188
|
+
bins: 8,
|
|
189
|
+
...opts,
|
|
190
|
+
};
|
|
191
|
+
const state = preseed(bins, scale, rnd);
|
|
192
|
+
const invN = 1 / bins;
|
|
193
|
+
let acc = sum(state);
|
|
194
|
+
for (let i = 0; true; i = (i + 1) >>> 0) {
|
|
195
|
+
const id = ctz32(i) % bins;
|
|
196
|
+
acc -= state[id];
|
|
197
|
+
acc += state[id] = rnd.norm(scale);
|
|
198
|
+
yield acc * invN;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Low-pass filtered noise (same as brown noise). Opposite of {@link blue}.
|
|
204
|
+
*
|
|
205
|
+
* @param opts -
|
|
206
|
+
*/
|
|
207
|
+
function* red(opts) {
|
|
208
|
+
const { bins, scale, rnd } = {
|
|
209
|
+
...DEFAULT_OPTS,
|
|
210
|
+
...opts,
|
|
211
|
+
};
|
|
212
|
+
const state = preseed(bins, scale, rnd);
|
|
213
|
+
const invN = 1 / bins;
|
|
214
|
+
let acc = sum(state);
|
|
215
|
+
for (let i = 0; true; ++i >= bins && (i = 0)) {
|
|
216
|
+
acc -= state[i];
|
|
217
|
+
acc += state[i] = rnd.norm(scale);
|
|
218
|
+
yield acc * invN;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Band-stop filtered noise (interleaved red noise). Opposite of {@link green}.
|
|
224
|
+
*
|
|
225
|
+
* @param opts -
|
|
226
|
+
*/
|
|
227
|
+
const violet = (opts) => interleave(red(opts), red(opts));
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Unfiltered noise w/ uniform distribution. Merely yields samples from
|
|
231
|
+
* given PRNG.
|
|
232
|
+
*
|
|
233
|
+
* @param opts -
|
|
234
|
+
*/
|
|
235
|
+
function* white(opts) {
|
|
236
|
+
const { scale, rnd } = { ...DEFAULT_OPTS, ...opts };
|
|
237
|
+
while (true) {
|
|
238
|
+
yield rnd.norm(scale);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const implementsFunction = (x, fn) => x != null && typeof x[fn] === "function";
|
|
243
|
+
|
|
244
|
+
const ensureTransducer = (x) => implementsFunction(x, "xform") ? x.xform() : x;
|
|
245
|
+
|
|
246
|
+
const isIterable = (x) => x != null && typeof x[Symbol.iterator] === "function";
|
|
247
|
+
|
|
248
|
+
class Reduced {
|
|
249
|
+
constructor(val) {
|
|
250
|
+
this.value = val;
|
|
251
|
+
}
|
|
252
|
+
deref() {
|
|
253
|
+
return this.value;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
const reduced = (x) => new Reduced(x);
|
|
257
|
+
const isReduced = (x) => x instanceof Reduced;
|
|
258
|
+
const ensureReduced = (x) => x instanceof Reduced ? x : new Reduced(x);
|
|
259
|
+
const unreduced = (x) => (x instanceof Reduced ? x.deref() : x);
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Convenience helper for building a full {@link Reducer} using the identity
|
|
263
|
+
* function (i.e. `(x) => x`) as completion step (true for 90% of all
|
|
264
|
+
* bundled transducers).
|
|
265
|
+
*
|
|
266
|
+
* @param init - init step of reducer
|
|
267
|
+
* @param rfn - reduction step of reducer
|
|
268
|
+
*/
|
|
269
|
+
const reducer = (init, rfn) => [init, (acc) => acc, rfn];
|
|
270
|
+
|
|
271
|
+
function push(xs) {
|
|
272
|
+
return xs
|
|
273
|
+
? [...xs]
|
|
274
|
+
: reducer(() => [], (acc, x) => (acc.push(x), acc));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Takes a transducer and input iterable. Returns iterator of
|
|
279
|
+
* transformed results.
|
|
280
|
+
*
|
|
281
|
+
* @param xform -
|
|
282
|
+
* @param xs -
|
|
283
|
+
*/
|
|
284
|
+
function* iterator(xform, xs) {
|
|
285
|
+
const rfn = ensureTransducer(xform)(push());
|
|
286
|
+
const complete = rfn[1];
|
|
287
|
+
const reduce = rfn[2];
|
|
288
|
+
for (let x of xs) {
|
|
289
|
+
const y = reduce([], x);
|
|
290
|
+
if (isReduced(y)) {
|
|
291
|
+
yield* unreduced(complete(y.deref()));
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if (y.length) {
|
|
295
|
+
yield* y;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
yield* unreduced(complete([]));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Reducer composition helper, internally used by various transducers
|
|
303
|
+
* during initialization. Takes existing reducer `rfn` (a 3-tuple) and a
|
|
304
|
+
* reducing function `fn`. Returns a new reducer tuple.
|
|
305
|
+
*
|
|
306
|
+
* @remarks
|
|
307
|
+
* `rfn[2]` reduces values of type `B` into an accumulator of type `A`.
|
|
308
|
+
* `fn` accepts values of type `C` and produces interim results of type
|
|
309
|
+
* `B`, which are then (possibly) passed to the "inner" `rfn[2]`
|
|
310
|
+
* function. Therefore the resulting reducer takes inputs of `C` and an
|
|
311
|
+
* accumulator of type `A`.
|
|
312
|
+
*
|
|
313
|
+
* It is assumed that `fn` internally calls `rfn[2]` to pass its own
|
|
314
|
+
* results for further processing by the nested reducer `rfn`.
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* ```ts
|
|
318
|
+
* compR(rfn, fn)
|
|
319
|
+
* // [rfn[0], rfn[1], fn]
|
|
320
|
+
* ```
|
|
321
|
+
*
|
|
322
|
+
* @param rfn -
|
|
323
|
+
* @param fn -
|
|
324
|
+
*/
|
|
325
|
+
const compR = (rfn, fn) => [rfn[0], rfn[1], fn];
|
|
326
|
+
|
|
327
|
+
function take(n, src) {
|
|
328
|
+
return isIterable(src)
|
|
329
|
+
? iterator(take(n), src)
|
|
330
|
+
: (rfn) => {
|
|
331
|
+
const r = rfn[2];
|
|
332
|
+
let m = n;
|
|
333
|
+
return compR(rfn, (acc, x) => --m > 0
|
|
334
|
+
? r(acc, x)
|
|
335
|
+
: m === 0
|
|
336
|
+
? ensureReduced(r(acc, x))
|
|
337
|
+
: reduced(acc));
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
54
341
|
class Reverb {
|
|
55
342
|
/** Version strings */
|
|
56
|
-
static version =
|
|
343
|
+
static version = Meta.version;
|
|
57
344
|
/** Build date */
|
|
58
|
-
static build =
|
|
345
|
+
static build = Meta.date;
|
|
59
346
|
/** AudioContext */
|
|
60
347
|
ctx;
|
|
61
348
|
/** Wet Level (Reverberated node) */
|
|
@@ -131,7 +418,7 @@ class Reverb {
|
|
|
131
418
|
* @param mix - Ratio (0~1)
|
|
132
419
|
*/
|
|
133
420
|
mix(mix) {
|
|
134
|
-
if (!
|
|
421
|
+
if (!Reverb.inRange(mix, 0, 1)) {
|
|
135
422
|
throw new RangeError("[Reverb.js] Dry/Wet ratio must be between 0 to 1.");
|
|
136
423
|
}
|
|
137
424
|
this.options.mix = mix;
|
|
@@ -144,7 +431,7 @@ class Reverb {
|
|
|
144
431
|
* @param value - IR length
|
|
145
432
|
*/
|
|
146
433
|
time(value) {
|
|
147
|
-
if (!
|
|
434
|
+
if (!Reverb.inRange(value, 1, 50)) {
|
|
148
435
|
throw new RangeError(
|
|
149
436
|
"[Reverb.js] Time length of inpulse response must be less than 50sec."
|
|
150
437
|
);
|
|
@@ -158,7 +445,7 @@ class Reverb {
|
|
|
158
445
|
* @param value - Decay value
|
|
159
446
|
*/
|
|
160
447
|
decay(value) {
|
|
161
|
-
if (!
|
|
448
|
+
if (!Reverb.inRange(value, 0, 100)) {
|
|
162
449
|
throw new RangeError(
|
|
163
450
|
"[Reverb.js] Inpulse Response decay level must be less than 100."
|
|
164
451
|
);
|
|
@@ -172,7 +459,7 @@ class Reverb {
|
|
|
172
459
|
* @param value - Time[ms]
|
|
173
460
|
*/
|
|
174
461
|
delay(value) {
|
|
175
|
-
if (!
|
|
462
|
+
if (!Reverb.inRange(value, 0, 100)) {
|
|
176
463
|
throw new RangeError(
|
|
177
464
|
"[Reverb.js] Inpulse Response delay time must be less than 100."
|
|
178
465
|
);
|
|
@@ -203,7 +490,7 @@ class Reverb {
|
|
|
203
490
|
* @param freq - Frequency
|
|
204
491
|
*/
|
|
205
492
|
filterFreq(freq) {
|
|
206
|
-
if (!
|
|
493
|
+
if (!Reverb.inRange(freq, 20, 2e4)) {
|
|
207
494
|
throw new RangeError(
|
|
208
495
|
"[Reverb.js] Filter frequrncy must be between 20 and 20000."
|
|
209
496
|
);
|
|
@@ -217,7 +504,7 @@ class Reverb {
|
|
|
217
504
|
* @param q - Quality
|
|
218
505
|
*/
|
|
219
506
|
filterQ(q) {
|
|
220
|
-
if (!
|
|
507
|
+
if (!Reverb.inRange(q, 0, 10)) {
|
|
221
508
|
throw new RangeError(
|
|
222
509
|
"[Reverb.js] Filter quality value must be between 0 and 10."
|
|
223
510
|
);
|
|
@@ -297,7 +584,7 @@ class Reverb {
|
|
|
297
584
|
* @param min - Minimum value
|
|
298
585
|
* @param max - Maximum value
|
|
299
586
|
*/
|
|
300
|
-
inRange(x, min, max) {
|
|
587
|
+
static inRange(x, min, max) {
|
|
301
588
|
return (x - min) * (x - max) <= 0;
|
|
302
589
|
}
|
|
303
590
|
/** Utility function for building an impulse response from the module parameters. */
|
|
@@ -344,8 +631,5 @@ class Reverb {
|
|
|
344
631
|
];
|
|
345
632
|
}
|
|
346
633
|
}
|
|
347
|
-
if (!window.Reverb) {
|
|
348
|
-
window.Reverb = Reverb;
|
|
349
|
-
}
|
|
350
634
|
|
|
351
635
|
export { Reverb as default };
|