1import("stdfaust.lib");
2
3// These are now in a separate file ./effects.dsp
4// echo = echog(component("echo.dsp")); // ./echo.dsp
5// flanger = flg(component("flanger.dsp")); // ./flanger.dsp
6// chorus = chg(component("chorus.dsp")); // ./chorus.dsp
7// reverb = rg(component("freeverb.dsp"));
8
9process = main <: _,_; // Now separate: : echo : flanger : chorus : reverb;
10main = (signal + attach(extInput,amp) : filters : *(ampScaling)) ~ _;
11signal = oscs + noise * noiseOff * namp;
12
13ampScaling = envelopeAmp * masterVolume; // masterVolume is redundant but easier to find
14oscs = par(i,3,(oscamp(i+1)*osc(i+1))) :> _;
15
16controlSelect(1) = osc1(vrockerred); // ("[0] use as LFO"));
17octaveSelect(1) = osc1(vslider("[1] Octave1 [midi:ctrl 23] [style:knob]",1,0,5,1):int); // LO, 32', 16', 8', 4', 2'
18// Osc1 detunes like Osc2 and Osc3 (unlike in the Minimoog where it would be an expensive extra knob):
19detuneOctaves(1) = osc1(vslider("[2] DeTuning1 [units:Octaves] [midi:ctrl 24] [style:knob]",0.0,-1.0,1.0,0.001));
20waveSelect(1) = osc1(vslider("[3] Waveform1 [midi:ctrl 25] [style:knob]",5,0,5,1):int);
21amp1Enable = mr1(vslider("[1] On [midi:ctrl 12] [style:knob] [color:blue]",1,0,1,1));
22oscamp(1) = mr1(vslider("[0] Osc1 Amp [midi:ctrl 26] [style:knob]",0.5,0.0,1.0,0.001)) * amp1Enable;
23
24eei = mr2(vslider("[1] On [midi:ctrl 13] [style:knob] [color:blue]",0,0,1,1)); // External input = MAIN OUTPUT when "off"
25sei = mr2(vslider("[0] Ext Input [midi:ctrl 27] [style: knob]",0,0,1.0,0.001));
26extInput(fb,extSig) = fb,extSig : select2(eei) : *(sei) : extClipLED;
27extClipLED = _ <: _, (abs : >(0.95) : mr2(vbargraph("[2] Ext Input Clip [style:led]",0,1)):!);
28keycLED = attach(mr2(vbargraph("[3] Keyboard Ctl [style:led]",0,1)));
29
30controlSelect(2) = osc2(vrockerred); // ("[0] use as LFO"));
31octaveSelect(2) = osc2(vslider("[1] Octave2 [midi:ctrl 28] [style:knob]",1,0,5,1):int); // LO, 32', 16', 8', 4', 2'
32detuneOctaves(2) = osc2(vslider("[2] DeTuning2 [units:Octaves] [midi:ctrl 29] [style:knob]",0.41667,-1.0,1.0,0.001));
33waveSelect(2) = osc2(vslider("[3] Waveform2 [midi:ctrl 30] [style:knob]",5,0,5,1):int);
34amp2Enable = mr3(vslider("[1] On [midi:ctrl 14] [style:knob] [color:blue]",1,0,1,1));
35oscamp(2) = mr3(vslider("[0] Osc2 Amp [midi:ctrl 31] [style:knob]",0.5,0.0,1.0,0.001)) * amp2Enable;
36
37noise = select2(ntype,no.noise,10.0*no.pink_noise); // pink noise needs some "make-up gain"
38namp = mr4(vslider("[0] Noise Amp [midi:ctrl 32] [style: knob]",0.0,0.0,1.0,0.001));
39noiseOff = mr4cbg(vslider("[0] On [midi:ctrl 15] [style:knob] [color:blue]",0,0,1,1));
40ntype = mr4cbg(vslider("[1] White/Pink [midi:ctrl 16] [tooltip: Choose either White or Pink Noise] [style: knob] [color:blue]",1,0,1,1));
41
42controlSelect(3) = osc3(vrockerred); // ("[0] use as LFO"));
43octaveSelect(3) = osc3(vslider("[1] Octave3 [midi:ctrl 33] [style:knob]",0,0,5,1):int); // LO, 32', 16', 8', 4', 2'
44detuneOctaves(3) = osc3(vslider("[2] DeTuning3 [units:Octaves] [midi:ctrl 34] [style:knob]",0.3,-1.0,1.0,0.001));
45waveSelect(3) = osc3(vslider("[3] Waveform3 [midi:ctrl 35] [style:knob]",0,0,5,1):int);
46amp3Enable = mr5(vslider("[1] On [midi:ctrl 17] [style:knob] [color:blue]",0,0,1,1));
47oscamp(3) = mr5(vslider("[0] Osc3 Amp [midi:ctrl 36] [style:knob]",0.5,0.0,1.0,0.001)) * amp3Enable;
48
49waveforms(i) = (tri(i), bent(i), saw(i), sq(i), ptm(i), ptn(i));
50
51// compute oscillator frequency scale factor, staying in lg(Hz) as much as possible:
52modWheelShift = 1.5*modWheel; // Manual says 0 to 1.5 octaves
53modulationCenterShift = 0; // Leave this off until triangle-wave modulation is debugged
54modulationShift = select2(oscModEnable, 0.0,
55		          modWheelShift * ( modulationCenterShift + (1.0-modulationCenterShift) * oscNoiseModulation ));
56octaveShift(i) = -2+int(octaveSelect(i));
57osc3FixedFreq = 369.994; // F# a tritone above middle C
58keyFreqGlidedMaybe = select2(osc3Control,osc3FixedFreq,keyFreqGlided);
59keyFreqModulatedShifted(3) = keyFreqGlidedMaybe; // osc3 not allowed to FM itself
60keyFreqModulatedShifted(i) = keyFreqGlided * pow(2.0, modulationShift); // i=1,2
61
62// When disconnected from the keyboard, Osc3 can detune 3 octaves up or down (Pat video):
63detuneBoost(3) = select2(osc3Control,3.0,1.0);
64detuneBoost(i) = 1.0; // i=1,2
65detuneOctavesFinal(i) = detuneOctaves(i)*detuneBoost(i);
66
67fBase(i) = keyFreqModulatedShifted(i) * pow(2.0, (masterTuneOctaves+octaveShift(i)+detuneOctavesFinal(i)))
68	       : si.smooth(ba.tau2pole(0.016));
69fLFOBase(i) = 3.0 * pow(2.0, detuneOctavesFinal(i)); // used when osc3 (only) is in LFO mode
70lfoMode(i) = (octaveSelect(i) == 0);
71f(i) = select2(lfoMode(i), fBase(i), fLFOBase(i)); // lowest range setting is LFO mode for any osc
72
73// i is 1-based:
74osc(i) = ba.selectn(6, int(waveSelect(i)), tri(i), bent(i), saw(i), sq(i), ptm(i), ptn(i));
75tri(i) = select2(lfoMode(i),
76          os.triangle(f(i)),
77          os.lf_triangle(f(i)));
78bent(i) = 0.5*tri(i) + 0.5*saw(i); // from Minimoog manual
79saw(i) = select2(lfoMode(i),
80          os.sawtooth(f(i)),
81          os.lf_saw(f(i)));
82sq(i) = select2(lfoMode(i),
83          os.square(f(i)),
84          os.lf_squarewave(f(i)));
85ptm(i) = select2(lfoMode(i), // Note: a Duty knob would be better than these two, or in addition
86          os.pulsetrain(f(i),0.25),
87          lf_pulsetrain(f(i),0.25));
88ptn(i) = select2(lfoMode(i),
89          os.pulsetrain(f(i),0.125),
90          lf_pulsetrain(f(i),0.125));
91
92// Soon to appear in oscillators.lib:
93lf_pulsetrain(freq,duty) = 2.0*os.lf_pulsetrainpos(freq,duty) - 1.0;
94
95filters = ba.bypass1(bp,vcf); // BYPASS WILL GO AWAY (I think you just open it up all the way to bypass):
96bp = 0; // VCF is always on
97fcLgHz = vcf1(vslider("[1] Corner Freq [unit:Log2(Hz)]
98		  [tooltip: Corner resonance frequency in Log2(Hertz)]
99		  [style: knob]
100                  [midi:ctrl 74]", // Frequency Cutoff (aka Brightness )
101		  10.6, log(40.0)/log(2), log(20000.0)/log(2), 0.000001)) // 9 octaves (from Minimoog manual)
102		  //p: 40, 30, 80, 0.01))
103		  //p: : ba.pianokey2hz
104		  : si.smooth(ba.tau2pole(0.016));
105res = vcf1(vslider("[2] Corner Resonance [midi:ctrl 37] [tooltip: Resonance Q at VCF corner frequency (0 to 1)]
106		  [style: knob]",
107		  0.7, 0, 1, 0.01));
108
109vcfKeyRange = vcf1cbg(vslider("[2] Kbd Ctl [midi:ctrl 38] [tooltip: Keyboard tracking of VCF corner-frequency (0=none, 1=full)]
110		  [style: knob]",
111		  1, 0, 1, 0.001)); // was in mr2
112vcfModEnable = vcf1cbg(vslider("[1] Filter Mod. [midi:ctrl 19] [color:red] [style:knob] [tooltip: Filter Modulation => Route Modulation Mix output to VCF frequency]",1,0,1,1));
113// Note that VCF has three sources of corner-frequency setting that are added together:
114// - Corner Freq knob (40 Hz to 20 kHz)
115// - VCF Contour envelope (0 to 4 octaves)
116// - Injection 32 of Modulation Mix (0 to 1.5 octaves)
117// Manual says maximum vcf sweep spans 0 to 4 octaves:
118// Original Knob went to 10, but we're going to 4 so we can say the knob is in "octaves" units:
119vcfContourAmountOctaves = vcf1(vslider("[3] Amount of Contour (octaves) [midi:ctrl 39] [style: knob]", 1.2, 0, 4.0, 0.001));
120vcfContourOctaves = vcfContourAmountOctaves * envelopeVCF; // in octaves
121// We are assuming that the modulation-mix range for the VCF freq is 1.5 octaves like it is for oscs 1 and 2:
122vcfModMixModulationOctaves = select2(vcfModEnable, 0, (1.5 * oscNoiseModulation * modWheel)); // octaves
123vcfModulationOctaves = vcfModMixModulationOctaves + vcfContourOctaves;
124keyFreqLogHzGlided = log(keyFreqGlided)/log(2.0); // FIXME: Start w freqLogHz not freq so we don't need exp(log()) here
125keyShiftOctaves = keyFreqLogHzGlided - log(261.625565)/log(2.0); // FIXME: ARBITRARILY centering on middle C - check device
126vcfKeyShiftOctaves = vcfKeyRange * keyShiftOctaves;
127modulatedFcLgHz = fcLgHz + vcfModulationOctaves + vcfKeyShiftOctaves;
128
129fc = min((0.5*ma.SR), pow(2.0,modulatedFcLgHz));
130vcf = ve.moog_vcf_2bn(res,fc);
131
132// Attack, Decay, and Sustain ranges are set according to the Minimoog manual:
133attT60VCF = 0.001 * vcf2(vslider("[0] AttackF [midi:ctrl 40] [tooltip: Attack Time] [unit:ms] [style: knob]",1400,10,10000,1));
134decT60VCF = 0.001 * vcf2(vslider("[0] DecayF [midi:ctrl 41] [tooltip: Decay-to-Sustain Time] [unit:ms] [style: knob]",10,10,10000,1));
135susLvlVCF = 0.01 * vcf2(vslider("[0] SustainF [midi:ctrl 42] [tooltip: Sustain level as percent of max] [style: knob]",80,0,100,0.1));
136decayButton = wg(vslider("Decay [midi:ctrl 20] [tooltip:Envelope Release either Decay value or 0][style:knob]",1,0,1,1):int); // was Staccato
137legatoButton = wg(vslider("Glide [midi:ctrl 65] [tooltip: Glide from note to note][style:knob]",1,0,1,1)); // was Legato
138relT60VCF = select2(decayButton,0.010,decT60VCF);
139envelopeVCF = en.adsre(attT60VCF,decT60VCF,susLvlVCF,relT60VCF,gate);
140
141// --- Smart Keyboard interface ---
142
143declare interface "SmartKeyboard{
144    'Number of Keyboards':'2',
145    'Keyboard 0 - Number of Keys':'13',
146    'Keyboard 1 - Number of Keys':'13',
147    'Keyboard 0 - Lowest Key':'72',
148    'Keyboard 1 - Lowest Key':'60'
149}";
150
151// --- functions ---
152
153// Signal controls:
154
155keyDownHold = gg(vslider("[0] gateHold [tooltip: lock sustain pedal on (hold gate set at 1)][style:knob]",0,0,1,1));
156
157keyDown = gg(button("[1] gate [tooltip: The gate signal is 1 during a
158	note and 0 otherwise. For MIDI, NoteOn occurs when the gate
159	transitions from 0 to 1, and NoteOff is an event corresponding
160	to the gate transition from 1 to 0. The name of this Faust
161	button must be 'gate'.]"));
162
163sustain = gg(button("[1] sustain [midi:ctrl 64]
164	[tooltip: extends the gate (keeps it set to 1)]")); // MIDI only (see smartkeyb doc)
165
166gate = keyDown + keyDownHold + sustain : min(1);
167
168attT60 = 0.001 * ng(vslider("[0] AttackA [midi:ctrl 43] [tooltip: Attack Time] [unit:ms] [style: knob]",2,0,5000,0.1));
169decT60 = 0.001 * ng(vslider("[0] DecayA [midi:ctrl 44] [tooltip: Decay-to-Sustain Time] [unit:ms] [style: knob]",10,0,10000,0.1));
170susLvl = 0.01 * ng(vslider("[0] SustainA [midi:ctrl 45] [tooltip: Sustain level as percent of max] [style: knob]",80,0,100,0.1));
171relT60 = select2(decayButton,0.010,decT60); // right?
172envelopeAmpNoAM = en.adsre(attT60,decT60,susLvl,relT60,gate);
173AMDepth = 0.5;
174envelopeAmp = select2(oscModEnable, envelopeAmpNoAM,
175	    envelopeAmpNoAM * (1.0 + AMDepth*modWheel * 0.5 * (1.0+oscNoiseModulation)));
176
177// Signal Parameters
178ampL = volg(vslider("[1] gain [style:knob] [tooltip: Amplitude]",0.2,0,1.0,0.001));
179amp = ampL : si.smoo; // envelopeAmp is multiplied once on entire signal sum
180//elecGuitar.dsp values used:
181bend = wg(ba.semi2ratio(hslider("[0] bend [style:knob] [midi:pitchwheel]",0,-2,2,0.01))) : si.polySmooth(gate,0.999,1);
182//Previous guess:
183modWheel = wg(vslider("[1] mod [midi:ctrl 1] [style:knob] [tooltip: PitchModulation amplitude in octaves]",
184	       0,0,1.0,0.01)) : si.polySmooth(gate,0.999,1);
185//p: MIDI requires frequency in Hz, not piano-keys as we had before
186// Frequency Range is 0.1 Hz to 20 kHz according to the Minimoog manual:
187// MIDI REQUIRES THE FOLLOWING PARAMETER TO BE NAMED 'freq':
188keyFreqBent = bend * kg(hslider("[2] freq [unit:Hz] [style:knob]",220,0.1,20000,0.1));
189masterVolume = vg(vslider("MasterVolume [style:knob] [midi:ctrl 7] [tooltip: master volume, MIDI controlled]",
190	     0.7,0,1,0.001))
191	       : si.smooth(ba.tau2pole(0.16));
192masterTuneOctaves = dg(vslider("[0] Tune [midi:ctrl 47] [unit:Octaves] [style:knob]
193    [tooltip: Frequency-shift up or down for all oscillators in Octaves]", 0.0,-1.0,1.0,0.001));
194    // Oscillator Modulation HrockerRed => apply Modulation Mix output osc1&2 pitches
195glide = gmmg(vslider("[0] Glide [midi:ctrl 5] [unit:sec/octave] [style:knob] [scale:log]
196    [tooltip: Portamento (frequency-glide) in seconds per octave]",
197		    0.008,0.001,1.0,0.001));
198legatoPole = select2(legatoButton,0.5,ba.tau2pole(glide*exp(1.0f)/2.0f)); // convert 1/e to 1/2 by slowing down exp
199keyFreqGlided = keyFreqBent : si.smooth(legatoPole);
200
201mmix = gmmg(vslider("[1] Mod. Mix [midi:ctrl 48] [style:knob] [tooltip: Modulation Mix: Osc3 (0) to Noise (1)]",
202            0.0,0.0,1.0,0.001));
203oscNoiseModulation = (mmix * noise) + ((1.0-mmix) * osc(3)); // noise amplitude and off-switch ignored here
204oscModEnable = dsg(vslider("[0] Osc. Mod. [midi:ctrl 22] [color:red] [style:knob] [tooltip:Oscillator Modulation adds Modulation Mix output to osc1&2 frequencies",1,0,1,1)); // any offset?
205osc3Control = dsg(vslider("[1] Osc. 3 Ctl [midi:ctrl 9] [color:red] [style:knob] [tooltip:Oscillator 3 frequency tracks the keyboard if on, else not",0,0,1,1):int);
206
207effect = _,_ : +
208: component_echo
209: component_flanger
210: component_chorus
211: component_freeverb;
212
213component_echo = environment {
214
215echo_group(x) = x; // Let layout2.dsp lay us out
216knobs_group(x) = ekg(x);
217switches_group(x)  = esg(x);
218
219dmax = 32768; // one and done
220dmaxs = float(dmax)/44100.0;
221
222Nnines = 1.8; // Increase until you get the desired maximum amount of smoothing when fbs==1
223//fastpow2 = ffunction(float fastpow2(float), "fast_pow2.h", "");
224fbspr(fbs) = 1.0 - pow(2.0, -3.33219*Nnines*fbs); // pole radius of feedback smoother
225inputSelect(gi) = _,0 : select2(gi);
226echo_mono(dmax,curdel,tapdel,fb,fbspr,gi) = inputSelect(gi) : (+:si.smooth(fbspr)
227<: de.fdelay(dmax,curdel),
228de.fdelay(dmax,tapdel))
229~(*(fb),!) : !,_;
230
231tau2pole(tau) = ba.if(tau>0, exp(-1.0/(tau*ma.SR)), 0.0);
232t60smoother(dEchoT60) = si.smooth(tau2pole(dEchoT60/6.91));
233
234dEchoT60 = knobs_group(vslider("[1] DelayT60 [midi:ctrl 60] [style:knob]", 0.5, 0, 100, 0.001));
235dEchoSamplesRaw = knobs_group(vslider("[0] Delay [midi:ctrl 61] [style:knob]", 0.5, 0.001, (dmaxs-0.001), 0.001)) * ma.SR;
236dEchoSamples = dEchoSamplesRaw : t60smoother(dEchoT60);
237warpRaw = knobs_group(vslider("[0] Warp [midi:ctrl 62] [style:knob]", 0, -1.0, 1.0, 0.001));
238
239scrubAmpRaw = 0;
240
241scrubPhaseRaw = 0;
242fb = knobs_group(vslider("[2] Feedback [midi:ctrl 2] [style:knob]", .3, 0.0, 1.0, 0.0001));
243amp = knobs_group(vslider("[3] Amp [midi:ctrl 75] [style:knob]", .5, 0, 1, 0.001)) : si.smooth(ba.tau2pole(ampT60/6.91));
244
245ampT60 = 0.15661;
246fbs = knobs_group(vslider("[5] [midi:ctrl 76] FeedbackSm [style:knob]", 0, 0, 1, 0.00001));
247gi = switches_group(1-vslider("[7] [midi:ctrl 105] EnableEcho[style:knob]",0,0,1,1)); // "ground input" switches input to zeros
248
249// Warp and Scrubber stuff:
250enableEcho = (scrubAmpRaw > 0.00001);
251triggerScrubOn = (enableEcho - enableEcho') > 0;  // enableEcho went 0 to 1
252triggerScrubOff = (enableEcho - enableEcho') < 0; // enableEcho went 1 to 0
253// Ramps up only during scrub "hold" time and is otherwise zero:
254counter = (enableEcho * (triggerScrubOn : + ~ +(1) * enableEcho : -(2))) & (dmax-1);
255// implementation that continues scrubbing where it left off:
256
257scrubPhase = scrubPhaseRaw : t60smoother(dEchoT60*(1-triggerScrubOff));
258scrubAmp = scrubAmpRaw : t60smoother(dEchoT60*(1-triggerScrubOff));
259warp = warpRaw : t60smoother(dEchoT60);
260dTapSamplesRaw = dEchoSamplesRaw * (1.0 + warp + scrubPhase * scrubAmp) + float(counter);
261dTapSamples = dTapSamplesRaw : t60smoother(dEchoT60*(1-triggerScrubOff));
262
263echo_process = _ <: _, amp * echo_mono(dmax,dEchoSamples,dTapSamples,fb,fbspr(fbs),gi) : +;
264
265}.echo_process;
266
267component_flanger = environment {
268
269// Created from flange.dsp 2015/06/21
270
271flanger_mono(dmax,curdel,depth,fb,invert,lfoshape) = _ <: _, (-:de.fdelay(dmax,curdel)) ~ *(fb) : _,*(select2(invert,depth,0-depth)) : + : *(1/(1+depth));  // ideal for dc and reinforced sinusoids (in-phase summed signals)
272
273flanger_process = ba.bypass1(fbp,flanger_mono_gui);
274
275// Kill the groups to save vertical space:
276meter_group(x) = flsg(x);
277ctl_group(x)  = flkg(x);
278del_group(x)  = flkg(x);
279lvl_group(x)  = flkf(x);
280
281flangeview = lfo(freq);
282
283flanger_mono_gui = attach(flangeview) : flanger_mono(dmax,curdel,depth,fb,invert,lfoshape);
284
285sinlfo(freq) = (1 + os.oscrs(freq))/2;
286trilfo(freq) = 1.0-abs(os.saw1(freq));
287lfo(f) = (lfoshape * trilfo(f)) + ((1-lfoshape) * sinlfo(f));
288
289dmax = 2048;
290odflange = 44; // ~1 ms at 44.1 kHz = min delay
291dflange  = ((dmax-1)-odflange)*del_group(vslider("[1] Delay [midi:ctrl 50][style:knob]", 0.22, 0, 1, 1));
292freq     = ctl_group(vslider("[1] Rate [midi:ctrl 51] [unit:Hz] [style:knob]", 0.5, 0, 10, 0.01)) : si.smooth(ba.tau2pole(freqT60/6.91));
293
294freqT60  = 0.15661;
295depth    = ctl_group(vslider("[3] Depth [midi:ctrl 52] [style:knob]", .75, 0, 1, 0.001)) : si.smooth(ba.tau2pole(depthT60/6.91));
296
297depthT60 = 0.15661;
298fb       = ctl_group(vslider("[5] Feedback [midi:ctrl 53] [style:knob]", 0, -0.995, 0.99, 0.001)) : si.smooth(ba.tau2pole(fbT60/6.91));
299
300fbT60    = 0.15661;
301lfoshape = ctl_group(vslider("[7] Waveshape [midi:ctrl 54] [style:knob]", 0, 0, 1, 0.001));
302curdel   = odflange+dflange*lfo(freq);
303
304fbp = 1-int(flsg(vslider("[0] Enable [midi:ctrl 102][style:knob]",0,0,1,1)));
305
306invert = flsg(vslider("[1] Invert [midi:ctrl 49][style:knob]",0,0,1,1):int);
307
308}.flanger_process;
309
310component_chorus = environment {
311
312voices = 8; // MUST BE EVEN
313chorus_process = ba.bypass1to2(cbp,chorus_mono(dmax,curdel,rate,sigma,do2,voices));
314
315dmax = 8192;
316curdel = dmax * ckg(vslider("[0] Delay [midi:ctrl 55] [style:knob]", 0.5, 0, 1, 1)) : si.smooth(0.999);
317rateMax = 7.0; // Hz
318rateMin = 0.01;
319rateT60 = 0.15661;
320rate = ckg(vslider("[1] Rate [midi:ctrl 56] [unit:Hz] [style:knob]", 0.5, rateMin, rateMax, 0.0001))
321       : si.smooth(ba.tau2pole(rateT60/6.91));
322
323depth = ckg(vslider("[4] Depth [midi:ctrl 57] [style:knob]", 0.5, 0, 1, 0.001)) : si.smooth(ba.tau2pole(depthT60/6.91));
324
325depthT60 = 0.15661;
326delayPerVoice = 0.5*curdel/voices;
327sigma = delayPerVoice * ckg(vslider("[6] Deviation [midi:ctrl 58] [style:knob]",0.5,0,1,0.001)) : si.smooth(0.999);
328
329periodic = 1;
330
331do2 = depth;   // use when depth=1 means "multivibrato" effect (no original => all are modulated)
332cbp = 1-int(csg(vslider("[0] Enable [midi:ctrl 103][style:knob]",0,0,1,1)));
333
334chorus_mono(dmax,curdel,rate,sigma,do2,voices)
335    = _ <: (*(1-do2)<:_,_),(*(do2) <: par(i,voices,voice(i)) :> _,_) : ro.interleave(2,2) : +,+
336    with {
337        angle(i) = 2*ma.PI*(i/2)/voices + (i%2)*ma.PI/2;
338        voice(i) = de.fdelay(dmax,min(dmax,del(i))) * cos(angle(i));
339        del(i) = curdel*(i+1)/voices + dev(i);
340        rates(i) = rate/float(i+1);
341        dev(i) = sigma * os.oscp(rates(i),i*2*ma.PI/voices);
342    };
343
344}.chorus_process;
345
346component_freeverb = environment {
347
348import("stdfaust.lib");
349
350declare name        "freeverb";
351declare version     "1.0";
352declare author      "Grame";
353declare license     "BSD";
354declare copyright   "(c) GRAME 2006 and MoForte Inc. 2017";
355declare reference   "https://ccrma.stanford.edu/~jos/pasp/Freeverb.html";
356
357//======================================================
358//
359//                      Freeverb
360//        Faster version using fixed delays (20% gain)
361//
362//======================================================
363
364// Constant Parameters
365//--------------------
366
367fixedgain   = 0.015; //value of the gain of fxctrl
368scalewet    = 3.0;
369scaledry    = 2.0;
370scaledamp   = 0.4;
371scaleroom   = 0.28;
372offsetroom  = 0.7;
373initialroom = 0.5;
374initialdamp = 0.5;
375initialwet  = 1.0/scalewet;
376initialdry  = 0;
377initialwidth= 1.0;
378initialmode = 0.0;
379freezemode  = 0.5;
380stereospread= 23;
381allpassfeed = 0.5; //feedback of the delays used in allpass filters
382
383// Filter Parameters
384//------------------
385
386combtuningL1    = 1116;
387combtuningL2    = 1188;
388combtuningL3    = 1277;
389combtuningL4    = 1356;
390combtuningL5    = 1422;
391combtuningL6    = 1491;
392combtuningL7    = 1557;
393combtuningL8    = 1617;
394
395allpasstuningL1 = 556;
396allpasstuningL2 = 441;
397allpasstuningL3 = 341;
398allpasstuningL4 = 225;
399
400// Control Sliders
401//--------------------
402// Damp : filters the high frequencies of the echoes (especially active for great values of RoomSize)
403// RoomSize : size of the reverberation room
404// Dry : original signal
405// Wet : reverberated signal
406
407dampSlider      = rkg(vslider("Damp [midi:ctrl 3] [style:knob]",0.5, 0, 1, 0.025))*scaledamp;
408roomsizeSlider  = rkg(vslider("RoomSize [midi:ctrl 4] [style:knob]", 0.5, 0, 1, 0.025))*scaleroom + offsetroom;
409wetSlider       = rkg(vslider("Wet [midi:ctrl 79] [style:knob]", 0.3333, 0, 1, 0.025));
410combfeed        = roomsizeSlider;
411
412// Comb and Allpass filters
413//-------------------------
414
415allpass(dt,fb) = (_,_ <: (*(fb),_:+:@(dt)), -) ~ _ : (!,_);
416
417comb(dt, fb, damp) = (+:@(dt)) ~ (*(1-damp) : (+ ~ *(damp)) : *(fb));
418
419// Reverb components
420//------------------
421
422monoReverb(fb1, fb2, damp, spread)
423= _ <:  comb(combtuningL1+spread, fb1, damp),
424comb(combtuningL2+spread, fb1, damp),
425comb(combtuningL3+spread, fb1, damp),
426comb(combtuningL4+spread, fb1, damp),
427comb(combtuningL5+spread, fb1, damp),
428comb(combtuningL6+spread, fb1, damp),
429comb(combtuningL7+spread, fb1, damp),
430comb(combtuningL8+spread, fb1, damp)
431+>
432allpass (allpasstuningL1+spread, fb2)
433:   allpass (allpasstuningL2+spread, fb2)
434:   allpass (allpasstuningL3+spread, fb2)
435:   allpass (allpasstuningL4+spread, fb2)
436;
437
438monoReverbToStereo(fb1, fb2, damp, spread)
439= + <: monoReverb(fb1, fb2, damp, 0) <: _,_;
440stereoReverb(fb1, fb2, damp, spread)
441= + <:  monoReverb(fb1, fb2, damp, 0), monoReverb(fb1, fb2, damp, spread);
442monoToStereoReverb(fb1, fb2, damp, spread)
443= _ <: monoReverb(fb1, fb2, damp, 0), monoReverb(fb1, fb2, damp, spread);
444
445// fxctrl : add an input gain and a wet-dry control to a stereo FX
446//----------------------------------------------------------------
447
448fxctrl(g,w,Fx) =  _,_ <: (*(g),*(g) : Fx : *(w),*(w)), *(1-w), *(1-w) +> _,_;
449
450rbp = 1-int(rsg(vslider("[0] Enable [midi:ctrl 104][style:knob]",0,0,1,1)));
451
452// Freeverb
453//---------
454
455//JOS:freeverb = fxctrl(fixedgain, wetSlider, stereoReverb(combfeed, allpassfeed, dampSlider, stereospread));
456freeverb = fxctrl(fixedgain, wetSlider, monoReverbToStereo(combfeed, allpassfeed, dampSlider, stereospread));
457
458freeverb_process = ba.bypass2(rbp,freeverb);
459
460}.freeverb_process;
461
462
463// This layout loosely follows the MiniMoog-V
464// Arturia-only features are labeled
465// Original versions also added where different
466
467// Need vrocker and hrocker toggle switches in Faust!
468// Need orange and blue color choices
469//   Orange => Connect modulation sources to their destinations
470//    Blue  => Turn audio sources On and Off
471// - and later -
472//   White  => Turn performance features On and Off
473//   Black  => Select between modulation sources
474//   Julius Smith for Analog Devices 3/1/2017
475
476vrocker(x) = checkbox("%%x [style:vrocker]");
477hrocker(x) = checkbox("%%x [style:hrocker]");
478vrockerblue(x) = checkbox("%x  [style:vrocker] [color:blue]");
479vrockerblue(x) = checkbox("%x [style:vrocker] [color:blue]");
480 // USAGE: vrockerorange("[0] ModulationEnable");
481
482hrockerblue(x) = checkbox("%%x [style:hrocker] [color:blue]");
483vrockerred(x) = checkbox("%%x [style:vrocker] [color:red]");
484hrockerred(x) = checkbox("%%x [style:hrocker] [color:red]");
485
486declare designer "Robert A. Moog";
487
488mmg(x) = hgroup("",x); // Minimoog + Effects
489  synthg(x) = mmg(vgroup("[0] Minimoog",x));
490  fxg(x) = mmg(hgroup("[1] Effects",x));
491  mg(x) = synthg(hgroup("[0]",x));
492    cg(x) = mg(vgroup("[0] Controllers",x)); // Formerly named "Modules" but "Minimoog" group-title is enough
493      vg(x) = cg(hgroup("[0] Master Volume", x));
494      dg(x) = cg(hgroup("[1] Oscillator Tuning & Switching", x));
495        // Tune knob = master tune
496        dsg(x) = dg(vgroup("[1] Switches", x));
497	  // Oscillator Modulation HrockerRed => apply Modulation Mix output to osc1&2 pitches
498	  // [MOVED here from osc3 group] Osc 3 Control VrockerRed => use osc3 as LFO instead of osc3
499      gmmg(x) = cg(hgroup("[2] Glide and ModMix", x));
500        // Glide knob [0:10] = portamento speed
501        // Modulation Mix knob [0:10] (between Osc3 and Noise) = mix of noise and osc3 modulating osc1&2 pitch and/or VCF freq
502    og(x) = mg(vgroup("[1] Oscillator Bank", x));
503      osc1(x) = og(hgroup("[1] Oscillator 1", x));
504        // UNUSED Control switch (for alignment) - Could put Oscillator Modulation switch there
505        // Range rotary switch: LO (slow pulses or rhythm), 32', 16', 8', 4', 2'
506        // Frequency <something> switch: LED to right
507        // Waveform rotary switch: tri, impulse/bent-triangle, saw, pulseWide, pulseMed, pulseNarrow
508      osc2(x) = og(hgroup("[2] Oscillator 2", x));
509        // UNUSED (originall) or Osc 2 Control VrockerRed
510        // Range rotary switch: LO, 32', 16', 8', 4', 2'
511        // Detuning knob: -7 to 7 [NO SWITCH]
512        // Waveform rotary switch: tri, impulse(?), saw, pulseWide, pulseMed, pulseNarrow
513      osc3(x) = og(hgroup("[3] Oscillator 3", x));
514        // Osc 3 Control VrockerRed => use osc3 as LFO instead of osc3
515        // Range rotary switch: LO, 32', 16', 8', 4', 2'
516        // Detuning knob: -7 to 7 [NO SWITCH]
517        // Waveform rotary switch: tri, impulse(?), saw, pulseWide, pulseMed, pulseNarrow
518    mixg(x) = mg(vgroup("[2] Mixer", x));
519      // Each row 5 slots to maintain alignment and include red rockers joining VCF area:
520      mr1(x) = mixg(hgroup("[0] Osc1", x)); // mixer row 1 =
521      // Osc1 Volume and Osc1 HrockerBlue & _ & _ & Filter Modulation HrockerRed
522      // Filter Modulation => Modulation Mix output to VCF freq
523      mr2(x) = mixg(hgroup("[1] Ext In, KeyCtl", x)); // row 2 = Ext In HrockerBlue and Vol and Overload LED and Keyboard Ctl HrockerRed 1
524      mr3(x) = mixg(hgroup("[2] Osc2", x)); // = Osc2 Volume and Osc2 HrockerBlue and Keyboard Ctl HrockerRed 2
525      // Keyboard Control Modulation 1&2 => 0, 1/3, 2/3, all of Keyboard Control Signal ("gate?") applied to VCF freq
526      mr4(x) = mixg(hgroup("[3] Noise", x)); // = Noise HrockerBlue and Volume and Noise Type VrockerBlue
527        mr4cbg(x) = mr4(vgroup("[1]", x)); // = Noise Off and White/Pink selection
528	// two rockers
529      mr5(x) = mixg(hgroup("[4] Osc3", x)); //  Osc3 Volume and Osc3 HrockerBlue
530    modg(x) = mg(vgroup("[3] Modifiers", x));
531      vcfg(x) = modg(vgroup("[0] Filter", x));
532        vcf1(x) = vcfg(hgroup("[0] [tooltip:freq, Q, ContourScale]", x));
533	  vcf1cbg(x) = vcf1(vgroup("[0] [tooltip:two checkboxes]", x));
534          // Filter Modulation switch
535          // VCF Off switch
536        // Corner Frequency knob
537        // Filter Emphasis knob
538        // Amount of Contour knob
539        vcf2(x) = vcfg(hgroup("[1] Filter Contour [tooltip:AttFilt, DecFilt, Sustain Level for Filter Contour]", x));
540        // Attack Time knob
541        // Decay Time knob
542        // Sustain Level knob
543      ng(x) = modg(hgroup("[1] Loudness Contour", x));
544        // Attack Time knob
545        // Decay Time knob
546        // Sustain Level knob
547    echog(x) = fxg(hgroup("[4] Echo",x));
548      ekg(x) = echog(vgroup("[0] Knobs",x));
549      esg(x) = echog(vgroup("[1] Switches",x));
550    flg(x) = fxg(hgroup("[5] Flanger",x));
551      flkg(x) = flg(vgroup("[0] Knobs",x));
552      flsg(x) = flg(vgroup("[1] Switches",x));
553    chg(x) = fxg(hgroup("[6] Chorus",x));
554      ckg(x) = chg(vgroup("[0] Knobs",x));
555      csg(x) = chg(vgroup("[1] Switches",x));
556    rg(x) = fxg(hgroup("[7] Reverb",x));
557      rkg(x) = rg(vgroup("[0] Knobs",x));
558      rsg(x) = rg(vgroup("[1] Switches",x));
559    outg(x) = fxg(vgroup("[8] Output", x));
560      volg(x) = outg(hgroup("[0] Volume Main Output", x));
561        // Volume knob [0-10]
562	// Unison switch (Arturia) or Output connect/disconnect switch (original)
563	//   When set, all voices are stacked and instrument is in mono mode
564      tunerg(x) = outg(hgroup("[1] A-440 Switch", x));
565      vdtpolyg(x) = outg(hgroup("[2] Voice Detune / Poly", x));
566        // Voice Detune knob [0-10] (Arturia) or
567	// Polyphonic switch [red LED below] (Arturia)
568	//   When set, instrument is in polyphonic mode with one oscillator per key
569    clipg(x) = fxg(vgroup("[9] Soft Clip", x));
570	// Soft Clipping switch [red LED above]
571  kg(x) = synthg(hgroup("[1] Keyboard Group", x)); // Keyboard was 3 1/2 octaves
572    ws(x) = kg(vgroup("[0] Wheels and Switches", x));
573      s1g(x) = ws(hgroup("[0] Jacks and Rockers", x));
574        jg(x) = s1g(vgroup("[0] MiniJacks",x));
575        gdlg(x) = s1g(vgroup("[1] Glide/Decay/Legato Enables",x)); // Arturia
576	// Glide Hrocker (see original Button version below)
577	// Decay Hrocker (see original Button version below) => Sets Release (R) of ADSR to either 0 or Decay (R)
578	// Legato Hrocker (not in original)
579      s2g(x) = ws(hgroup("[1] [tooltip:Wheels+]", x));
580        bg(x) = s2g(vgroup("[0] [tooltip:Bend Enable and Range]", x));
581        wg(x) = s2g(hgroup("[1] [tooltip:Bend and Mod Wheels]", x));
582	// Using Glide/Decay/Legato enables above following Arturia:
583	//   dg(x) = s2g(hgroup("[2] Glide and Decay momentary pushbuttons", x));
584	//   Glide Button injects portamento as set by Glide knob
585	//   Decay Button uses decay of Loudness Contour (else 0)
586    keys(x) = kg(hgroup("[1] [tooltip:Keys]", x));
587      gg(x) = keys(hgroup("[0] [tooltip: Gates]",x));
588      // leave slot 1 open for sustain (below)
589