/
AutoWah.ts
208 lines (184 loc) · 5.39 KB
/
AutoWah.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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
import { Effect, EffectOptions } from "./Effect";
import { Filter } from "../component/filter/Filter";
import { Follower } from "../component/analysis/Follower";
import { Decibels, Frequency, GainFactor, Hertz, Positive, Time } from "../core/type/Units";
import { optionsFromArguments } from "../core/util/Defaults";
import { Gain } from "../core/context/Gain";
import { dbToGain, gainToDb } from "../core/type/Conversions";
import { ScaleExp } from "../signal/ScaleExp";
import { Signal } from "../signal/Signal";
import { readOnly } from "../core/util/Interface";
export interface AutoWahOptions extends EffectOptions {
baseFrequency: Frequency;
octaves: Positive;
sensitivity: Decibels;
Q: Positive;
gain: GainFactor;
follower: Time;
}
/**
* AutoWah connects a [[Follower]] to a [[Filter]].
* The frequency of the filter, follows the input amplitude curve.
* Inspiration from [Tuna.js](https://github.com/Dinahmoe/tuna).
*
* @example
* const autoWah = new Tone.AutoWah(50, 6, -30).toDestination();
* // initialize the synth and connect to autowah
* const synth = new Tone.Synth().connect(autoWah);
* // Q value influences the effect of the wah - default is 2
* autoWah.Q.value = 6;
* // more audible on higher notes
* synth.triggerAttackRelease("C4", "8n");
* @category Effect
*/
export class AutoWah extends Effect<AutoWahOptions> {
readonly name: string = "AutoWah";
/**
* The envelope follower. Set the attack/release
* timing to adjust how the envelope is followed.
*/
private _follower: Follower;
/**
* scales the follower value to the frequency domain
*/
private _sweepRange: ScaleExp;
/**
* Hold the base frequency value
*/
private _baseFrequency: Hertz;
/**
* Private holder for the octave count
*/
private _octaves: Positive;
/**
* the input gain to adjust the sensitivity
*/
private _inputBoost: Gain;
/**
* Private holder for the filter
*/
private _bandpass: Filter;
/**
* The peaking fitler
*/
private _peaking: Filter;
/**
* The gain of the filter.
*/
readonly gain: Signal<"decibels">;
/**
* The quality of the filter.
*/
readonly Q: Signal<"positive">;
/**
* @param baseFrequency The frequency the filter is set to at the low point of the wah
* @param octaves The number of octaves above the baseFrequency the filter will sweep to when fully open.
* @param sensitivity The decibel threshold sensitivity for the incoming signal. Normal range of -40 to 0.
*/
constructor(baseFrequency?: Frequency, octaves?: Positive, sensitivity?: Decibels);
constructor(options?: Partial<AutoWahOptions>);
constructor() {
super(optionsFromArguments(AutoWah.getDefaults(), arguments, ["baseFrequency", "octaves", "sensitivity"]));
const options = optionsFromArguments(AutoWah.getDefaults(), arguments, ["baseFrequency", "octaves", "sensitivity"]);
this._follower = new Follower({
context: this.context,
smoothing: options.follower,
});
this._sweepRange = new ScaleExp({
context: this.context,
min: 0,
max: 1,
exponent: 0.5,
});
this._baseFrequency = this.toFrequency(options.baseFrequency);
this._octaves = options.octaves;
this._inputBoost = new Gain({ context: this.context });
this._bandpass = new Filter({
context: this.context,
rolloff: -48,
frequency: 0,
Q: options.Q,
});
this._peaking = new Filter({
context: this.context,
type: "peaking"
});
this._peaking.gain.value = options.gain;
this.gain = this._peaking.gain;
this.Q = this._bandpass.Q;
// the control signal path
this.effectSend.chain(this._inputBoost, this._follower, this._sweepRange);
this._sweepRange.connect(this._bandpass.frequency);
this._sweepRange.connect(this._peaking.frequency);
// the filtered path
this.effectSend.chain(this._bandpass, this._peaking, this.effectReturn);
// set the initial value
this._setSweepRange();
this.sensitivity = options.sensitivity;
readOnly(this, ["gain", "Q"]);
}
static getDefaults(): AutoWahOptions {
return Object.assign(Effect.getDefaults(), {
baseFrequency: 100,
octaves: 6,
sensitivity: 0,
Q: 2,
gain: 2,
follower: 0.2,
});
}
/**
* The number of octaves that the filter will sweep above the baseFrequency.
*/
get octaves() {
return this._octaves;
}
set octaves(octaves) {
this._octaves = octaves;
this._setSweepRange();
}
/**
* The follower's smoothing time
*/
get follower(): Time {
return this._follower.smoothing;
}
set follower(follower) {
this._follower.smoothing = follower;
}
/**
* The base frequency from which the sweep will start from.
*/
get baseFrequency(): Frequency {
return this._baseFrequency;
}
set baseFrequency(baseFreq) {
this._baseFrequency = this.toFrequency(baseFreq);
this._setSweepRange();
}
/**
* The sensitivity to control how responsive to the input signal the filter is.
*/
get sensitivity(): Decibels {
return gainToDb(1 / this._inputBoost.gain.value);
}
set sensitivity(sensitivity) {
this._inputBoost.gain.value = 1 / dbToGain(sensitivity);
}
/**
* sets the sweep range of the scaler
*/
private _setSweepRange() {
this._sweepRange.min = this._baseFrequency;
this._sweepRange.max = Math.min(this._baseFrequency * Math.pow(2, this._octaves), this.context.sampleRate / 2);
}
dispose(): this {
super.dispose();
this._follower.dispose();
this._sweepRange.dispose();
this._bandpass.dispose();
this._peaking.dispose();
this._inputBoost.dispose();
return this;
}
}