0bd634be5e1ef72992574f7d1e349ee1ca4f66cc
[Faustine.git] / interpretor / faust-0.9.47mr3 / architecture / effect.lib
1 declare name "Faust Audio Effect Library";
2 declare author "Julius O. Smith (jos at ccrma.stanford.edu)";
3 declare copyright "Julius O. Smith III";
4 declare version "1.33";
5 declare license "STK-4.3"; // Synthesis Tool Kit 4.3 (MIT style license)
6 declare reference "https://ccrma.stanford.edu/realsimple/faust_strings/";
7
8 import("filter.lib"); // dcblocker*, lowpass, filterbank, ...
9
10 // The following utilities (or equivalents) could go in music.lib:
11
12 //----------------------- midikey2hz,pianokey2hz ------------------------
13 midikey2hz(x) = 440.0*pow(2.0, (x-69.0)/12); // MIDI key 69 = A440
14 pianokey2hz(x) = 440.0*pow(2.0, (x-49.0)/12); // piano key 49 = A440
15
16 //---------------- cross2, bypass1, bypass2, select2stereo --------------
17 //
18 cross2 = _,_,_,_ <: _,!,_,!,!,_,!,_;
19
20 bypass1(bpc,e) = _ <: select2(bpc,(inswitch:e),_)
21 with {inswitch = select2(bpc,_,0);};
22
23 bypass2(bpc,e) = _,_ <: ((inswitch:e),_,_) : select2stereo(bpc) with {
24 inswitch = _,_ : (select2(bpc,_,0), select2(bpc,_,0)) : _,_;
25 };
26
27 select2stereo(bpc) = cross2 : select2(bpc), select2(bpc) : _,_;
28
29 //---------------------- levelfilter, levelfilterN ----------------------
30 // Dynamic level lowpass filter:
31 //
32 // USAGE: levelfilter(L,freq), where
33 // L = desired level (in dB) at Nyquist limit (SR/2), e.g., -60
34 // freq = corner frequency (-3dB point) usually set to fundamental freq
35 //
36 // REFERENCE:
37 // https://ccrma.stanford.edu/realsimple/faust_strings/Dynamic_Level_Lowpass_Filter.html
38 //
39 levelfilter(L,freq,x) = (L * L0 * x) + ((1.0-L) * lp2out(x))
40 with {
41 L0 = pow(L,1/3);
42 Lw = PI*freq/SR; // = w1 T / 2
43 Lgain = Lw / (1.0 + Lw);
44 Lpole2 = (1.0 - Lw) / (1.0 + Lw);
45 lp2out = *(Lgain) : + ~ *(Lpole2);
46 };
47
48 levelfilterN(N,freq,L) = seq(i,N,levelfilter((L/N),freq));
49
50 //------------------------- speakerbp -------------------------------
51 // Dirt-simple speaker simulator (overall bandpass eq with observed
52 // roll-offs above and below the passband).
53 //
54 // Low-frequency speaker model = +12 dB/octave slope breaking to
55 // flat near f1. Implemented using two dc blockers in series.
56 //
57 // High-frequency model = -24 dB/octave slope implemented using a
58 // fourth-order Butterworth lowpass.
59 //
60 // Example based on measured Celestion G12 (12" speaker):
61 // speakerbp(130,5000);
62 //
63 // Requires filter.lib
64 //
65 speakerbp(f1,f2) = dcblockerat(f1) : dcblockerat(f1) : lowpass(4,f2);
66
67
68 //--------------------- cubicnl(drive,offset) -----------------------
69 // Cubic nonlinearity distortion
70 //
71 // USAGE: cubicnl(drive,offset), where
72 // drive = distortion amount, between 0 and 1
73 // offset = constant added before nonlinearity to give even harmonics
74 // Note: offset can introduce a nonzero mean - feed
75 // cubicnl output to dcblocker to remove this.
76 //
77 // REFERENCES:
78 // https://ccrma.stanford.edu/~jos/pasp/Cubic_Soft_Clipper.html
79 // https://ccrma.stanford.edu/~jos/pasp/Nonlinear_Distortion.html
80 //
81 cubicnl(drive,offset) = *(pregain) : +(offset) : clip(-1,1) : cubic
82 with {
83 pregain = pow(10.0,2*drive);
84 clip(lo,hi) = min(hi) : max(lo);
85 cubic(x) = x - x*x*x/3;
86 postgain = max(1.0,1.0/pregain); // unity gain when nearly linear
87 };
88
89 cubicnl_nodc(drive,offset) = cubicnl(drive,offset) : dcblocker;
90
91 //--------------------------- cubicnl_demo --------------------------
92 // USAGE: _ : cubicnl_demo : _;
93 //
94 cubicnl_demo = bypass1(bp,
95 cubicnl_nodc(drive:smooth(0.999),offset:smooth(0.999)))
96 with {
97 cnl_group(x) = vgroup("CUBIC NONLINEARITY cubicnl
98 [tooltip: Reference:
99 https://ccrma.stanford.edu/~jos/pasp/Cubic_Soft_Clipper.html]", x);
100 // bypass_group(x) = cnl_group(hgroup("[0]", x));
101 slider_group(x) = cnl_group(hgroup("[1]", x));
102 // bp = bypass_group(checkbox("[0] Bypass
103 bp = slider_group(checkbox("[0] Bypass
104 [tooltip: When this is checked, the nonlinearity has no effect]"));
105 // drive = slider_group(vslider("[1] Drive [style: knob]
106 drive = slider_group(hslider("[1] Drive
107 [tooltip: Amount of distortion]",
108 0, 0, 1, 0.01));
109 // offset = slider_group(vslider("[2] Offset [style: knob]
110 offset = slider_group(hslider("[2] Offset
111 [tooltip: Brings in even harmonics]",
112 0, 0, 1, 0.01));
113 };
114
115 //------------------------- moog_vcf(res,fr) ---------------------------
116 // Moog "Voltage Controlled Filter" (VCF) in "analog" form
117 //
118 // USAGE: moog_vcf(res,fr), where
119 // fr = corner-resonance frequency in Hz ( less than SR/6.3 or so )
120 // res = Normalized amount of corner-resonance between 0 and 1
121 // (0 is no resonance, 1 is maximum)
122 // Requires filter.lib.
123 //
124 // DESCRIPTION: Moog VCF implemented using the same logical block diagram
125 // as the classic analog circuit. As such, it neglects the one-sample
126 // delay associated with the feedback path around the four one-poles.
127 // This extra delay alters the response, especially at high frequencies
128 // (see reference [1] for details).
129 // See moog_vcf_2b below for a more accurate implementation.
130 //
131 // REFERENCES:
132 // [1] https://ccrma.stanford.edu/~stilti/papers/moogvcf.pdf
133 // [2] https://ccrma.stanford.edu/~jos/pasp/vegf.html
134 //
135 moog_vcf(res,fr) = (+ : seq(i,4,pole(p)) : *(unitygain(p))) ~ *(mk)
136 with {
137 p = 1.0 - fr * 2.0 * PI / SR; // good approximation for fr << SR
138 unitygain(p) = pow(1.0-p,4.0); // one-pole unity-gain scaling
139 mk = -4.0*max(0,min(res,0.999999)); // need mk > -4 for stability
140 };
141
142 //----------------------- moog_vcf_2b[n] ---------------------------
143 // Moog "Voltage Controlled Filter" (VCF) as two biquads
144 //
145 // USAGE:
146 // moog_vcf_2b(res,fr)
147 // moog_vcf_2bn(res,fr)
148 // where
149 // fr = corner-resonance frequency in Hz
150 // res = Normalized amount of corner-resonance between 0 and 1
151 // (0 is min resonance, 1 is maximum)
152 //
153 // DESCRIPTION: Implementation of the ideal Moog VCF transfer
154 // function factored into second-order sections. As a result, it is
155 // more accurate than moog_vcf above, but its coefficient formulas are
156 // more complex when one or both parameters are varied. Here, res
157 // is the fourth root of that in moog_vcf, so, as the sampling rate
158 // approaches infinity, moog_vcf(res,fr) becomes equivalent
159 // to moog_vcf_2b[n](res^4,fr) (when res and fr are constant).
160 //
161 // moog_vcf_2b uses two direct-form biquads (tf2)
162 // moog_vcf_2bn uses two protected normalized-ladder biquads (tf2np)
163 //
164 // REQUIRES: filter.lib
165 //
166 moog_vcf_2b(res,fr) = tf2s(0,0,b0,a11,a01,w1) : tf2s(0,0,b0,a12,a02,w1)
167 with {
168 s = 1; // minus the open-loop location of all four poles
169 frl = max(20,min(10000,fr)); // limit fr to reasonable 20-10k Hz range
170 w1 = 2*PI*frl; // frequency-scaling parameter for bilinear xform
171 // Equivalent: w1 = 1; s = 2*PI*frl;
172 kmax = sqrt(2)*0.999; // 0.999 gives stability margin (tf2 is unprotected)
173 k = min(kmax,sqrt(2)*res); // fourth root of Moog VCF feedback gain
174 b0 = s^2;
175 s2k = sqrt(2) * k;
176 a11 = s * (2 + s2k);
177 a12 = s * (2 - s2k);
178 a01 = b0 * (1 + s2k + k^2);
179 a02 = b0 * (1 - s2k + k^2);
180 };
181
182 moog_vcf_2bn(res,fr) = tf2snp(0,0,b0,a11,a01,w1) : tf2snp(0,0,b0,a12,a02,w1)
183 with {
184 s = 1; // minus the open-loop location of all four poles
185 w1 = 2*PI*max(fr,20); // frequency-scaling parameter for bilinear xform
186 k = sqrt(2)*0.999*res; // fourth root of Moog VCF feedback gain
187 b0 = s^2;
188 s2k = sqrt(2) * k;
189 a11 = s * (2 + s2k);
190 a12 = s * (2 - s2k);
191 a01 = b0 * (1 + s2k + k^2);
192 a02 = b0 * (1 - s2k + k^2);
193 };
194
195 //------------------------- moog_vcf_demo ---------------------------
196 // Illustrate and compare all three Moog VCF implementations above
197 // (called by <faust>/examples/vcf_wah_pedals.dsp).
198 //
199 // USAGE: _ : moog_vcf_demo : _;
200
201 moog_vcf_demo = bypass1(bp,vcf) with {
202 mvcf_group(x) = hgroup("MOOG VCF (Voltage Controlled Filter)
203 [tooltip: See Faust's effect.lib for info and references]",x);
204
205 meter_group(x) = mvcf_group(vgroup("[0]",x));
206 cb_group(x) = meter_group(hgroup("[0]",x));
207
208 bp = cb_group(checkbox("[0] Bypass [tooltip: When this is checked, the Moog VCF has no effect]"));
209 archsw = cb_group(checkbox("[1] Use Biquads
210 [tooltip: Select moog_vcf_2b (two-biquad) implementation, instead of the default moog_vcf (analog style) implementation]"));
211 bqsw = cb_group(checkbox("[2] Normalized Ladders
212 [tooltip: If using biquads, make them normalized ladders (moog_vcf_2bn)]"));
213
214 freq = mvcf_group(hslider("[1] Corner Frequency [unit:PK] [style:knob]
215 [tooltip: The VCF resonates at the corner frequency (specified in PianoKey (PK) units, with A440 = 49 PK). The VCF response is flat below the corner frequency, and rolls off -24 dB per octave above.]",
216 25, 1, 88, 0.01) : pianokey2hz) : smooth(0.999);
217
218 res = mvcf_group(hslider("[2] Corner Resonance [style:knob]
219 [tooltip: Amount of resonance near VCF corner frequency (specified between 0 and 1)]",
220 0.9, 0, 1, 0.01));
221
222 outgain = meter_group(hslider("[1] VCF Output Level [unit:dB]
223 [tooltip: output level in decibels]",
224 5, -60, 20, 0.1)) : smooth(0.999)
225 : component("music.lib").db2linear;
226
227 vcfbq = _ <: select2(bqsw, moog_vcf_2b(res,freq), moog_vcf_2bn(res,freq));
228 vcfarch = _ <: select2(archsw, moog_vcf(res^4,freq), vcfbq);
229 vcf = vcfarch : *(outgain);
230 };
231
232 //-------------------------- wah4(fr) -------------------------------
233 // Wah effect, 4th order
234 // USAGE: wah4(fr), where fr = resonance frequency in Hz
235 // REFERENCE "https://ccrma.stanford.edu/~jos/pasp/vegf.html";
236 //
237 wah4(fr) = 4*moog_vcf((3.2/4),fr:smooth(0.999));
238
239 //------------------------- wah4_demo ---------------------------
240 // USAGE: _ : wah4_demo : _;
241
242 wah4_demo = bypass1(bp, wah4(fr)) with {
243 wah4_group(x) = hgroup("WAH4
244 [tooltip: Fourth-order wah effect made using moog_vcf]", x);
245 bp = wah4_group(checkbox("[0] Bypass
246 [tooltip: When this is checked, the wah pedal has no effect]"));
247 fr = wah4_group(hslider("[1] Resonance Frequency
248 [tooltip: wah resonance frequency in Hz]",
249 200,100,2000,1));
250 // Avoid dc with the moog_vcf (amplitude too high when freq comes up from dc)
251 // Also, avoid very high resonance frequencies (e.g., 5kHz or above).
252 };
253
254 //------------------------ autowah(level) -----------------------------
255 // Auto-wah effect
256 // USAGE: _ : autowah(level) : _;
257 // where level = amount of effect desired (0 to 1).
258 //
259 autowah(level,x) = level * crybaby(amp_follower(0.1,x),x) + (1.0-level)*x;
260
261 //-------------------------- crybaby(wah) -----------------------------
262 // Digitized CryBaby wah pedal
263 // USAGE: _ : crybaby(wah) : _;
264 // where wah = "pedal angle" from 0 to 1.
265 // REFERENCE: https://ccrma.stanford.edu/~jos/pasp/vegf.html
266 //
267 crybaby(wah) = *(gs) : tf2(1,-1,0,a1s,a2s)
268 with {
269 Q = pow(2.0,(2.0*(1.0-wah)+1.0)); // Resonance "quality factor"
270 fr = 450.0*pow(2.0,2.3*wah); // Resonance tuning
271 g = 0.1*pow(4.0,wah); // gain (optional)
272
273 // Biquad fit using z = exp(s T) ~ 1 + sT for low frequencies:
274 frn = fr/SR; // Normalized pole frequency (cycles per sample)
275 R = 1 - PI*frn/Q; // pole radius
276 theta = 2*PI*frn; // pole angle
277 a1 = 0-2.0*R*cos(theta); // biquad coeff
278 a2 = R*R; // biquad coeff
279
280 // dezippering of slider-driven signals:
281 s = 0.999; // smoothing parameter (one-pole pole location)
282 a1s = a1 : smooth(s);
283 a2s = a2 : smooth(s);
284 gs = g : smooth(s);
285
286 tf2 = component("filter.lib").tf2;
287 };
288
289 //------------------------- crybaby_demo ---------------------------
290 // USAGE: _ : crybaby_demo : _ ;
291
292 crybaby_demo = bypass1(bp, crybaby(wah)) with {
293 crybaby_group(x) = hgroup("CRYBABY [tooltip: Reference: https://ccrma.stanford.edu/~jos/pasp/vegf.html]", x);
294 bp = crybaby_group(checkbox("[0] Bypass [tooltip: When this is checked, the wah pedal has no effect]"));
295 wah = crybaby_group(hslider("[1] Wah parameter [tooltip: wah pedal angle between 0 (rocked back) and 1 (rocked forward)]",0.8,0,1,0.01));
296 };
297
298 //------------ apnl(a1,a2) ---------------
299 // Passive Nonlinear Allpass:
300 // switch between allpass coefficient a1 and a2 at signal zero crossings
301 // REFERENCE:
302 // "A Passive Nonlinear Digital Filter Design ..."
303 // by John R. Pierce and Scott A. Van Duyne,
304 // JASA, vol. 101, no. 2, pp. 1120-1126, 1997
305 // Written by Romain Michon and JOS based on Pierce switching springs idea:
306 apnl(a1,a2,x) = nonLinFilter
307 with{
308 condition = _>0;
309 nonLinFilter = (x - _ <: _*(condition*a1 + (1-condition)*a2),_')~_ :> +;
310 };
311
312 //------------ piano_dispersion_filter(M,B,f0) ---------------
313 // Piano dispersion allpass filter in closed form
314 //
315 // ARGUMENTS:
316 // M = number of first-order allpass sections (compile-time only)
317 // Keep below 20. 8 is typical for medium-sized piano strings.
318 // B = string inharmonicity coefficient (0.0001 is typical)
319 // f0 = fundamental frequency in Hz
320 //
321 // INPUT:
322 // Signal to be filtered by the allpass chain
323 //
324 // OUTPUTS:
325 // 1. MINUS the estimated delay at f0 of allpass chain in samples,
326 // provided in negative form to facilitate subtraction
327 // from delay-line length (see USAGE below).
328 // 2. Output signal from allpass chain
329 //
330 // USAGE:
331 // piano_dispersion_filter(1,B,f0) : +(totalDelay),_ : fdelay(maxDelay)
332 //
333 // REFERENCE:
334 // "Dispersion Modeling in Waveguide Piano Synthesis
335 // Using Tunable Allpass Filters",
336 // by Jukka Rauhala and Vesa Valimaki, DAFX-2006, pp. 71-76
337 // URL: http://www.dafx.ca/proceedings/papers/p_071.pdf
338 // NOTE: An erratum in Eq. (7) is corrected in Dr. Rauhala's
339 // encompassing dissertation (and below).
340 // See also: http://www.acoustics.hut.fi/research/asp/piano/
341 //
342 piano_dispersion_filter(M,B,f0) = -Df0*M,seq(i,M,tf1(a1,1,a1))
343 with {
344 a1 = (1-D)/(1+D); // By Eq. 3, have D >= 0, hence a1 >= 0 also
345 D = exp(Cd - Ikey(f0)*kd);
346 trt = pow(2.0,1.0/12.0); // 12th root of 2
347 logb(b,x) = log(x) / log(b); // log-base-b of x
348 Ikey(f0) = logb(trt,f0*trt/27.5);
349 Bc = max(B,0.000001);
350 kd = exp(k1*log(Bc)*log(Bc) + k2*log(Bc)+k3);
351 Cd = exp((m1*log(M)+m2)*log(Bc)+m3*log(M)+m4);
352 k1 = -0.00179;
353 k2 = -0.0233;
354 k3 = -2.93;
355 m1 = 0.0126;
356 m2 = 0.0606;
357 m3 = -0.00825;
358 m4 = 1.97;
359 wT = 2*PI*f0/SR;
360 polydel(a) = atan(sin(wT)/(a+cos(wT)))/wT;
361 Df0 = polydel(a1) - polydel(1.0/a1);
362 };
363
364 //===================== Phasing and Flanging Effects ====================
365
366 //--------------- flanger_mono, flanger_stereo, flanger_demo -------------
367 // Flanging effect
368 //
369 // USAGE:
370 // _ : flanger_mono(dmax,curdel,depth,fb,invert) : _;
371 // _,_ : flanger_stereo(dmax,curdel1,curdel2,depth,fb,invert) : _,_;
372 // _,_ : flanger_demo : _,_;
373 //
374 // ARGUMENTS:
375 // dmax = maximum delay-line length (power of 2) - 10 ms typical
376 // curdel = current dynamic delay (not to exceed dmax)
377 // depth = effect strength between 0 and 1 (1 typical)
378 // fb = feedback gain between 0 and 1 (0 typical)
379 // invert = 0 for normal, 1 to invert sign of flanging sum
380 //
381 // REFERENCE:
382 // https://ccrma.stanford.edu/~jos/pasp/Flanging.html
383 //
384 flanger_mono(dmax,curdel,depth,fb,invert)
385 = _ <: _, (-:fdelay(dmax,curdel)) ~ *(fb) : _,
386 *(select2(invert,depth,0-depth))
387 : + : *(0.5);
388
389 flanger_stereo(dmax,curdel1,curdel2,depth,fb,invert)
390 = flanger_mono(dmax,curdel1,depth,fb,invert),
391 flanger_mono(dmax,curdel2,depth,fb,invert);
392
393 //------------------------- flanger_demo ---------------------------
394 // USAGE: _,_ : flanger_demo : _,_;
395 //
396 flanger_demo = bypass2(fbp,flanger_stereo_demo) with {
397 flanger_group(x) =
398 vgroup("FLANGER [tooltip: Reference: https://ccrma.stanford.edu/~jos/pasp/Flanging.html]", x);
399 meter_group(x) = flanger_group(hgroup("[0]", x));
400 ctl_group(x) = flanger_group(hgroup("[1]", x));
401 del_group(x) = flanger_group(hgroup("[2] Delay Controls", x));
402 lvl_group(x) = flanger_group(hgroup("[3]", x));
403
404 fbp = meter_group(checkbox(
405 "[0] Bypass [tooltip: When this is checked, the flanger has no effect]"));
406 invert = meter_group(checkbox("[1] Invert Flange Sum"));
407
408 // FIXME: This should be an amplitude-response display:
409 flangeview = lfor(freq) + lfol(freq) : meter_group(hbargraph(
410 "[2] Flange LFO [style: led] [tooltip: Display sum of flange delays]", -1.5,+1.5));
411
412 flanger_stereo_demo(x,y) = attach(x,flangeview),y :
413 *(level),*(level) : flanger_stereo(dmax,curdel1,curdel2,depth,fb,invert);
414
415 lfol = component("oscillator.lib").oscrs; // sine for left channel
416 lfor = component("oscillator.lib").oscrc; // cosine for right channel
417 dmax = 2048;
418 dflange = 0.001 * SR *
419 del_group(hslider("[1] Flange Delay [unit:ms] [style:knob]", 10, 0, 20, 0.001));
420 odflange = 0.001 * SR *
421 del_group(hslider("[2] Delay Offset [unit:ms] [style:knob]", 1, 0, 20, 0.001));
422 freq = ctl_group(hslider("[1] Speed [unit:Hz] [style:knob]", 0.5, 0, 10, 0.01));
423 depth = ctl_group(hslider("[2] Depth [style:knob]", 1, 0, 1, 0.001));
424 fb = ctl_group(hslider("[3] Feedback [style:knob]", 0, -0.999, 0.999, 0.001));
425 level = lvl_group(hslider("Flanger Output Level [unit:dB]", 0, -60, 10, 0.1)) : db2linear;
426 curdel1 = odflange+dflange*(1 + lfol(freq))/2;
427 curdel2 = odflange+dflange*(1 + lfor(freq))/2;
428 };
429
430 //------- phaser2_mono, phaser2_stereo, phaser2_demo -------
431 // Phasing effect
432 //
433 // USAGE:
434 // _ : phaser2_mono(Notches,width,frqmin,fratio,frqmax,speed,depth,fb,invert) : _;
435 // _,_ : phaser2_stereo(") : _,_;
436 // _,_ : phaser2_demo : _,_;
437 //
438 // ARGUMENTS:
439 // Notches = number of spectral notches (MACRO ARGUMENT - not a signal)
440 // width = approximate width of spectral notches in Hz
441 // frqmin = approximate minimum frequency of first spectral notch in Hz
442 // fratio = ratio of adjacent notch frequencies
443 // frqmax = approximate maximum frequency of first spectral notch in Hz
444 // speed = LFO frequency in Hz (rate of periodic notch sweep cycles)
445 // depth = effect strength between 0 and 1 (1 typical) (aka "intensity")
446 // when depth=2, "vibrato mode" is obtained (pure allpass chain)
447 // fb = feedback gain between -1 and 1 (0 typical)
448 // invert = 0 for normal, 1 to invert sign of flanging sum
449 //
450 // REFERENCES:
451 // https://ccrma.stanford.edu/~jos/pasp/Phasing.html
452 // http://www.geofex.com/Article_Folders/phasers/phase.html
453 // 'An Allpass Approach to Digital Phasing and Flanging', Julius O. Smith III,
454 // Proc. Int. Computer Music Conf. (ICMC-84), pp. 103-109, Paris, 1984.
455 // CCRMA Tech. Report STAN-M-21: https://ccrma.stanford.edu/STANM/stanms/stanm21/
456
457 vibrato2_mono(sections,phase01,fb,width,frqmin,fratio,frqmax,speed) =
458 (+ : seq(i,sections,ap2p(R,th(i)))) ~ *(fb)
459 with {
460 tf2 = component("filter.lib").tf2;
461 // second-order resonant digital allpass given pole radius and angle:
462 ap2p(R,th) = tf2(a2,a1,1,a1,a2) with {
463 a2 = R^2;
464 a1 = -2*R*cos(th);
465 };
466 SR = component("music.lib").SR;
467 R = exp(-pi*width/SR);
468 cososc = component("oscillator.lib").oscrc;
469 sinosc = component("oscillator.lib").oscrs;
470 osc = cososc(speed) * phase01 + sinosc(speed) * (1-phase01);
471 lfo = (1-osc)/2; // in [0,1]
472 pi = 4*atan(1);
473 thmin = 2*pi*frqmin/SR;
474 thmax = 2*pi*frqmax/SR;
475 th1 = thmin + (thmax-thmin)*lfo;
476 th(i) = (fratio^(i+1))*th1;
477 };
478
479 phaser2_mono(Notches,phase01,width,frqmin,fratio,frqmax,speed,depth,fb,invert) =
480 _ <: *(g1) + g2mi*vibrato2_mono(Notches,phase01,fb,width,frqmin,fratio,frqmax,speed)
481 with { // depth=0 => direct-signal only
482 g1 = 1-depth/2; // depth=1 => phaser mode (equal sum of direct and allpass-chain)
483 g2 = depth/2; // depth=2 => vibrato mode (allpass-chain signal only)
484 g2mi = select2(invert,g2,-g2); // inversion negates the allpass-chain signal
485 };
486
487 phaser2_stereo(Notches,width,frqmin,fratio,frqmax,speed,depth,fb,invert)
488 = phaser2_mono(Notches,0,width,frqmin,fratio,frqmax,speed,depth,fb,invert),
489 phaser2_mono(Notches,1,width,frqmin,fratio,frqmax,speed,depth,fb,invert);
490
491 //------------------------- phaser2_demo ---------------------------
492 // USAGE: _,_ : phaser2_demo : _,_;
493 //
494 phaser2_demo = bypass2(pbp,phaser2_stereo_demo) with {
495 phaser2_group(x) =
496 vgroup("PHASER2 [tooltip: Reference: https://ccrma.stanford.edu/~jos/pasp/Flanging.html]", x);
497 meter_group(x) = phaser2_group(hgroup("[0]", x));
498 ctl_group(x) = phaser2_group(hgroup("[1]", x));
499 nch_group(x) = phaser2_group(hgroup("[2]", x));
500 lvl_group(x) = phaser2_group(hgroup("[3]", x));
501
502 pbp = meter_group(checkbox(
503 "[0] Bypass [tooltip: When this is checked, the phaser has no effect]"));
504 invert = meter_group(checkbox("[1] Invert Internal Phaser Sum"));
505 vibr = meter_group(checkbox("[2] Vibrato Mode")); // In this mode you can hear any "Doppler"
506
507 // FIXME: This should be an amplitude-response display:
508 //flangeview = phaser2_amp_resp : meter_group(hspectrumview("[2] Phaser Amplitude Response", 0,1));
509 //phaser2_stereo_demo(x,y) = attach(x,flangeview),y : ...
510
511 phaser2_stereo_demo = *(level),*(level) :
512 phaser2_stereo(Notches,width,frqmin,fratio,frqmax,speed,mdepth,fb,invert);
513
514 Notches = 4; // Compile-time parameter: 2 is typical for analog phaser stomp-boxes
515
516 // FIXME: Add tooltips
517 speed = ctl_group(hslider("[1] Speed [unit:Hz] [style:knob]", 0.5, 0, 10, 0.001));
518 depth = ctl_group(hslider("[2] Notch Depth (Intensity) [style:knob]", 1, 0, 1, 0.001));
519 fb = ctl_group(hslider("[3] Feedback Gain [style:knob]", 0, -0.999, 0.999, 0.001));
520
521 width = nch_group(hslider("[1] Notch width [unit:Hz] [style:knob]", 1000, 10, 5000, 1));
522 frqmin = nch_group(hslider("[2] Min Notch1 Freq [unit:Hz] [style:knob]", 100, 20, 5000, 1));
523 frqmax = nch_group(hslider("[3] Max Notch1 Freq [unit:Hz] [style:knob]", 800, 20, 10000, 1)) : max(frqmin);
524 fratio = nch_group(hslider("[4] Notch Freq Ratio: NotchFreq(n+1)/NotchFreq(n) [style:knob]", 1.5, 1.1, 4, 0.001));
525
526 level = lvl_group(hslider("Phaser Output Level [unit:dB]", 0, -60, 10, 0.1)) : component("music.lib").db2linear;
527
528 mdepth = select2(vibr,depth,2); // Improve "ease of use"
529 };
530
531 //------------------------- stereo_width(w) ---------------------------
532 // Stereo Width effect using the Blumlein Shuffler technique.
533 //
534 // USAGE: "_,_ : stereo_width(w) : _,_", where
535 // w = stereo width between 0 and 1
536 //
537 // At w=0, the output signal is mono ((left+right)/2 in both channels).
538 // At w=1, there is no effect (original stereo image).
539 // Thus, w between 0 and 1 varies stereo width from 0 to "original".
540 //
541 // REFERENCE:
542 // "Applications of Blumlein Shuffling to Stereo Microphone Techniques"
543 // Michael A. Gerzon, JAES vol. 42, no. 6, June 1994
544 //
545 stereo_width(w) = shuffle : *(mgain),*(sgain) : shuffle
546 with {
547 shuffle = _,_ <: +,-; // normally scaled by 1/sqrt(2) for orthonormality,
548 mgain = 1-w/2; // but we pick up the needed normalization here.
549 sgain = w/2;
550 };
551
552 //--------------------------- amp_follower ---------------------------
553 // Classic analog audio envelope follower with infinitely fast rise and
554 // exponential decay. The amplitude envelope instantaneously follows
555 // the absolute value going up, but then floats down exponentially.
556 //
557 // USAGE:
558 // _ : amp_follower(rel) : _
559 //
560 // where
561 // rel = release time = amplitude-envelope time-constant (sec) going down
562 //
563 // REFERENCES:
564 // Musical Engineer's Handbook, Bernie Hutchins, Ithaca NY, 1975
565 // Elecronotes Newsletter, Bernie Hutchins
566
567 amp_follower(rel) = abs : env with {
568 p = tau2pole(rel);
569 env(x) = x * (1.0 - p) : + ~ max(x,_) * p;
570 };
571
572 //--------------------------- amp_follower_ud ---------------------------
573 // Envelope follower with different up and down time-constants
574 //
575 // USAGE:
576 // _ : amp_follower_ud(att,rel) : _
577 //
578 // where
579 // att = attack time = amplitude-envelope time constant (sec) going up
580 // rel = release time = amplitude-envelope time constant (sec) going down
581 //
582 // For audio, att should be faster (smaller) than rel (e.g., 0.001 and 0.01)
583
584 amp_follower_ud(att,rel) = amp_follower(rel) : smooth(tau2pole(att));
585
586 //=============== Gates, Limiters, and Dynamic Range Compression ============
587
588 //----------------- gate_mono, gate_stereo -------------------
589 // Mono and stereo signal gates
590 //
591 // USAGE:
592 // _ : gate_mono(thresh,att,hold,rel) : _
593 // or
594 // _,_ : gate_stereo(thresh,att,hold,rel) : _,_
595 //
596 // where
597 // thresh = dB level threshold above which gate opens (e.g., -60 dB)
598 // att = attack time = time constant (sec) for gate to open (e.g., 0.0001 s = 0.1 ms)
599 // hold = hold time = time (sec) gate stays open after signal level < thresh (e.g., 0.1 s)
600 // rel = release time = time constant (sec) for gate to close (e.g., 0.020 s = 20 ms)
601 //
602 // REFERENCES:
603 // - http://en.wikipedia.org/wiki/Noise_gate
604 // - http://www.soundonsound.com/sos/apr01/articles/advanced.asp
605 // - http://en.wikipedia.org/wiki/Gating_(sound_engineering)
606
607 gate_mono(thresh,att,hold,rel,x) = x * gate_gain_mono(thresh,att,hold,rel,x);
608
609 gate_stereo(thresh,att,hold,rel,x,y) = ggm*x, ggm*y with {
610 ggm = gate_gain_mono(thresh,att,hold,rel,abs(x)+abs(y));
611 };
612
613 gate_gain_mono(thresh,att,hold,rel,x) = extendedrawgate : amp_follower_ud(att,rel) with {
614 extendedrawgate = max(rawgatesig,holdsig);
615 rawgatesig = inlevel(x) > db2linear(thresh);
616 inlevel(x) = amp_follower_ud(att/2,rel/2,x);
617 holdsig = ((max(holdreset & holdsamps,_) ~-(1)) > 0);
618 holdreset = rawgatesig > rawgatesig'; // reset hold when raw gate falls
619 holdsamps = int(hold*SR);
620 };
621
622 //-------------------- compressor_mono, compressor_stereo ----------------------
623 // Mono and stereo dynamic range compressor_s
624 //
625 // USAGE:
626 // _ : compressor_mono(ratio,thresh,att,rel) : _
627 // or
628 // _,_ : compressor_stereo(ratio,thresh,att,rel) : _,_
629 //
630 // where
631 // ratio = compression ratio (1 = no compression, >1 means compression)
632 // thresh = dB level threshold above which compression kicks in
633 // att = attack time = time constant (sec) when level & compression going up
634 // rel = release time = time constant (sec) coming out of compression
635 //
636 // REFERENCES:
637 // - http://en.wikipedia.org/wiki/Dynamic_range_compression
638 // - https://ccrma.stanford.edu/~jos/filters/Nonlinear_Filter_Example_Dynamic.html
639 // - Albert Graef's <faust2pd>/examples/synth/compressor_.dsp
640 //
641
642 compressor_mono(ratio,thresh,att,rel,x) = x * compression_gain_mono(ratio,thresh,att,rel,x);
643
644 compressor_stereo(ratio,thresh,att,rel,x,y) = cgm*x, cgm*y with {
645 cgm = compression_gain_mono(ratio,thresh,att,rel,abs(x)+abs(y));
646 };
647
648 compression_gain_mono(ratio,thresh,att,rel) =
649 amp_follower_ud(att,rel) : linear2db : outminusindb(ratio,thresh) :
650 kneesmooth(att) : db2linear
651 with {
652 // kneesmooth(att) installs a "knee" in the dynamic-range compression,
653 // where knee smoothness is set equal to half that of the compression-attack.
654 // A general 'knee' parameter could be used instead of tying it to att/2:
655 kneesmooth(att) = smooth(tau2pole(att/2.0));
656 // compression gain in dB:
657 outminusindb(ratio,thresh,level) = max(level-thresh,0) * (1/float(ratio)-1);
658 // Note: "float(ratio)" REQUIRED when ratio is an integer > 1!
659 };
660
661 //---------------------------- gate_demo -------------------------
662 // USAGE: _,_ : gate_demo : _,_;
663 //
664 gate_demo = bypass2(gbp,gate_stereo_demo) with {
665
666 gate_group(x) = vgroup("GATE [tooltip: Reference: http://en.wikipedia.org/wiki/Noise_gate]", x);
667 meter_group(x) = gate_group(hgroup("[0]", x));
668 knob_group(x) = gate_group(hgroup("[1]", x));
669
670 gbp = meter_group(checkbox("[0] Bypass [tooltip: When this is checked, the gate has no effect]"));
671
672 gateview = gate_gain_mono(gatethr,gateatt,gatehold,gaterel) : linear2db :
673 meter_group(hbargraph("[1] Gate Gain [unit:dB] [tooltip: Current gain of the gate in dB]",
674 -50,+10)); // [style:led]
675
676 gate_stereo_demo(x,y) = attach(x,gateview(abs(x)+abs(y))),y :
677 gate_stereo(gatethr,gateatt,gatehold,gaterel);
678
679 gatethr = knob_group(hslider("[1] Threshold [unit:dB] [style:knob] [tooltip: When the signal level falls below the Threshold (expressed in dB), the signal is muted]",
680 -30, -120, 0, 0.1));
681
682 gateatt = knob_group(hslider("[2] Attack [unit:us] [style:knob] [tooltip: Time constant in MICROseconds (1/e smoothing time) for the gate gain to go (exponentially) from 0 (muted) to 1 (unmuted)]",
683 10, 10, 10000, 1)) : *(0.000001) : max(1/SR);
684
685 gatehold = knob_group(hslider("[3] Hold [unit:ms] [style:knob] [tooltip: Time in ms to keep the gate open (no muting) after the signal level falls below the Threshold]",
686 200, 0, 1000, 1)) : *(0.001) : max(1/SR);
687
688 gaterel = knob_group(hslider("[4] Release [unit:ms] [style:knob] [tooltip: Time constant in ms (1/e smoothing time) for the gain to go (exponentially) from 1 (unmuted) to 0 (muted)]",
689 100, 0, 1000, 1)) : *(0.001) : max(1/SR);
690 };
691
692 //---------------------------- compressor_demo -------------------------
693 // USAGE: _,_ : compressor_demo : _,_;
694 //
695 compressor_demo = bypass2(cbp,compressor_stereo_demo) with {
696
697 comp_group(x) = vgroup("COMPRESSOR [tooltip: Reference: http://en.wikipedia.org/wiki/Dynamic_range_compression]", x);
698
699 meter_group(x) = comp_group(hgroup("[0]", x));
700 knob_group(x) = comp_group(hgroup("[1]", x));
701
702 cbp = meter_group(checkbox("[0] Bypass [tooltip: When this is checked, the compressor has no effect]"));
703
704 gainview =
705 compression_gain_mono(ratio,threshold,attack,release) : linear2db :
706 meter_group(hbargraph("[1] Compressor Gain [unit:dB] [tooltip: Current gain of the compressor in dB]",
707 -50,+10));
708
709 displaygain = _,_ <: _,_,(abs,abs:+) : _,_,gainview : _,attach;
710
711 compressor_stereo_demo =
712 displaygain(compressor_stereo(ratio,threshold,attack,release)) :
713 *(makeupgain), *(makeupgain);
714
715 ctl_group(x) = knob_group(hgroup("[3] Compression Control", x));
716
717 ratio = ctl_group(hslider("[0] Ratio [style:knob] [tooltip: A compression Ratio of N means that for each N dB increase in input signal level above Threshold, the output level goes up 1 dB]",
718 5, 1, 20, 0.1));
719
720 threshold = ctl_group(hslider("[1] Threshold [unit:dB] [style:knob] [tooltip: When the signal level exceeds the Threshold (in dB), its level is compressed according to the Ratio]",
721 -30, -100, 10, 0.1));
722
723 env_group(x) = knob_group(hgroup("[4] Compression Response", x));
724
725 attack = env_group(hslider("[1] Attack [unit:ms] [style:knob] [tooltip: Time constant in ms (1/e smoothing time) for the compression gain to approach (exponentially) a new lower target level (the compression `kicking in')]",
726 50, 0, 500, 0.1)) : *(0.001) : max(1/SR);
727
728 release = env_group(hslider("[2] Release [unit:ms] [style: knob] [tooltip: Time constant in ms (1/e smoothing time) for the compression gain to approach (exponentially) a new higher target level (the compression 'releasing')]",
729 500, 0, 1000, 0.1)) : *(0.001) : max(1/SR);
730
731 makeupgain = comp_group(hslider("[5] Makeup Gain [unit:dB] [tooltip: The compressed-signal output level is increased by this amount (in dB) to make up for the level lost due to compression]",
732 40, -96, 96, 0.1)) : db2linear;
733 };
734
735 //------------------------------- limiter_* ------------------------------------
736 // USAGE:
737 // _ : limiter_1176_R4_mono : _;
738 // _,_ : limiter_1176_R4_stereo : _,_;
739 //
740 // DESCRIPTION:
741 // A limiter guards against hard-clipping. It can be can be
742 // implemented as a compressor having a high threshold (near the
743 // clipping level), fast attack and release, and high ratio. Since
744 // the ratio is so high, some knee smoothing is
745 // desirable ("soft limiting"). This example is intended
746 // to get you started using compressor_* as a limiter, so all
747 // parameters are hardwired to nominal values here.
748 //
749 // REFERENCE: http://en.wikipedia.org/wiki/1176_Peak_Limiter
750 // Ratios: 4 (moderate compression), 8 (severe compression),
751 // 12 (mild limiting), or 20 to 1 (hard limiting)
752 // Att: 20-800 MICROseconds (Note: scaled by ratio in the 1176)
753 // Rel: 50-1100 ms (Note: scaled by ratio in the 1176)
754 // Mike Shipley likes 4:1 (Grammy-winning mixer for Queen, Tom Petty, etc.)
755 // Faster attack gives "more bite" (e.g. on vocals)
756 // He hears a bright, clear eq effect as well (not implemented here)
757 //
758 limiter_1176_R4_mono = compressor_mono(4,-6,0.0008,0.5);
759 limiter_1176_R4_stereo = compressor_stereo(4,-6,0.0008,0.5);
760
761 //========================== Schroeder Reverberators ======================
762
763 //------------------------------ jcrev,satrev ------------------------------
764 // USAGE:
765 // _ : jcrev : _,_,_,_
766 // _ : satrev : _,_
767 //
768 // DESCRIPTION:
769 // These artificial reverberators take a mono signal and output stereo
770 // (satrev) and quad (jcrev). They were implemented by John Chowning
771 // in the MUS10 computer-music language (descended from Music V by Max
772 // Mathews). They are Schroeder Reverberators, well tuned for their size.
773 // Nowadays, the more expensive freeverb is more commonly used (see the
774 // Faust examples directory).
775
776 // The reverb below was made from a listing of "RV", dated April 14, 1972,
777 // which was recovered from an old SAIL DART backup tape.
778 // John Chowning thinks this might be the one that became the
779 // well known and often copied JCREV:
780
781 jcrev = *(0.06) : allpass_chain <: comb_bank :> _ <: mix_mtx with {
782
783 rev1N = component("filter.lib").rev1;
784
785 rev12(len,g) = rev1N(2048,len,g);
786 rev14(len,g) = rev1N(4096,len,g);
787
788 allpass_chain =
789 rev2(512,347,0.7) :
790 rev2(128,113,0.7) :
791 rev2( 64, 37,0.7);
792
793 comb_bank =
794 rev12(1601,.802),
795 rev12(1867,.773),
796 rev14(2053,.753),
797 rev14(2251,.733);
798
799 mix_mtx = _,_,_,_ <: psum, -psum, asum, -asum : _,_,_,_ with {
800 psum = _,_,_,_ :> _;
801 asum = *(-1),_,*(-1),_ :> _;
802 };
803 };
804
805 // The reverb below was made from a listing of "SATREV", dated May 15, 1971,
806 // which was recovered from an old SAIL DART backup tape.
807 // John Chowning thinks this might be the one used on his
808 // often-heard brass canon sound examples, one of which can be found at
809 // https://ccrma.stanford.edu/~jos/wav/FM_BrassCanon2.wav
810
811 satrev = *(0.2) <: comb_bank :> allpass_chain <: _,*(-1) with {
812
813 rev1N = component("filter.lib").rev1;
814
815 rev11(len,g) = rev1N(1024,len,g);
816 rev12(len,g) = rev1N(2048,len,g);
817
818 comb_bank =
819 rev11( 778,.827),
820 rev11( 901,.805),
821 rev11(1011,.783),
822 rev12(1123,.764);
823
824 rev2N = component("filter.lib").rev2;
825
826 allpass_chain =
827 rev2N(128,125,0.7) :
828 rev2N( 64, 42,0.7) :
829 rev2N( 16, 12,0.7);
830 };
831
832 //-------------------------------- freeverb --------------------------------
833 // Freeverb is a widely used, free, open-source Schroeder reverb contributed
834 // by ``Jezar at Dreampoint.'' See <faust_distribution>/examples/freeverb.dsp
835
836 //=============== Feedback Delay Network (FDN) Reverberators ==============
837
838 //-------------------------------- fdnrev0 ---------------------------------
839 // Pure Feedback Delay Network Reverberator (generalized for easy scaling).
840 //
841 // USAGE:
842 // <1,2,4,...,N signals> <:
843 // fdnrev0(MAXDELAY,delays,BBSO,freqs,durs,loopgainmax,nonl) :>
844 // <1,2,4,...,N signals>
845 //
846 // WHERE
847 // N = 2, 4, 8, ... (power of 2)
848 // MAXDELAY = power of 2 at least as large as longest delay-line length
849 // delays = N delay lines, N a power of 2, lengths perferably coprime
850 // BBSO = odd positive integer = order of bandsplit desired at freqs
851 // freqs = NB-1 crossover frequencies separating desired frequency bands
852 // durs = NB decay times (t60) desired for the various bands
853 // loopgainmax = scalar gain between 0 and 1 used to "squelch" the reverb
854 // nonl = nonlinearity (0 to 0.999..., 0 being linear)
855 //
856 // REFERENCE:
857 // https://ccrma.stanford.edu/~jos/pasp/FDN_Reverberation.html
858 //
859 // DEPENDENCIES: filter.lib (filterbank)
860
861 fdnrev0(MAXDELAY, delays, BBSO, freqs, durs, loopgainmax, nonl)
862 = (bus(2*N) :> bus(N) : delaylines(N)) ~
863 (delayfilters(N,freqs,durs) : feedbackmatrix(N))
864 with {
865 N = count(delays);
866 NB = count(durs);
867 //assert(count(freqs)+1==NB);
868 delayval(i) = take(i+1,delays);
869 dlmax(i) = MAXDELAY; // must hardwire this from argument for now
870 //dlmax(i) = 2^max(1,nextpow2(delayval(i))) // try when slider min/max is known
871 // with { nextpow2(x) = ceil(log(x)/log(2.0)); };
872 // -1 is for feedback delay:
873 delaylines(N) = par(i,N,(delay(dlmax(i),(delayval(i)-1))));
874 delayfilters(N,freqs,durs) = par(i,N,filter(i,freqs,durs));
875 feedbackmatrix(N) = bhadamard(N);
876 vbutterfly(n) = bus(n) <: (bus(n):>bus(n/2)) , ((bus(n/2),(bus(n/2):par(i,n/2,*(-1)))) :> bus(n/2));
877 bhadamard(2) = bus(2) <: +,-;
878 bhadamard(n) = bus(n) <: (bus(n):>bus(n/2)) , ((bus(n/2),(bus(n/2):par(i,n/2,*(-1)))) :> bus(n/2))
879 : (bhadamard(n/2) , bhadamard(n/2));
880
881 // Experimental nonlinearities:
882 // nonlinallpass = apnl(nonl,-nonl);
883 // s = nonl*PI;
884 // nonlinallpass(x) = allpassnn(3,(s*x,s*x*x,s*x*x*x)); // filter.lib
885 nonlinallpass = _; // disabled by default (rather expensive)
886
887 filter(i,freqs,durs) = filterbank(BBSO,freqs) : par(j,NB,*(g(j,i)))
888 :> *(loopgainmax) / sqrt(N) : nonlinallpass
889 with {
890 dur(j) = take(j+1,durs);
891 n60(j) = dur(j)*SR; // decay time in samples
892 g(j,i) = exp(-3.0*log(10.0)*delayval(i)/n60(j));
893 // ~ 1.0 - 6.91*delayval(i)/(SR*dur(j)); // valid for large dur(j)
894 };
895 };
896
897 // ---------- prime_power_delays -----
898 // Prime Power Delay Line Lengths
899 //
900 // USAGE:
901 // bus(N) : prime_power_delays(N,pathmin,pathmax) : bus(N);
902 //
903 // WHERE
904 // N = positive integer up to 16
905 // (for higher powers of 2, extend 'primes' array below.)
906 // pathmin = minimum acoustic ray length in the reverberator (in meters)
907 // pathmax = maximum acoustic ray length (meters) - think "room size"
908 //
909 // DEPENDENCIES:
910 // math.lib (SR, selector, take)
911 // music.lib (db2linear)
912 //
913 // REFERENCE:
914 // https://ccrma.stanford.edu/~jos/pasp/Prime_Power_Delay_Line.html
915 //
916 prime_power_delays(N,pathmin,pathmax) = par(i,N,delayvals(i)) with {
917 Np = 16;
918 primes = 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53;
919 prime(n) = primes : selector(n,Np); // math.lib
920
921 // Prime Power Bounds [matlab: floor(log(maxdel)./log(primes(53)))]
922 maxdel=8192; // more than 63 meters at 44100 samples/sec & 343 m/s
923 ppbs = 13,8,5,4, 3,3,3,3, 2,2,2,2, 2,2,2,2; // 8192 is enough for all
924 ppb(i) = take(i+1,ppbs);
925
926 // Approximate desired delay-line lengths using powers of distinct primes:
927 c = 343; // soundspeed in m/s at 20 degrees C for dry air
928 dmin = SR*pathmin/c;
929 dmax = SR*pathmax/c;
930 dl(i) = dmin * (dmax/dmin)^(i/float(N-1)); // desired delay in samples
931 ppwr(i) = floor(0.5+log(dl(i))/log(prime(i))); // best prime power
932 delayvals(i) = prime(i)^ppwr(i); // each delay a power of a distinct prime
933 };
934
935 //--------------------- stereo_reverb_tester --------------------
936 // Handy test inputs for reverberator demos below.
937
938 stereo_reverb_tester(revin_group,x,y) = inx,iny with {
939 ck_group(x) = revin_group(vgroup("[1] Input Config",x));
940 mutegain = 1 - ck_group(checkbox("[1] Mute Ext Inputs
941 [tooltip: When this is checked, the stereo external audio inputs are disabled (good for hearing the impulse response or pink-noise response alone)]"));
942 pinkin = ck_group(checkbox("[2] Pink Noise
943 [tooltip: Pink Noise (or 1/f noise) is Constant-Q Noise (useful for adjusting the EQ sections)]"));
944
945 impulsify = _ <: _,mem : - : >(0);
946 imp_group(x) = revin_group(hgroup("[2] Impulse Selection",x));
947 pulseL = imp_group(button("[1] Left
948 [tooltip: Send impulse into LEFT channel]")) : impulsify;
949 pulseC = imp_group(button("[2] Center
950 [tooltip: Send impulse into LEFT and RIGHT channels]")) : impulsify;
951 pulseR = imp_group(button("[3] Right
952 [tooltip: Send impulse into RIGHT channel]")) : impulsify;
953
954 inx = x*mutegain + (pulseL+pulseC) + pn;
955 iny = y*mutegain + (pulseR+pulseC) + pn;
956 pn = 0.1*pinkin*component("oscillator.lib").pink_noise;
957 };
958
959 //------------------------- fdnrev0_demo ---------------------------
960 // USAGE: _,_ : fdnrev0_demo(N,NB,BBSO) : _,_
961 // WHERE
962 // N = Feedback Delay Network (FDN) order
963 // = number of delay lines used = order of feedback matrix
964 // = 2, 4, 8, or 16 [extend primes array below for 32, 64, ...]
965 // NB = number of frequency bands
966 // = number of (nearly) independent T60 controls
967 // = integer 3 or greater
968 // BBSO = Butterworth band-split order
969 // = order of lowpass/highpass bandsplit used at each crossover freq
970 // = odd positive integer
971
972 fdnrev0_demo(N,NB,BBSO,x,y) = stereo_reverb_tester(revin_group,x,y)
973 <: fdnrev0(MAXDELAY,delays,BBSO,freqs,durs,loopgainmax,nonl)
974 :> *(gain),*(gain)
975 with {
976 MAXDELAY = 8192; // sync w delays and prime_power_delays above
977 defdurs = (8.4,6.5,5.0,3.8,2.7); // NB default durations (sec)
978 deffreqs = (500,1000,2000,4000); // NB-1 default crossover frequencies (Hz)
979 deflens = (56.3,63.0); // 2 default min and max path lengths
980
981 fdn_group(x) = vgroup("FEEDBACK DELAY NETWORK (FDN) REVERBERATOR, ORDER 16
982 [tooltip: See Faust's effect.lib for documentation and references]", x);
983
984 freq_group(x) = fdn_group(vgroup("[1] Band Crossover Frequencies", x));
985 t60_group(x) = fdn_group(hgroup("[2] Band Decay Times (T60)", x));
986 path_group(x) = fdn_group(vgroup("[3] Room Dimensions", x));
987 revin_group(x) = fdn_group(hgroup("[4] Input Controls", x));
988 nonl_group(x) = revin_group(vgroup("[4] Nonnlinearity",x));
989 quench_group(x) = revin_group(vgroup("[3] Reverb State",x));
990
991 nonl = nonl_group(hslider("[style:knob] [tooltip: nonlinear mode coupling]",
992 0, -0.999, 0.999, 0.001));
993 loopgainmax = 1.0-0.5*quench_group(button("[1] Quench
994 [tooltip: Hold down 'Quench' to clear the reverberator]"));
995
996 pathmin = path_group(hslider("[1] min acoustic ray length [unit:m]
997 [tooltip: This length (in meters) determines the shortest delay-line used in the FDN reverberator.
998 Think of it as the shortest wall-to-wall separation in the room.]",
999 46, 0.1, 63, 0.1));
1000 pathmax = path_group(hslider("[2] max acoustic ray length [unit:m]
1001 [tooltip: This length (in meters) determines the longest delay-line used in the FDN reverberator.
1002 Think of it as the largest wall-to-wall separation in the room.]",
1003 63, 0.1, 63, 0.1));
1004
1005 durvals(i) = t60_group(vslider("[%i] %i [unit:s]
1006 [tooltip: T60 is the 60dB decay-time in seconds. For concert halls, an overall reverberation time (T60) near 1.9 seconds is typical [Beranek 2004]. Here we may set T60 independently in each frequency band. In real rooms, higher frequency bands generally decay faster due to absorption and scattering.]",
1007 take(i+1,defdurs), 0.1, 10, 0.1));
1008 durs = par(i,NB,durvals(NB-1-i));
1009
1010 freqvals(i) = freq_group(hslider("[%i] Band %i upper edge in Hz [unit:Hz]
1011 [tooltip: Each delay-line signal is split into frequency-bands for separate decay-time control in each band]",
1012 take(i+1,deffreqs), 100, 10000, 1));
1013 freqs = par(i,NB-1,freqvals(i));
1014
1015 delays = prime_power_delays(N,pathmin,pathmax);
1016
1017 gain = hslider("[3] Output Level (dB) [unit:dB]
1018 [tooltip: Output scale factor]", -40, -70, 20, 0.1) : db2linear;
1019 // (can cause infinite loop:) with { db2linear(x) = pow(10, x/20.0); };
1020 };
1021
1022 //------------------------------- zita_rev_fdn -------------------------------
1023 // Internal 8x8 late-reverberation FDN used in the FOSS Linux reverb zita-rev1
1024 // by Fons Adriaensen <fons@linuxaudio.org>. This is an FDN reverb with
1025 // allpass comb filters in each feedback delay in addition to the
1026 // damping filters.
1027 //
1028 // USAGE:
1029 // bus(8) : zita_rev_fdn(f1,f2,t60dc,t60m,fsmax) : bus(8)
1030 //
1031 // WHERE
1032 // f1 = crossover frequency (Hz) separating dc and midrange frequencies
1033 // f2 = frequency (Hz) above f1 where T60 = t60m/2 (see below)
1034 // t60dc = desired decay time (t60) at frequency 0 (sec)
1035 // t60m = desired decay time (t60) at midrange frequencies (sec)
1036 // fsmax = maximum sampling rate to be used (Hz)
1037 //
1038 // REFERENCES:
1039 // http://www.kokkinizita.net/linuxaudio/zita-rev1-doc/quickguide.html
1040 // https://ccrma.stanford.edu/~jos/pasp/Zita_Rev1.html
1041 //
1042 // DEPENDENCIES:
1043 // filter.lib (allpass_comb, lowpass, smooth)
1044 // math.lib (hadamard, take, etc.)
1045
1046 zita_rev_fdn(f1,f2,t60dc,t60m,fsmax) =
1047 ((bus(2*N) :> allpass_combs(N) : feedbackmatrix(N)) ~
1048 (delayfilters(N,freqs,durs) : fbdelaylines(N)))
1049 with {
1050 N = 8;
1051
1052 // Delay-line lengths in seconds:
1053 apdelays = (0.020346, 0.024421, 0.031604, 0.027333, 0.022904,
1054 0.029291, 0.013458, 0.019123); // feedforward delays in seconds
1055 tdelays = ( 0.153129, 0.210389, 0.127837, 0.256891, 0.174713,
1056 0.192303, 0.125000, 0.219991); // total delays in seconds
1057 tdelay(i) = floor(0.5 + SR*take(i+1,tdelays)); // samples
1058 apdelay(i) = floor(0.5 + SR*take(i+1,apdelays));
1059 fbdelay(i) = tdelay(i) - apdelay(i);
1060 // NOTE: Since SR is not bounded at compile time, we can't use it to
1061 // allocate delay lines; hence, the fsmax parameter:
1062 tdelaymaxfs(i) = floor(0.5 + fsmax*take(i+1,tdelays));
1063 apdelaymaxfs(i) = floor(0.5 + fsmax*take(i+1,apdelays));
1064 fbdelaymaxfs(i) = tdelaymaxfs(i) - apdelaymaxfs(i);
1065 nextpow2(x) = ceil(log(x)/log(2.0));
1066 maxapdelay(i) = int(2.0^max(1.0,nextpow2(apdelaymaxfs(i))));
1067 maxfbdelay(i) = int(2.0^max(1.0,nextpow2(fbdelaymaxfs(i))));
1068
1069 apcoeff(i) = select2(i&1,0.6,-0.6); // allpass comb-filter coefficient
1070 allpass_combs(N) =
1071 par(i,N,(allpass_comb(maxapdelay(i),apdelay(i),apcoeff(i)))); // filter.lib
1072 fbdelaylines(N) = par(i,N,(delay(maxfbdelay(i),(fbdelay(i)))));
1073 freqs = (f1,f2); durs = (t60dc,t60m);
1074 delayfilters(N,freqs,durs) = par(i,N,filter(i,freqs,durs));
1075 feedbackmatrix(N) = hadamard(N); // math.lib
1076
1077 staynormal = 10.0^(-20); // let signals decay well below LSB, but not to zero
1078
1079 special_lowpass(g,f) = smooth(p) with {
1080 // unity-dc-gain lowpass needs gain g at frequency f => quadratic formula:
1081 p = mbo2 - sqrt(max(0,mbo2*mbo2 - 1.0)); // other solution is unstable
1082 mbo2 = (1.0 - gs*c)/(1.0 - gs); // NOTE: must ensure |g|<1 (t60m finite)
1083 gs = g*g;
1084 c = cos(2.0*PI*f/float(SR));
1085 };
1086
1087 filter(i,freqs,durs) = lowshelf_lowpass(i)/sqrt(float(N))+staynormal
1088 with {
1089 lowshelf_lowpass(i) = gM*low_shelf1_l(g0/gM,f(1)):special_lowpass(gM,f(2));
1090 low_shelf1_l(G0,fx,x) = x + (G0-1)*lowpass(1,fx,x); // filter.lib
1091 g0 = g(0,i);
1092 gM = g(1,i);
1093 f(k) = take(k,freqs);
1094 dur(j) = take(j+1,durs);
1095 n60(j) = dur(j)*SR; // decay time in samples
1096 g(j,i) = exp(-3.0*log(10.0)*tdelay(i)/n60(j));
1097 };
1098 };
1099
1100 // Stereo input delay used by zita_rev1 in both stereo and ambisonics mode:
1101 zita_in_delay(rdel) = zita_delay_mono(rdel), zita_delay_mono(rdel) with {
1102 zita_delay_mono(rdel) = delay(8192,SR*rdel*0.001) * 0.3;
1103 };
1104
1105 // Stereo input mapping used by zita_rev1 in both stereo and ambisonics mode:
1106 zita_distrib2(N) = _,_ <: fanflip(N) with {
1107 fanflip(4) = _,_,*(-1),*(-1);
1108 fanflip(N) = fanflip(N/2),fanflip(N/2);
1109 };
1110
1111 //--------------------------- zita_rev_fdn_demo ------------------------------
1112 // zita_rev_fdn_demo = zita_rev_fdn (above) + basic GUI
1113 //
1114 // USAGE:
1115 // bus(8) : zita_rev_fdn_demo(f1,f2,t60dc,t60m,fsmax) : bus(8)
1116 //
1117 // WHERE
1118 // (args and references as for zita_rev_fdn above)
1119
1120 zita_rev_fdn_demo = zita_rev_fdn(f1,f2,t60dc,t60m,fsmax)
1121 with {
1122 fsmax = 48000.0;
1123 fdn_group(x) = hgroup(
1124 "Zita_Rev Internal FDN Reverb [tooltip: ~ Zita_Rev's internal 8x8 Feedback Delay Network (FDN) & Schroeder allpass-comb reverberator. See Faust's effect.lib for documentation and references]",x);
1125 t60dc = fdn_group(vslider("[1] Low RT60 [unit:s] [style:knob]
1126 [style:knob]
1127 [tooltip: T60 = time (in seconds) to decay 60dB in low-frequency band]",
1128 3, 1, 8, 0.1));
1129 f1 = fdn_group(vslider("[2] LF X [unit:Hz] [style:knob]
1130 [tooltip: Crossover frequency (Hz) separating low and middle frequencies]",
1131 200, 50, 1000, 1));
1132 t60m = fdn_group(vslider("[3] Mid RT60 [unit:s] [style:knob]
1133 [tooltip: T60 = time (in seconds) to decay 60dB in middle band]",
1134 2, 1, 8, 0.1));
1135 f2 = fdn_group(vslider("[4] HF Damping [unit:Hz] [style:knob]
1136 [tooltip: Frequency (Hz) at which the high-frequency T60 is half the middle-band's T60]",
1137 6000, 1500, 0.49*fsmax, 1));
1138 };
1139
1140 //---------------------------- zita_rev1_stereo ---------------------------
1141 // Extend zita_rev_fdn to include zita_rev1 input/output mapping in stereo mode.
1142 //
1143 // USAGE:
1144 // _,_ : zita_rev1_stereo(rdel,f1,f2,t60dc,t60m,fsmax) : _,_
1145 //
1146 // WHERE
1147 // rdel = delay (in ms) before reverberation begins (e.g., 0 to ~100 ms)
1148 // (remaining args and refs as for zita_rev_fdn above)
1149
1150 zita_rev1_stereo(rdel,f1,f2,t60dc,t60m,fsmax) =
1151 zita_in_delay(rdel)
1152 : zita_distrib2(N)
1153 : zita_rev_fdn(f1,f2,t60dc,t60m,fsmax)
1154 : output2(N)
1155 with {
1156 N = 8;
1157 output2(N) = outmix(N) : *(t1),*(t1);
1158 t1 = 0.37; // zita-rev1 linearly ramps from 0 to t1 over one buffer
1159 outmix(4) = !,butterfly(2),!; // probably the result of some experimenting!
1160 outmix(N) = outmix(N/2),par(i,N/2,!);
1161 };
1162
1163 //----------------------------- zita_rev1_ambi ---------------------------
1164 // Extend zita_rev_fdn to include zita_rev1 input/output mapping in
1165 // "ambisonics mode", as provided in the Linux C++ version.
1166 //
1167 // USAGE:
1168 // _,_ : zita_rev1_ambi(rgxyz,rdel,f1,f2,t60dc,t60m,fsmax) : _,_,_,_
1169 //
1170 // WHERE
1171 // rgxyz = relative gain of lanes 1,4,2 to lane 0 in output (e.g., -9 to 9)
1172 // (remaining args and references as for zita_rev1_stereo above)
1173
1174 zita_rev1_ambi(rgxyz,rdel,f1,f2,t60dc,t60m,fsmax) =
1175 zita_in_delay(rdel)
1176 : zita_distrib2(N)
1177 : zita_rev_fdn(f1,f2,t60dc,t60m,fsmax)
1178 : output4(N) // ambisonics mode
1179 with {
1180 N=8;
1181 output4(N) = select4 : *(t0),*(t1),*(t1),*(t1);
1182 select4 = _,_,_,!,_,!,!,! : _,_,cross with { cross(x,y) = y,x; };
1183 t0 = 1.0/sqrt(2.0);
1184 t1 = t0 * 10.0^(0.05 * rgxyz);
1185 };
1186
1187 //---------------------------------- zita_rev1 ------------------------------
1188 // Example GUI for zita_rev1_stereo (mostly following the Linux zita-rev1 GUI).
1189 //
1190 // Only the dry/wet and output level parameters are "dezippered" here. If
1191 // parameters are to be varied in real time, use "smooth(0.999)" or the like
1192 // in the same way.
1193 //
1194 // REFERENCE:
1195 // http://www.kokkinizita.net/linuxaudio/zita-rev1-doc/quickguide.html
1196 //
1197 // DEPENDENCIES:
1198 // filter.lib (peak_eq_rm)
1199
1200 zita_rev1(x,y) = zita_rev1_stereo(rdel,f1,f2,t60dc,t60m,fsmax,x,y)
1201 : out_eq : dry_wet(x,y) : out_level
1202 with {
1203
1204 fsmax = 48000.0; // highest sampling rate that will be used
1205
1206 fdn_group(x) = hgroup(
1207 "[0] Zita_Rev1 [tooltip: ~ ZITA REV1 FEEDBACK DELAY NETWORK (FDN) & SCHROEDER ALLPASS-COMB REVERBERATOR (8x8). See Faust's effect.lib for documentation and references]", x);
1208
1209 in_group(x) = fdn_group(hgroup("[1] Input", x));
1210
1211 rdel = in_group(vslider("[1] In Delay [unit:ms] [style:knob]
1212 [tooltip: Delay in ms before reverberation begins]",
1213 60,20,100,1));
1214
1215 freq_group(x) = fdn_group(hgroup("[2] Decay Times in Bands (see tooltips)", x));
1216
1217 f1 = freq_group(vslider("[1] LF X [unit:Hz] [style:knob]
1218 [tooltip: Crossover frequency (Hz) separating low and middle frequencies]",
1219 200, 50, 1000, 1));
1220
1221 t60dc = freq_group(vslider("[2] Low RT60 [unit:s] [style:knob]
1222 [style:knob] [tooltip: T60 = time (in seconds) to decay 60dB in low-frequency band]",
1223 3, 1, 8, 0.1));
1224
1225 t60m = freq_group(vslider("[3] Mid RT60 [unit:s] [style:knob]
1226 [tooltip: T60 = time (in seconds) to decay 60dB in middle band]",
1227 2, 1, 8, 0.1));
1228
1229 f2 = freq_group(vslider("[4] HF Damping [unit:Hz] [style:knob]
1230 [tooltip: Frequency (Hz) at which the high-frequency T60 is half the middle-band's T60]",
1231 6000, 1500, 0.49*fsmax, 1));
1232
1233 out_eq = pareq_stereo(eq1f,eq1l,eq1q) : pareq_stereo(eq2f,eq2l,eq2q);
1234 // Zolzer style peaking eq (not used in zita-rev1) (filter.lib):
1235 // pareq_stereo(eqf,eql,Q) = peak_eq(eql,eqf,eqf/Q), peak_eq(eql,eqf,eqf/Q);
1236 // Regalia-Mitra peaking eq with "Q" hard-wired near sqrt(g)/2 (filter.lib):
1237 pareq_stereo(eqf,eql,Q) = peak_eq_rm(eql,eqf,tpbt), peak_eq_rm(eql,eqf,tpbt)
1238 with {
1239 tpbt = wcT/sqrt(max(0,g)); // tan(PI*B/SR), B bw in Hz (Q^2 ~ g/4)
1240 wcT = 2*PI*eqf/SR; // peak frequency in rad/sample
1241 g = db2linear(eql); // peak gain
1242 };
1243
1244 eq1_group(x) = fdn_group(hgroup("[3] RM Peaking Equalizer 1", x));
1245
1246 eq1f = eq1_group(vslider("[1] Eq1 Freq [unit:Hz] [style:knob]
1247 [tooltip: Center-frequency of second-order Regalia-Mitra peaking equalizer section 1]",
1248 315, 40, 2500, 1));
1249
1250 eq1l = eq1_group(vslider("[2] Eq1 Level [unit:dB] [style:knob]
1251 [tooltip: Peak level in dB of second-order Regalia-Mitra peaking equalizer section 1]",
1252 0, -15, 15, 0.1));
1253
1254 eq1q = eq1_group(vslider("[3] Eq1 Q [style:knob]
1255 [tooltip: Q = centerFrequency/bandwidth of second-order peaking equalizer section 1]",
1256 3, 0.1, 10, 0.1));
1257
1258 eq2_group(x) = fdn_group(hgroup("[4] RM Peaking Equalizer 2", x));
1259
1260 eq2f = eq2_group(vslider("[1] Eq2 Freq [unit:Hz] [style:knob]
1261 [tooltip: Center-frequency of second-order Regalia-Mitra peaking equalizer section 2]",
1262 315, 40, 2500, 1));
1263
1264 eq2l = eq2_group(vslider("[2] Eq2 Level [unit:dB] [style:knob]
1265 [tooltip: Peak level in dB of second-order Regalia-Mitra peaking equalizer section 2]",
1266 0, -15, 15, 0.1));
1267
1268 eq2q = eq2_group(vslider("[3] Eq2 Q [style:knob]
1269 [tooltip: Q = centerFrequency/bandwidth of second-order peaking equalizer section 2]",
1270 3, 0.1, 10, 0.1));
1271
1272 out_group(x) = fdn_group(hgroup("[5] Output", x));
1273
1274 dry_wet(x,y) = *(wet) + dry*x, *(wet) + dry*y with {
1275 wet = 0.5*(drywet+1.0);
1276 dry = 1.0-wet;
1277 };
1278
1279 drywet = out_group(vslider("[1] Dry/Wet Mix [style:knob]
1280 [tooltip: -1 = dry, 1 = wet]",
1281 0, -1.0, 1.0, 0.01)) : smooth(0.999);
1282
1283 out_level = *(gain),*(gain);
1284
1285 gain = out_group(vslider("[2] Level [unit:dB] [style:knob]
1286 [tooltip: Output scale factor]", -20, -70, 40, 0.1))
1287 : smooth(0.999) : db2linear;
1288
1289 };
1290
1291 //---------------------------------- mesh_square ------------------------------
1292 // Square Rectangular Digital Waveguide Mesh
1293 //
1294 // USAGE:
1295 // bus(4*N) : mesh_square(N) : bus(4*N);
1296 //
1297 // WHERE
1298 // N = number of nodes along each edge - a power of two (1,2,4,8,...)
1299 //
1300 // EXAMPLE: Reflectively terminated mesh impulsed at one corner:
1301 // mesh_square_test(N,x) = mesh_square(N)~(busi(4*N,x)) // input to corner
1302 // with { busi(N,x) = bus(N) : par(i,N,*(-1)) : par(i,N-1,_), +(x); };
1303 // process = 1-1' : mesh_square_test(4); // all modes excited forever
1304 //
1305 // REQUIRES: math.lib.
1306 //
1307 // REFERENCE:
1308 // https://ccrma.stanford.edu/~jos/pasp/Digital_Waveguide_Mesh.html
1309
1310 // four-port scattering junction:
1311 mesh_square(1)
1312 = bus(4) <: par(i,4,*(-1)), (bus(4) :> (*(.5)) <: bus(4)) :> bus(4);
1313
1314 // rectangular NxN square waveguide mesh:
1315 mesh_square(N) = bus(4*N) : (route_inputs(N/2) : par(i,4,mesh_square(N/2)))
1316 ~(prune_feedback(N/2))
1317 : prune_outputs(N/2) : route_outputs(N/2) : bus(4*N)
1318 with {
1319 block(N) = par(i,N,!);
1320
1321 // select block i of N, block size = M:
1322 s(i,N,M) = par(j, M*N, Sv(i, j))
1323 with { Sv(i,i) = bus(N); Sv(i,j) = block(N); };
1324
1325 // prune mesh outputs down to the signals which make it out:
1326 prune_outputs(N)
1327 = bus(16*N) :
1328 block(N), bus(N), block(N), bus(N),
1329 block(N), bus(N), bus(N), block(N),
1330 bus(N), block(N), block(N), bus(N),
1331 bus(N), block(N), bus(N), block(N)
1332 : bus(8*N);
1333
1334 // collect mesh outputs into standard order (N,W,E,S):
1335 route_outputs(N)
1336 = bus(8*N)
1337 <: s(4,N,8),s(5,N,8), s(0,N,8),s(2,N,8),
1338 s(3,N,8),s(7,N,8), s(1,N,8),s(6,N,8)
1339 : bus(8*N);
1340
1341 // collect signals used as feedback:
1342 prune_feedback(N) = bus(16*N) :
1343 bus(N), block(N), bus(N), block(N),
1344 bus(N), block(N), block(N), bus(N),
1345 block(N), bus(N), bus(N), block(N),
1346 block(N), bus(N), block(N), bus(N) :
1347 bus(8*N);
1348
1349 // route mesh inputs (feedback, external inputs):
1350 route_inputs(N) = bus(8*N), bus(8*N)
1351 <:s(8,N,16),s(4,N,16), s(12,N,16),s(3,N,16),
1352 s(9,N,16),s(6,N,16), s(1,N,16),s(14,N,16),
1353 s(0,N,16),s(10,N,16), s(13,N,16),s(7,N,16),
1354 s(2,N,16),s(11,N,16), s(5,N,16),s(15,N,16)
1355 : bus(16*N);
1356 };