@logue/reverb 1.3.5 → 1.3.7

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.
@@ -5,642 +5,8 @@
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.3.5
8
+ * @version 1.3.7
9
9
  * @see {@link https://github.com/logue/Reverb.js}
10
10
  */
11
11
 
12
- (function (global, factory) {
13
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
14
- typeof define === 'function' && define.amd ? define(factory) :
15
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Reverb = factory());
16
- })(this, (function () { 'use strict';
17
-
18
- const INV_MAX = 1 / 2 ** 32;
19
- class ARandom {
20
- float(norm = 1) {
21
- return this.int() * INV_MAX * norm;
22
- }
23
- probability(p) {
24
- return this.float() < p;
25
- }
26
- norm(norm = 1) {
27
- return (this.int() * INV_MAX - 0.5) * 2 * norm;
28
- }
29
- normMinMax(min, max) {
30
- const x = this.minmax(min, max);
31
- return this.float() < 0.5 ? x : -x;
32
- }
33
- minmax(min, max) {
34
- return this.float() * (max - min) + min;
35
- }
36
- minmaxInt(min, max) {
37
- min |= 0;
38
- const range = (max | 0) - min;
39
- return range ? min + (this.int() % range) : min;
40
- }
41
- minmaxUint(min, max) {
42
- min >>>= 0;
43
- const range = (max >>> 0) - min;
44
- return range ? min + (this.int() % range) : min;
45
- }
46
- }
47
-
48
- const random = Math.random;
49
- /**
50
- * A `Math.random()` based {@link IRandom} implementation. Also @see
51
- * {@link SYSTEM}.
52
- */
53
- class SystemRandom extends ARandom {
54
- int() {
55
- return (random() * 4294967296) /* 2**32 */ >>> 0;
56
- }
57
- float(norm = 1) {
58
- return random() * norm;
59
- }
60
- norm(norm = 1) {
61
- return (random() - 0.5) * 2 * norm;
62
- }
63
- }
64
- /**
65
- * Used as default PRNG throughout most other thi.ng projects, though usually is
66
- * configurable.
67
- */
68
- const SYSTEM = new SystemRandom();
69
-
70
- const defaults = {
71
- noise: "white",
72
- scale: 1,
73
- peaks: 2,
74
- randomAlgorithm: SYSTEM,
75
- decay: 2,
76
- delay: 0,
77
- reverse: false,
78
- time: 2,
79
- filterType: "allpass",
80
- filterFreq: 2200,
81
- filterQ: 1,
82
- mix: 0.5,
83
- once: false
84
- };
85
-
86
- const Meta = {
87
- version: "1.3.5",
88
- date: "2023-11-20T03:01:00.334Z"
89
- };
90
-
91
- const Noise = {
92
- /** Blue noise */
93
- blue: "blue",
94
- /** Brown noise (same as red noise) */
95
- brown: "red",
96
- /** Green noise */
97
- green: "green",
98
- /** Pink noise */
99
- pink: "pink",
100
- /** Red noise */
101
- red: "red",
102
- /** Violet noise */
103
- violet: "violet",
104
- /** White noise */
105
- white: "white"
106
- };
107
-
108
- const DEFAULT_OPTS = {
109
- bins: 2,
110
- scale: 1,
111
- rnd: SYSTEM,
112
- };
113
-
114
- const preseed = (n, scale, rnd) => {
115
- const state = new Array(n);
116
- for (let i = 0; i < n; i++) {
117
- state[i] = rnd.norm(scale);
118
- }
119
- return state;
120
- };
121
- const sum = (src) => src.reduce((sum, x) => sum + x, 0);
122
- function* interleave(a, b) {
123
- const src = [a[Symbol.iterator](), b[Symbol.iterator]()];
124
- for (let i = 0; true; i ^= 1) {
125
- const next = src[i].next();
126
- if (next.done)
127
- return;
128
- yield next.value;
129
- }
130
- }
131
-
132
- /**
133
- * High-pass filtered noise. Opposite of {@link red}.
134
- *
135
- * @param opts -
136
- */
137
- function* blue(opts) {
138
- const { bins, scale, rnd } = {
139
- ...DEFAULT_OPTS,
140
- ...opts,
141
- };
142
- const state = preseed(bins, scale, rnd);
143
- state.forEach((x, i) => (state[i] = i & 1 ? x : -x));
144
- const invN = 1 / bins;
145
- let acc = sum(state);
146
- for (let i = 0, sign = -1; true; ++i >= bins && (i = 0)) {
147
- acc -= state[i];
148
- acc += state[i] = sign * rnd.norm(scale);
149
- sign ^= 0xfffffffe;
150
- yield sign * acc * invN;
151
- }
152
- }
153
-
154
- /**
155
- * Band-pass filtered noise (interleaved blue noise). Opposite of
156
- * {@link violet}.
157
- *
158
- * @param opts -
159
- */
160
- const green = (opts) => interleave(blue(opts), blue(opts));
161
-
162
- /**
163
- * Returns number of 1 bits in `x`.
164
- *
165
- * @param x -
166
- */
167
- const ctz32 = (x) => {
168
- let c = 32;
169
- x &= -x;
170
- x && c--;
171
- x & 0x0000ffff && (c -= 16);
172
- x & 0x00ff00ff && (c -= 8);
173
- x & 0x0f0f0f0f && (c -= 4);
174
- x & 0x33333333 && (c -= 2);
175
- x & 0x55555555 && (c -= 1);
176
- return c;
177
- };
178
-
179
- /**
180
- * Exponential decay (1/f) noise, based on Voss-McCarthy algorithm.
181
- *
182
- * @remarks
183
- * The number of internal states should be in the [4..32] range (default: 8).
184
- * Due to JS integer limitations, `n` > 32 are meaningless.
185
- *
186
- * References:
187
- *
188
- * - https://www.dsprelated.com/showarticle/908.php
189
- * - https://www.firstpr.com.au/dsp/pink-noise/#Voss-McCartney
190
- *
191
- * @param opts -
192
- */
193
- function* pink(opts) {
194
- const { bins, scale, rnd } = {
195
- ...DEFAULT_OPTS,
196
- bins: 8,
197
- ...opts,
198
- };
199
- const state = preseed(bins, scale, rnd);
200
- const invN = 1 / bins;
201
- let acc = sum(state);
202
- for (let i = 0; true; i = (i + 1) >>> 0) {
203
- const id = ctz32(i) % bins;
204
- acc -= state[id];
205
- acc += state[id] = rnd.norm(scale);
206
- yield acc * invN;
207
- }
208
- }
209
-
210
- /**
211
- * Low-pass filtered noise (same as brown noise). Opposite of {@link blue}.
212
- *
213
- * @param opts -
214
- */
215
- function* red(opts) {
216
- const { bins, scale, rnd } = {
217
- ...DEFAULT_OPTS,
218
- ...opts,
219
- };
220
- const state = preseed(bins, scale, rnd);
221
- const invN = 1 / bins;
222
- let acc = sum(state);
223
- for (let i = 0; true; ++i >= bins && (i = 0)) {
224
- acc -= state[i];
225
- acc += state[i] = rnd.norm(scale);
226
- yield acc * invN;
227
- }
228
- }
229
-
230
- /**
231
- * Band-stop filtered noise (interleaved red noise). Opposite of {@link green}.
232
- *
233
- * @param opts -
234
- */
235
- const violet = (opts) => interleave(red(opts), red(opts));
236
-
237
- /**
238
- * Unfiltered noise w/ uniform distribution. Merely yields samples from
239
- * given PRNG.
240
- *
241
- * @param opts -
242
- */
243
- function* white(opts) {
244
- const { scale, rnd } = { ...DEFAULT_OPTS, ...opts };
245
- while (true) {
246
- yield rnd.norm(scale);
247
- }
248
- }
249
-
250
- const implementsFunction = (x, fn) => x != null && typeof x[fn] === "function";
251
-
252
- const ensureTransducer = (x) => implementsFunction(x, "xform") ? x.xform() : x;
253
-
254
- const isIterable = (x) => x != null && typeof x[Symbol.iterator] === "function";
255
-
256
- class Reduced {
257
- value;
258
- constructor(val) {
259
- this.value = val;
260
- }
261
- deref() {
262
- return this.value;
263
- }
264
- }
265
- const reduced = (x) => new Reduced(x);
266
- const isReduced = (x) => x instanceof Reduced;
267
- const ensureReduced = (x) => x instanceof Reduced ? x : new Reduced(x);
268
- const unreduced = (x) => (x instanceof Reduced ? x.deref() : x);
269
-
270
- /**
271
- * Convenience helper for building a full {@link Reducer} using the identity
272
- * function (i.e. `(x) => x`) as completion step (true for 90% of all
273
- * bundled transducers).
274
- *
275
- * @param init - init step of reducer
276
- * @param rfn - reduction step of reducer
277
- */
278
- const reducer = (init, rfn) => [init, (acc) => acc, rfn];
279
-
280
- function push(xs) {
281
- return xs
282
- ? [...xs]
283
- : reducer(() => [], (acc, x) => (acc.push(x), acc));
284
- }
285
-
286
- /**
287
- * Takes a transducer and input iterable. Returns iterator of
288
- * transformed results.
289
- *
290
- * @param xform -
291
- * @param xs -
292
- */
293
- function* iterator(xform, xs) {
294
- const rfn = ensureTransducer(xform)(push());
295
- const complete = rfn[1];
296
- const reduce = rfn[2];
297
- for (let x of xs) {
298
- const y = reduce([], x);
299
- if (isReduced(y)) {
300
- yield* unreduced(complete(y.deref()));
301
- return;
302
- }
303
- if (y.length) {
304
- yield* y;
305
- }
306
- }
307
- yield* unreduced(complete([]));
308
- }
309
-
310
- /**
311
- * Reducer composition helper, internally used by various transducers
312
- * during initialization. Takes existing reducer `rfn` (a 3-tuple) and a
313
- * reducing function `fn`. Returns a new reducer tuple.
314
- *
315
- * @remarks
316
- * `rfn[2]` reduces values of type `B` into an accumulator of type `A`.
317
- * `fn` accepts values of type `C` and produces interim results of type
318
- * `B`, which are then (possibly) passed to the "inner" `rfn[2]`
319
- * function. Therefore the resulting reducer takes inputs of `C` and an
320
- * accumulator of type `A`.
321
- *
322
- * It is assumed that `fn` internally calls `rfn[2]` to pass its own
323
- * results for further processing by the nested reducer `rfn`.
324
- *
325
- * @example
326
- * ```ts
327
- * compR(rfn, fn)
328
- * // [rfn[0], rfn[1], fn]
329
- * ```
330
- *
331
- * @param rfn -
332
- * @param fn -
333
- */
334
- const compR = (rfn, fn) => [rfn[0], rfn[1], fn];
335
-
336
- function take(n, src) {
337
- return isIterable(src)
338
- ? iterator(take(n), src)
339
- : (rfn) => {
340
- const r = rfn[2];
341
- let m = n;
342
- return compR(rfn, (acc, x) => --m > 0
343
- ? r(acc, x)
344
- : m === 0
345
- ? ensureReduced(r(acc, x))
346
- : reduced(acc));
347
- };
348
- }
349
-
350
- class Reverb {
351
- /** Version strings */
352
- static version = Meta.version;
353
- /** Build date */
354
- static build = Meta.date;
355
- /** AudioContext */
356
- ctx;
357
- /** Wet Level (Reverberated node) */
358
- wetGainNode;
359
- /** Dry Level (Original sound node) */
360
- dryGainNode;
361
- /** Impulse response filter */
362
- filterNode;
363
- /** Convolution node for applying impulse response */
364
- convolverNode;
365
- /** Output gain node */
366
- outputNode;
367
- /** Option */
368
- options;
369
- /** Connected flag */
370
- isConnected;
371
- /** Noise Generator */
372
- noise = white;
373
- /**
374
- * Constructor
375
- *
376
- * @param ctx - Root AudioContext
377
- * @param options - Configure
378
- */
379
- constructor(ctx, options) {
380
- this.ctx = ctx;
381
- this.options = Object.assign(defaults, options);
382
- this.wetGainNode = this.ctx.createGain();
383
- this.dryGainNode = this.ctx.createGain();
384
- this.filterNode = this.ctx.createBiquadFilter();
385
- this.convolverNode = this.ctx.createConvolver();
386
- this.outputNode = this.ctx.createGain();
387
- this.isConnected = false;
388
- this.filterType(this.options.filterType);
389
- this.setNoise(this.options.noise);
390
- this.buildImpulse();
391
- this.mix(this.options.mix);
392
- }
393
- /**
394
- * Connect the node for the reverb effect to the original sound node.
395
- *
396
- * @param sourceNode - Input source node
397
- */
398
- connect(sourceNode) {
399
- if (this.isConnected && this.options.once) {
400
- this.isConnected = false;
401
- return this.outputNode;
402
- }
403
- this.convolverNode.connect(this.filterNode);
404
- this.filterNode.connect(this.wetGainNode);
405
- sourceNode.connect(this.convolverNode);
406
- sourceNode.connect(this.dryGainNode).connect(this.outputNode);
407
- sourceNode.connect(this.wetGainNode).connect(this.outputNode);
408
- this.isConnected = true;
409
- return this.outputNode;
410
- }
411
- /**
412
- * Disconnect the reverb node
413
- *
414
- * @param sourceNode - Input source node
415
- */
416
- disconnect(sourceNode) {
417
- if (this.isConnected) {
418
- this.convolverNode.disconnect(this.filterNode);
419
- this.filterNode.disconnect(this.wetGainNode);
420
- }
421
- this.isConnected = false;
422
- return sourceNode;
423
- }
424
- /**
425
- * Dry/Wet ratio
426
- *
427
- * @param mix - Ratio (0~1)
428
- */
429
- mix(mix) {
430
- if (!Reverb.inRange(mix, 0, 1)) {
431
- throw new RangeError("[Reverb.js] Dry/Wet ratio must be between 0 to 1.");
432
- }
433
- this.options.mix = mix;
434
- this.dryGainNode.gain.value = 1 - this.options.mix;
435
- this.wetGainNode.gain.value = this.options.mix;
436
- }
437
- /**
438
- * Set Impulse Response time length (second)
439
- *
440
- * @param value - IR length
441
- */
442
- time(value) {
443
- if (!Reverb.inRange(value, 1, 50)) {
444
- throw new RangeError(
445
- "[Reverb.js] Time length of inpulse response must be less than 50sec."
446
- );
447
- }
448
- this.options.time = value;
449
- this.buildImpulse();
450
- }
451
- /**
452
- * Impulse response decay rate.
453
- *
454
- * @param value - Decay value
455
- */
456
- decay(value) {
457
- if (!Reverb.inRange(value, 0, 100)) {
458
- throw new RangeError(
459
- "[Reverb.js] Inpulse Response decay level must be less than 100."
460
- );
461
- }
462
- this.options.decay = value;
463
- this.buildImpulse();
464
- }
465
- /**
466
- * Delay before reverberation starts
467
- *
468
- * @param value - Time[ms]
469
- */
470
- delay(value) {
471
- if (!Reverb.inRange(value, 0, 100)) {
472
- throw new RangeError(
473
- "[Reverb.js] Inpulse Response delay time must be less than 100."
474
- );
475
- }
476
- this.options.delay = value;
477
- this.buildImpulse();
478
- }
479
- /**
480
- * Reverse the impulse response.
481
- *
482
- * @param reverse - Reverse IR
483
- */
484
- reverse(reverse) {
485
- this.options.reverse = reverse;
486
- this.buildImpulse();
487
- }
488
- /**
489
- * Filter for impulse response
490
- *
491
- * @param type - Filiter Type
492
- */
493
- filterType(type = "allpass") {
494
- this.filterNode.type = this.options.filterType = type;
495
- }
496
- /**
497
- * Filter frequency applied to impulse response
498
- *
499
- * @param freq - Frequency
500
- */
501
- filterFreq(freq) {
502
- if (!Reverb.inRange(freq, 20, 2e4)) {
503
- throw new RangeError(
504
- "[Reverb.js] Filter frequrncy must be between 20 and 20000."
505
- );
506
- }
507
- this.options.filterFreq = freq;
508
- this.filterNode.frequency.value = this.options.filterFreq;
509
- }
510
- /**
511
- * Filter quality.
512
- *
513
- * @param q - Quality
514
- */
515
- filterQ(q) {
516
- if (!Reverb.inRange(q, 0, 10)) {
517
- throw new RangeError(
518
- "[Reverb.js] Filter quality value must be between 0 and 10."
519
- );
520
- }
521
- this.options.filterQ = q;
522
- this.filterNode.Q.value = this.options.filterQ;
523
- }
524
- /**
525
- * set IR source noise peaks
526
- *
527
- * @param p - Peaks
528
- */
529
- peaks(p) {
530
- this.options.peaks = p;
531
- this.buildImpulse();
532
- }
533
- /**
534
- * set IR source noise scale.
535
- *
536
- * @param s - Scale
537
- */
538
- scale(s) {
539
- this.options.scale = s;
540
- this.buildImpulse();
541
- }
542
- /**
543
- * set IR source noise generator.
544
- *
545
- * @param a - Algorithm
546
- */
547
- randomAlgorithm(a) {
548
- this.options.randomAlgorithm = a;
549
- this.buildImpulse();
550
- }
551
- /**
552
- * Inpulse Response Noise algorithm.
553
- *
554
- * @param type - IR noise algorithm type.
555
- */
556
- setNoise(type) {
557
- this.options.noise = type;
558
- switch (type) {
559
- case Noise.blue:
560
- this.noise = blue;
561
- break;
562
- case Noise.green:
563
- this.noise = green;
564
- break;
565
- case Noise.pink:
566
- this.noise = pink;
567
- break;
568
- case Noise.red:
569
- case Noise.brown:
570
- this.noise = red;
571
- break;
572
- case Noise.violet:
573
- this.noise = violet;
574
- break;
575
- default:
576
- this.noise = white;
577
- }
578
- this.buildImpulse();
579
- }
580
- /**
581
- * Set Random Algorythm
582
- *
583
- * @param algorithm - Algorythm
584
- */
585
- setRandomAlgorythm(algorithm) {
586
- this.options.randomAlgorithm = algorithm;
587
- this.buildImpulse();
588
- }
589
- /**
590
- * Return true if in range, otherwise false
591
- *
592
- * @param x - Target value
593
- * @param min - Minimum value
594
- * @param max - Maximum value
595
- */
596
- static inRange(x, min, max) {
597
- return (x - min) * (x - max) <= 0;
598
- }
599
- /** Utility function for building an impulse response from the module parameters. */
600
- buildImpulse() {
601
- const rate = this.ctx.sampleRate;
602
- const duration = Math.max(rate * this.options.time, 1);
603
- const delayDuration = rate * this.options.delay;
604
- const impulse = this.ctx.createBuffer(2, duration, rate);
605
- const impulseL = new Float32Array(duration);
606
- const impulseR = new Float32Array(duration);
607
- const noiseL = this.getNoise(duration);
608
- const noiseR = this.getNoise(duration);
609
- for (let i = 0; i < duration; i++) {
610
- let n = 0;
611
- if (i < delayDuration) {
612
- impulseL[i] = 0;
613
- impulseR[i] = 0;
614
- n = this.options.reverse ?? false ? duration - (i - delayDuration) : i - delayDuration;
615
- } else {
616
- n = this.options.reverse ?? false ? duration - i : i;
617
- }
618
- impulseL[i] = (noiseL[i] ?? 0) * (1 - n / duration) ** this.options.decay;
619
- impulseR[i] = (noiseR[i] ?? 0) * (1 - n / duration) ** this.options.decay;
620
- }
621
- impulse.getChannelData(0).set(impulseL);
622
- impulse.getChannelData(1).set(impulseR);
623
- this.convolverNode.buffer = impulse;
624
- }
625
- /**
626
- * Noise source
627
- *
628
- * @param duration - length of IR.
629
- */
630
- getNoise(duration) {
631
- return [
632
- ...take(
633
- duration,
634
- this.noise({
635
- bins: this.options.peaks,
636
- scale: this.options.scale,
637
- rnd: this.options.randomAlgorithm
638
- })
639
- )
640
- ];
641
- }
642
- }
643
-
644
- return Reverb;
645
-
646
- }));
12
+ (function(d,f){typeof exports=="object"&&typeof module<"u"?module.exports=f():typeof define=="function"&&define.amd?define(f):(d=typeof globalThis<"u"?globalThis:d||self,d.Reverb=f())})(this,function(){"use strict";const d=23283064365386963e-26;class f{float(e=1){return this.int()*d*e}probability(e){return this.float()<e}norm(e=1){return(this.int()*d-.5)*2*e}normMinMax(e,s){const i=this.minmax(e,s);return this.float()<.5?i:-i}minmax(e,s){return this.float()*(s-e)+e}minmaxInt(e,s){e|=0;const i=(s|0)-e;return i?e+this.int()%i:e}minmaxUint(e,s){e>>>=0;const i=(s>>>0)-e;return i?e+this.int()%i:e}}const y=Math.random;class x extends f{int(){return y()*4294967296>>>0}float(e=1){return y()*e}norm(e=1){return(y()-.5)*2*e}}const R=new x,F={noise:"white",scale:1,peaks:2,randomAlgorithm:R,decay:2,delay:0,reverse:!1,time:2,filterType:"allpass",filterFreq:2200,filterQ:1,mix:.5,once:!1},I={version:"1.3.7",date:"2024-01-29T10:30:54.595Z"},u={blue:"blue",brown:"red",green:"green",pink:"pink",red:"red",violet:"violet",white:"white"},m={bins:2,scale:1,rnd:R},v=(t,e,s)=>{const i=new Array(t);for(let n=0;n<t;n++)i[n]=s.norm(e);return i},N=t=>t.reduce((e,s)=>e+s,0);function*k(t,e){const s=[t[Symbol.iterator](),e[Symbol.iterator]()];for(let i=0;;i^=1){const n=s[i].next();if(n.done)return;yield n.value}}function*g(t){const{bins:e,scale:s,rnd:i}={...m,...t},n=v(e,s,i);n.forEach((r,a)=>n[a]=a&1?r:-r);const c=1/e;let o=N(n);for(let r=0,a=-1;;++r>=e&&(r=0))o-=n[r],o+=n[r]=a*i.norm(s),a^=4294967294,yield a*o*c}const C=t=>k(g(t),g(t)),E=t=>{let e=32;return t&=-t,t&&e--,t&65535&&(e-=16),t&16711935&&(e-=8),t&252645135&&(e-=4),t&858993459&&(e-=2),t&1431655765&&(e-=1),e};function*j(t){const{bins:e,scale:s,rnd:i}={...m,bins:8,...t},n=v(e,s,i),c=1/e;let o=N(n);for(let r=0;;r=r+1>>>0){const a=E(r)%e;o-=n[a],o+=n[a]=i.norm(s),yield o*c}}function*w(t){const{bins:e,scale:s,rnd:i}={...m,...t},n=v(e,s,i),c=1/e;let o=N(n);for(let r=0;;++r>=e&&(r=0))o-=n[r],o+=n[r]=i.norm(s),yield o*c}const M=t=>k(w(t),w(t));function*G(t){const{scale:e,rnd:s}={...m,...t};for(;;)yield s.norm(e)}const S=(t,e)=>t!=null&&typeof t[e]=="function",Q=t=>S(t,"xform")?t.xform():t,q=t=>t!=null&&typeof t[Symbol.iterator]=="function";class p{value;constructor(e){this.value=e}deref(){return this.value}}const D=t=>new p(t),L=t=>t instanceof p,B=t=>t instanceof p?t:new p(t),T=t=>t instanceof p?t.deref():t,O=(t,e)=>[t,s=>s,e];function U(t){return t?[...t]:O(()=>[],(e,s)=>(e.push(s),e))}function*z(t,e){const s=Q(t)(U()),i=s[1],n=s[2];for(let c of e){const o=n([],c);if(L(o)){yield*T(i(o.deref()));return}o.length&&(yield*o)}yield*T(i([]))}const P=(t,e)=>[t[0],t[1],e];function A(t,e){return q(e)?z(A(t),e):s=>{const i=s[2];let n=t;return P(s,(c,o)=>--n>0?i(c,o):n===0?B(i(c,o)):D(c))}}class h{static version=I.version;static build=I.date;ctx;wetGainNode;dryGainNode;filterNode;convolverNode;outputNode;options;isConnected;noise=G;constructor(e,s){this.ctx=e,this.options=Object.assign(F,s),this.wetGainNode=this.ctx.createGain(),this.dryGainNode=this.ctx.createGain(),this.filterNode=this.ctx.createBiquadFilter(),this.convolverNode=this.ctx.createConvolver(),this.outputNode=this.ctx.createGain(),this.isConnected=!1,this.filterType(this.options.filterType),this.setNoise(this.options.noise),this.buildImpulse(),this.mix(this.options.mix)}connect(e){return this.isConnected&&this.options.once?(this.isConnected=!1,this.outputNode):(this.convolverNode.connect(this.filterNode),this.filterNode.connect(this.wetGainNode),e.connect(this.convolverNode),e.connect(this.dryGainNode).connect(this.outputNode),e.connect(this.wetGainNode).connect(this.outputNode),this.isConnected=!0,this.outputNode)}disconnect(e){return this.isConnected&&(this.convolverNode.disconnect(this.filterNode),this.filterNode.disconnect(this.wetGainNode)),this.isConnected=!1,e}mix(e){if(!h.inRange(e,0,1))throw new RangeError("[Reverb.js] Dry/Wet ratio must be between 0 to 1.");this.options.mix=e,this.dryGainNode.gain.value=1-this.options.mix,this.wetGainNode.gain.value=this.options.mix}time(e){if(!h.inRange(e,1,50))throw new RangeError("[Reverb.js] Time length of inpulse response must be less than 50sec.");this.options.time=e,this.buildImpulse()}decay(e){if(!h.inRange(e,0,100))throw new RangeError("[Reverb.js] Inpulse Response decay level must be less than 100.");this.options.decay=e,this.buildImpulse()}delay(e){if(!h.inRange(e,0,100))throw new RangeError("[Reverb.js] Inpulse Response delay time must be less than 100.");this.options.delay=e,this.buildImpulse()}reverse(e){this.options.reverse=e,this.buildImpulse()}filterType(e="allpass"){this.filterNode.type=this.options.filterType=e}filterFreq(e){if(!h.inRange(e,20,2e4))throw new RangeError("[Reverb.js] Filter frequrncy must be between 20 and 20000.");this.options.filterFreq=e,this.filterNode.frequency.value=this.options.filterFreq}filterQ(e){if(!h.inRange(e,0,10))throw new RangeError("[Reverb.js] Filter Q value must be between 0 and 10.");this.options.filterQ=e,this.filterNode.Q.value=this.options.filterQ}peaks(e){this.options.peaks=e,this.buildImpulse()}scale(e){this.options.scale=e,this.buildImpulse()}randomAlgorithm(e){this.options.randomAlgorithm=e,this.buildImpulse()}setNoise(e){switch(this.options.noise=e,e){case u.blue:this.noise=g;break;case u.green:this.noise=C;break;case u.pink:this.noise=j;break;case u.red:case u.brown:this.noise=w;break;case u.violet:this.noise=M;break;default:this.noise=G}this.buildImpulse()}setRandomAlgorythm(e){this.options.randomAlgorithm=e,this.buildImpulse()}static inRange(e,s,i){return(e-s)*(e-i)<=0}buildImpulse(){const e=this.ctx.sampleRate,s=Math.max(e*this.options.time,1),i=e*this.options.delay,n=this.ctx.createBuffer(2,s,e),c=new Float32Array(s),o=new Float32Array(s),r=this.getNoise(s),a=this.getNoise(s);for(let l=0;l<s;l++){let b=0;l<i?(c[l]=0,o[l]=0,b=this.options.reverse??!1?s-(l-i):l-i):b=this.options.reverse??!1?s-l:l,c[l]=(r[l]??0)*(1-b/s)**this.options.decay,o[l]=(a[l]??0)*(1-b/s)**this.options.decay}n.getChannelData(0).set(c),n.getChannelData(1).set(o),this.convolverNode.buffer=n}getNoise(e){return[...A(e,this.noise({bins:this.options.peaks,scale:this.options.scale,rnd:this.options.randomAlgorithm}))]}}return h});