/
Freeverb.ts
137 lines (117 loc) · 4.12 KB
/
Freeverb.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import { StereoEffect, StereoEffectOptions } from "./StereoEffect";
import { Frequency, NormalRange } from "../core/type/Units";
import { optionsFromArguments } from "../core/util/Defaults";
import { readOnly } from "../core/util/Interface";
import { Signal } from "../signal/Signal";
import { LowpassCombFilter } from "../component/filter/LowpassCombFilter";
export interface FreeverbOptions extends StereoEffectOptions {
dampening: Frequency;
roomSize: NormalRange;
}
/**
* An array of comb filter delay values from Freeverb implementation
*/
const combFilterTunings = [1557 / 44100, 1617 / 44100, 1491 / 44100, 1422 / 44100, 1277 / 44100, 1356 / 44100, 1188 / 44100, 1116 / 44100];
/**
* An array of allpass filter frequency values from Freeverb implementation
*/
const allpassFilterFrequencies = [225, 556, 441, 341];
/**
* Freeverb is a reverb based on [Freeverb](https://ccrma.stanford.edu/~jos/pasp/Freeverb.html).
* Read more on reverb on [Sound On Sound](https://web.archive.org/web/20160404083902/http://www.soundonsound.com:80/sos/feb01/articles/synthsecrets.asp).
* Freeverb is now implemented with an AudioWorkletNode which may result on performance degradation on some platforms. Consider using [[Reverb]].
* @example
* const freeverb = new Tone.Freeverb().toDestination();
* freeverb.dampening = 1000;
* // routing synth through the reverb
* const synth = new Tone.NoiseSynth().connect(freeverb);
* synth.triggerAttackRelease(0.05);
* @category Effect
*/
export class Freeverb extends StereoEffect<FreeverbOptions> {
readonly name: string = "Freeverb";
/**
* The roomSize value between 0 and 1. A larger roomSize will result in a longer decay.
*/
readonly roomSize: Signal<"normalRange">;
/**
* the comb filters
*/
private _combFilters: LowpassCombFilter[] = [];
/**
* the allpass filters on the left
*/
private _allpassFiltersL: BiquadFilterNode[] = [];
/**
* the allpass filters on the right
*/
private _allpassFiltersR: BiquadFilterNode[] = [];
/**
* @param roomSize Correlated to the decay time.
* @param dampening The cutoff frequency of a lowpass filter as part of the reverb.
*/
constructor(roomSize?: NormalRange, dampening?: Frequency);
constructor(options?: Partial<FreeverbOptions>);
constructor() {
super(optionsFromArguments(Freeverb.getDefaults(), arguments, ["roomSize", "dampening"]));
const options = optionsFromArguments(Freeverb.getDefaults(), arguments, ["roomSize", "dampening"]);
this.roomSize = new Signal({
context: this.context,
value: options.roomSize,
units: "normalRange",
});
// make the allpass filters on the right
this._allpassFiltersL = allpassFilterFrequencies.map(freq => {
const allpassL = this.context.createBiquadFilter();
allpassL.type = "allpass";
allpassL.frequency.value = freq;
return allpassL;
});
// make the allpass filters on the left
this._allpassFiltersR = allpassFilterFrequencies.map(freq => {
const allpassR = this.context.createBiquadFilter();
allpassR.type = "allpass";
allpassR.frequency.value = freq;
return allpassR;
});
// make the comb filters
this._combFilters = combFilterTunings.map((delayTime, index) => {
const lfpf = new LowpassCombFilter({
context: this.context,
dampening: options.dampening,
delayTime,
});
if (index < combFilterTunings.length / 2) {
this.connectEffectLeft(lfpf, ...this._allpassFiltersL);
} else {
this.connectEffectRight(lfpf, ...this._allpassFiltersR);
}
this.roomSize.connect(lfpf.resonance);
return lfpf;
});
readOnly(this, ["roomSize"]);
}
static getDefaults(): FreeverbOptions {
return Object.assign(StereoEffect.getDefaults(), {
roomSize: 0.7,
dampening: 3000
});
}
/**
* The amount of dampening of the reverberant signal.
*/
get dampening(): Frequency {
return this._combFilters[0].dampening;
}
set dampening(d) {
this._combFilters.forEach(c => c.dampening = d);
}
dispose(): this {
super.dispose();
this._allpassFiltersL.forEach(al => al.disconnect());
this._allpassFiltersR.forEach(ar => ar.disconnect());
this._combFilters.forEach(cf => cf.dispose());
this.roomSize.dispose();
return this;
}
}