1// All effects used by minimoog.dsp 2 3import("stdfaust.lib"); 4 5process = _,_ : + 6 : component_echo 7 : component_flanger 8 : component_chorus 9 : component_freeverb; 10 11component_echo = environment { 12 13echo_group(x) = x; // Let layout2.dsp lay us out 14knobs_group(x) = ekg(x); 15switches_group(x) = esg(x); 16 17dmax = 32768; // one and done 18dmaxs = float(dmax)/44100.0; 19 20Nnines = 1.8; // Increase until you get the desired maximum amount of smoothing when fbs==1 21//fastpow2 = ffunction(float fastpow2(float), "fast_pow2.h", ""); 22fbspr(fbs) = 1.0 - pow(2.0, -3.33219*Nnines*fbs); // pole radius of feedback smoother 23inputSelect(gi) = _,0 : select2(gi); 24echo_mono(dmax,curdel,tapdel,fb,fbspr,gi) = inputSelect(gi) : (+:si.smooth(fbspr) 25<: de.fdelay(dmax,curdel), 26de.fdelay(dmax,tapdel)) 27~(*(fb),!) : !,_; 28 29tau2pole(tau) = ba.if(tau>0, exp(-1.0/(tau*ma.SR)), 0.0); 30t60smoother(dEchoT60) = si.smooth(tau2pole(dEchoT60/6.91)); 31 32dEchoT60 = knobs_group(vslider("[1] DelayT60 [midi:ctrl 60] [style:knob]", 0.5, 0, 100, 0.001)); 33dEchoSamplesRaw = knobs_group(vslider("[0] Delay [midi:ctrl 61] [style:knob]", 0.5, 0.001, (dmaxs-0.001), 0.001)) * ma.SR; 34dEchoSamples = dEchoSamplesRaw : t60smoother(dEchoT60); 35warpRaw = knobs_group(vslider("[0] Warp [midi:ctrl 62] [style:knob]", 0, -1.0, 1.0, 0.001)); 36 37scrubAmpRaw = 0; 38 39scrubPhaseRaw = 0; 40fb = knobs_group(vslider("[2] Feedback [midi:ctrl 2] [style:knob]", .3, 0.0, 1.0, 0.0001)); 41amp = knobs_group(vslider("[3] Amp [midi:ctrl 75] [style:knob]", .5, 0, 1, 0.001)) : si.smooth(ba.tau2pole(ampT60/6.91)); 42 43ampT60 = 0.15661; 44fbs = knobs_group(vslider("[5] [midi:ctrl 76] FeedbackSm [style:knob]", 0, 0, 1, 0.00001)); 45gi = switches_group(1-vslider("[7] [midi:ctrl 105] EnableEcho[style:knob]",0,0,1,1)); // "ground input" switches input to zeros 46 47// Warp and Scrubber stuff: 48enableEcho = (scrubAmpRaw > 0.00001); 49triggerScrubOn = (enableEcho - enableEcho') > 0; // enableEcho went 0 to 1 50triggerScrubOff = (enableEcho - enableEcho') < 0; // enableEcho went 1 to 0 51// Ramps up only during scrub "hold" time and is otherwise zero: 52counter = (enableEcho * (triggerScrubOn : + ~ +(1) * enableEcho : -(2))) & (dmax-1); 53// implementation that continues scrubbing where it left off: 54 55scrubPhase = scrubPhaseRaw : t60smoother(dEchoT60*(1-triggerScrubOff)); 56scrubAmp = scrubAmpRaw : t60smoother(dEchoT60*(1-triggerScrubOff)); 57warp = warpRaw : t60smoother(dEchoT60); 58dTapSamplesRaw = dEchoSamplesRaw * (1.0 + warp + scrubPhase * scrubAmp) + float(counter); 59dTapSamples = dTapSamplesRaw : t60smoother(dEchoT60*(1-triggerScrubOff)); 60 61echo_process = _ <: _, amp * echo_mono(dmax,dEchoSamples,dTapSamples,fb,fbspr(fbs),gi) : +; 62 63}.echo_process; 64 65component_flanger = environment { 66 67// Created from flange.dsp 2015/06/21 68 69flanger_mono(dmax,curdel,depth,fb,invert,lfoshape) 70 = _ <: _, (-:de.fdelay(dmax,curdel)) ~ *(fb) : _, *(select2(invert,depth,0-depth)) : + : *(1/(1+depth)); // ideal for dc and reinforced sinusoids (in-phase summed signals) 71 72flanger_process = ba.bypass1(fbp,flanger_mono_gui); 73 74// Kill the groups to save vertical space: 75meter_group(x) = flsg(x); 76ctl_group(x) = flkg(x); 77del_group(x) = flkg(x); 78lvl_group(x) = flkf(x); 79 80flangeview = lfo(freq); 81 82flanger_mono_gui = attach(flangeview) : flanger_mono(dmax,curdel,depth,fb,invert,lfoshape); 83 84sinlfo(freq) = (1 + os.oscrs(freq))/2; 85trilfo(freq) = 1.0-abs(os.saw1(freq)); 86lfo(f) = (lfoshape * trilfo(f)) + ((1-lfoshape) * sinlfo(f)); 87 88dmax = 2048; 89odflange = 44; // ~1 ms at 44.1 kHz = min delay 90dflange = ((dmax-1)-odflange)*del_group(vslider("[1] Delay [midi:ctrl 50][style:knob]", 0.22, 0, 1, 1)); 91freq = 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)); 92 93freqT60 = 0.15661; 94depth = ctl_group(vslider("[3] Depth [midi:ctrl 52] [style:knob]", .75, 0, 1, 0.001)) : si.smooth(ba.tau2pole(depthT60/6.91)); 95 96depthT60 = 0.15661; 97fb = ctl_group(vslider("[5] Feedback [midi:ctrl 53] [style:knob]", 0, -0.995, 0.99, 0.001)) : si.smooth(ba.tau2pole(fbT60/6.91)); 98 99fbT60 = 0.15661; 100lfoshape = ctl_group(vslider("[7] Waveshape [midi:ctrl 54] [style:knob]", 0, 0, 1, 0.001)); 101curdel = odflange+dflange*lfo(freq); 102 103fbp = 1-int(flsg(vslider("[0] Enable [midi:ctrl 102][style:knob]",0,0,1,1))); 104 105invert = flsg(vslider("[1] Invert [midi:ctrl 49][style:knob]",0,0,1,1):int); 106 107}.flanger_process; 108 109component_chorus = environment { 110 111voices = 8; // MUST BE EVEN 112chorus_process = ba.bypass1to2(cbp,chorus_mono(dmax,curdel,rate,sigma,do2,voices)); 113 114dmax = 8192; 115curdel = dmax * ckg(vslider("[0] Delay [midi:ctrl 55] [style:knob]", 0.5, 0, 1, 1)) : si.smooth(0.999); 116rateMax = 7.0; // Hz 117rateMin = 0.01; 118rateT60 = 0.15661; 119rate = ckg(vslider("[1] Rate [midi:ctrl 56] [unit:Hz] [style:knob]", 0.5, rateMin, rateMax, 0.0001)) 120 : si.smooth(ba.tau2pole(rateT60/6.91)); 121 122depth = ckg(vslider("[4] Depth [midi:ctrl 57] [style:knob]", 0.5, 0, 1, 0.001)) : si.smooth(ba.tau2pole(depthT60/6.91)); 123 124depthT60 = 0.15661; 125delayPerVoice = 0.5*curdel/voices; 126sigma = delayPerVoice * ckg(vslider("[6] Deviation [midi:ctrl 58] [style:knob]",0.5,0,1,0.001)) : si.smooth(0.999); 127 128periodic = 1; 129 130do2 = depth; // use when depth=1 means "multivibrato" effect (no original => all are modulated) 131cbp = 1-int(csg(vslider("[0] Enable [midi:ctrl 103][style:knob]",0,0,1,1))); 132 133chorus_mono(dmax,curdel,rate,sigma,do2,voices) 134 = _ <: (*(1-do2)<:_,_),(*(do2) <: par(i,voices,voice(i)) :> _,_) : ro.interleave(2,2) : +,+ 135 with { 136 angle(i) = 2*ma.PI*(i/2)/voices + (i%2)*ma.PI/2; 137 voice(i) = de.fdelay(dmax,min(dmax,del(i))) * cos(angle(i)); 138 del(i) = curdel*(i+1)/voices + dev(i); 139 rates(i) = rate/float(i+1); 140 dev(i) = sigma * os.oscp(rates(i),i*2*ma.PI/voices); 141 }; 142 143}.chorus_process; 144 145component_freeverb = environment { 146 147import("stdfaust.lib"); 148 149declare name "freeverb"; 150declare version "1.0"; 151declare author "Grame"; 152declare license "BSD"; 153declare copyright "(c) GRAME 2006 and MoForte Inc. 2017"; 154declare reference "https://ccrma.stanford.edu/~jos/pasp/Freeverb.html"; 155 156//====================================================== 157// 158// Freeverb 159// Faster version using fixed delays (20% gain) 160// 161//====================================================== 162 163// Constant Parameters 164//-------------------- 165 166fixedgain = 0.015; //value of the gain of fxctrl 167scalewet = 3.0; 168scaledry = 2.0; 169scaledamp = 0.4; 170scaleroom = 0.28; 171offsetroom = 0.7; 172initialroom = 0.5; 173initialdamp = 0.5; 174initialwet = 1.0/scalewet; 175initialdry = 0; 176initialwidth= 1.0; 177initialmode = 0.0; 178freezemode = 0.5; 179stereospread= 23; 180allpassfeed = 0.5; //feedback of the delays used in allpass filters 181 182// Filter Parameters 183//------------------ 184 185combtuningL1 = 1116; 186combtuningL2 = 1188; 187combtuningL3 = 1277; 188combtuningL4 = 1356; 189combtuningL5 = 1422; 190combtuningL6 = 1491; 191combtuningL7 = 1557; 192combtuningL8 = 1617; 193 194allpasstuningL1 = 556; 195allpasstuningL2 = 441; 196allpasstuningL3 = 341; 197allpasstuningL4 = 225; 198 199// Control Sliders 200//-------------------- 201// Damp : filters the high frequencies of the echoes (especially active for great values of RoomSize) 202// RoomSize : size of the reverberation room 203// Dry : original signal 204// Wet : reverberated signal 205 206dampSlider = rkg(vslider("Damp [midi:ctrl 3] [style:knob]",0.5, 0, 1, 0.025))*scaledamp; 207roomsizeSlider = rkg(vslider("RoomSize [midi:ctrl 4] [style:knob]", 0.5, 0, 1, 0.025))*scaleroom + offsetroom; 208wetSlider = rkg(vslider("Wet [midi:ctrl 79] [style:knob]", 0.3333, 0, 1, 0.025)); 209combfeed = roomsizeSlider; 210 211// Comb and Allpass filters 212//------------------------- 213 214allpass(dt,fb) = (_,_ <: (*(fb),_:+:@(dt)), -) ~ _ : (!,_); 215 216comb(dt, fb, damp) = (+:@(dt)) ~ (*(1-damp) : (+ ~ *(damp)) : *(fb)); 217 218// Reverb components 219//------------------ 220 221monoReverb(fb1, fb2, damp, spread) 222= _ <: comb(combtuningL1+spread, fb1, damp), 223comb(combtuningL2+spread, fb1, damp), 224comb(combtuningL3+spread, fb1, damp), 225comb(combtuningL4+spread, fb1, damp), 226comb(combtuningL5+spread, fb1, damp), 227comb(combtuningL6+spread, fb1, damp), 228comb(combtuningL7+spread, fb1, damp), 229comb(combtuningL8+spread, fb1, damp) 230+> 231allpass (allpasstuningL1+spread, fb2) 232: allpass (allpasstuningL2+spread, fb2) 233: allpass (allpasstuningL3+spread, fb2) 234: allpass (allpasstuningL4+spread, fb2) 235; 236 237monoReverbToStereo(fb1, fb2, damp, spread) 238= + <: monoReverb(fb1, fb2, damp, 0) <: _,_; 239stereoReverb(fb1, fb2, damp, spread) 240= + <: monoReverb(fb1, fb2, damp, 0), monoReverb(fb1, fb2, damp, spread); 241monoToStereoReverb(fb1, fb2, damp, spread) 242= _ <: monoReverb(fb1, fb2, damp, 0), monoReverb(fb1, fb2, damp, spread); 243 244// fxctrl : add an input gain and a wet-dry control to a stereo FX 245//---------------------------------------------------------------- 246 247fxctrl(g,w,Fx) = _,_ <: (*(g),*(g) : Fx : *(w),*(w)), *(1-w), *(1-w) +> _,_; 248 249rbp = 1-int(rsg(vslider("[0] Enable [midi:ctrl 104][style:knob]",0,0,1,1))); 250 251// Freeverb 252//--------- 253 254//JOS:freeverb = fxctrl(fixedgain, wetSlider, stereoReverb(combfeed, allpassfeed, dampSlider, stereospread)); 255freeverb = fxctrl(fixedgain, wetSlider, monoReverbToStereo(combfeed, allpassfeed, dampSlider, stereospread)); 256 257freeverb_process = ba.bypass2(rbp,freeverb); 258 259}.freeverb_process; 260 261// This layout loosely follows the MiniMoog-V 262// Arturia-only features are labeled 263// Original versions also added where different 264 265// Need vrocker and hrocker toggle switches in Faust! 266// Need orange and blue color choices 267// Orange => Connect modulation sources to their destinations 268// Blue => Turn audio sources On and Off 269// - and later - 270// White => Turn performance features On and Off 271// Black => Select between modulation sources 272// Julius Smith for Analog Devices 3/1/2017 273 274vrocker(x) = checkbox("%%x [style:vrocker]"); 275hrocker(x) = checkbox("%%x [style:hrocker]"); 276vrockerblue(x) = checkbox("%x [style:vrocker] [color:blue]"); 277vrockerblue(x) = checkbox("%x [style:vrocker] [color:blue]"); 278 // USAGE: vrockerorange("[0] ModulationEnable"); 279 280hrockerblue(x) = checkbox("%%x [style:hrocker] [color:blue]"); 281vrockerred(x) = checkbox("%%x [style:vrocker] [color:red]"); 282hrockerred(x) = checkbox("%%x [style:hrocker] [color:red]"); 283 284declare designer "Robert A. Moog"; 285 286mmg(x) = hgroup("",x); // Minimoog + Effects 287 synthg(x) = mmg(vgroup("[0] Minimoog",x)); 288 fxg(x) = mmg(hgroup("[1] Effects",x)); 289 mg(x) = synthg(hgroup("[0]",x)); 290 cg(x) = mg(vgroup("[0] Controllers",x)); // Formerly named "Modules" but "Minimoog" group-title is enough 291 vg(x) = cg(hgroup("[0] Master Volume", x)); 292 dg(x) = cg(hgroup("[1] Oscillator Tuning & Switching", x)); 293 // Tune knob = master tune 294 dsg(x) = dg(vgroup("[1] Switches", x)); 295 // Oscillator Modulation HrockerRed => apply Modulation Mix output to osc1&2 pitches 296 // [MOVED here from osc3 group] Osc 3 Control VrockerRed => use osc3 as LFO instead of osc3 297 gmmg(x) = cg(hgroup("[2] Glide and ModMix", x)); 298 // Glide knob [0:10] = portamento speed 299 // Modulation Mix knob [0:10] (between Osc3 and Noise) = mix of noise and osc3 modulating osc1&2 pitch and/or VCF freq 300 og(x) = mg(vgroup("[1] Oscillator Bank", x)); 301 osc1(x) = og(hgroup("[1] Oscillator 1", x)); 302 // UNUSED Control switch (for alignment) - Could put Oscillator Modulation switch there 303 // Range rotary switch: LO (slow pulses or rhythm), 32', 16', 8', 4', 2' 304 // Frequency <something> switch: LED to right 305 // Waveform rotary switch: tri, impulse/bent-triangle, saw, pulseWide, pulseMed, pulseNarrow 306 osc2(x) = og(hgroup("[2] Oscillator 2", x)); 307 // UNUSED (originall) or Osc 2 Control VrockerRed 308 // Range rotary switch: LO, 32', 16', 8', 4', 2' 309 // Detuning knob: -7 to 7 [NO SWITCH] 310 // Waveform rotary switch: tri, impulse(?), saw, pulseWide, pulseMed, pulseNarrow 311 osc3(x) = og(hgroup("[3] Oscillator 3", x)); 312 // Osc 3 Control VrockerRed => use osc3 as LFO instead of osc3 313 // Range rotary switch: LO, 32', 16', 8', 4', 2' 314 // Detuning knob: -7 to 7 [NO SWITCH] 315 // Waveform rotary switch: tri, impulse(?), saw, pulseWide, pulseMed, pulseNarrow 316 mixg(x) = mg(vgroup("[2] Mixer", x)); 317 // Each row 5 slots to maintain alignment and include red rockers joining VCF area: 318 mr1(x) = mixg(hgroup("[0] Osc1", x)); // mixer row 1 = 319 // Osc1 Volume and Osc1 HrockerBlue & _ & _ & Filter Modulation HrockerRed 320 // Filter Modulation => Modulation Mix output to VCF freq 321 mr2(x) = mixg(hgroup("[1] Ext In, KeyCtl", x)); // row 2 = Ext In HrockerBlue and Vol and Overload LED and Keyboard Ctl HrockerRed 1 322 mr3(x) = mixg(hgroup("[2] Osc2", x)); // = Osc2 Volume and Osc2 HrockerBlue and Keyboard Ctl HrockerRed 2 323 // Keyboard Control Modulation 1&2 => 0, 1/3, 2/3, all of Keyboard Control Signal ("gate?") applied to VCF freq 324 mr4(x) = mixg(hgroup("[3] Noise", x)); // = Noise HrockerBlue and Volume and Noise Type VrockerBlue 325 mr4cbg(x) = mr4(vgroup("[1]", x)); // = Noise Off and White/Pink selection 326 // two rockers 327 mr5(x) = mixg(hgroup("[4] Osc3", x)); // Osc3 Volume and Osc3 HrockerBlue 328 modg(x) = mg(vgroup("[3] Modifiers", x)); 329 vcfg(x) = modg(vgroup("[0] Filter", x)); 330 vcf1(x) = vcfg(hgroup("[0] [tooltip:freq, Q, ContourScale]", x)); 331 vcf1cbg(x) = vcf1(vgroup("[0] [tooltip:two checkboxes]", x)); 332 // Filter Modulation switch 333 // VCF Off switch 334 // Corner Frequency knob 335 // Filter Emphasis knob 336 // Amount of Contour knob 337 vcf2(x) = vcfg(hgroup("[1] Filter Contour [tooltip:AttFilt, DecFilt, Sustain Level for Filter Contour]", x)); 338 // Attack Time knob 339 // Decay Time knob 340 // Sustain Level knob 341 ng(x) = modg(hgroup("[1] Loudness Contour", x)); 342 // Attack Time knob 343 // Decay Time knob 344 // Sustain Level knob 345 echog(x) = fxg(hgroup("[4] Echo",x)); 346 ekg(x) = echog(vgroup("[0] Knobs",x)); 347 esg(x) = echog(vgroup("[1] Switches",x)); 348 flg(x) = fxg(hgroup("[5] Flanger",x)); 349 flkg(x) = flg(vgroup("[0] Knobs",x)); 350 flsg(x) = flg(vgroup("[1] Switches",x)); 351 chg(x) = fxg(hgroup("[6] Chorus",x)); 352 ckg(x) = chg(vgroup("[0] Knobs",x)); 353 csg(x) = chg(vgroup("[1] Switches",x)); 354 rg(x) = fxg(hgroup("[7] Reverb",x)); 355 rkg(x) = rg(vgroup("[0] Knobs",x)); 356 rsg(x) = rg(vgroup("[1] Switches",x)); 357 outg(x) = fxg(vgroup("[8] Output", x)); 358 volg(x) = outg(hgroup("[0] Volume Main Output", x)); 359 // Volume knob [0-10] 360 // Unison switch (Arturia) or Output connect/disconnect switch (original) 361 // When set, all voices are stacked and instrument is in mono mode 362 tunerg(x) = outg(hgroup("[1] A-440 Switch", x)); 363 vdtpolyg(x) = outg(hgroup("[2] Voice Detune / Poly", x)); 364 // Voice Detune knob [0-10] (Arturia) or 365 // Polyphonic switch [red LED below] (Arturia) 366 // When set, instrument is in polyphonic mode with one oscillator per key 367 clipg(x) = fxg(vgroup("[9] Soft Clip", x)); 368 // Soft Clipping switch [red LED above] 369 kg(x) = synthg(hgroup("[1] Keyboard Group", x)); // Keyboard was 3 1/2 octaves 370 ws(x) = kg(vgroup("[0] Wheels and Switches", x)); 371 s1g(x) = ws(hgroup("[0] Jacks and Rockers", x)); 372 jg(x) = s1g(vgroup("[0] MiniJacks",x)); 373 gdlg(x) = s1g(vgroup("[1] Glide/Decay/Legato Enables",x)); // Arturia 374 // Glide Hrocker (see original Button version below) 375 // Decay Hrocker (see original Button version below) => Sets Release (R) of ADSR to either 0 or Decay (R) 376 // Legato Hrocker (not in original) 377 s2g(x) = ws(hgroup("[1] [tooltip:Wheels+]", x)); 378 bg(x) = s2g(vgroup("[0] [tooltip:Bend Enable and Range]", x)); 379 wg(x) = s2g(hgroup("[1] [tooltip:Bend and Mod Wheels]", x)); 380 // Using Glide/Decay/Legato enables above following Arturia: 381 // dg(x) = s2g(hgroup("[2] Glide and Decay momentary pushbuttons", x)); 382 // Glide Button injects portamento as set by Glide knob 383 // Decay Button uses decay of Loudness Contour (else 0) 384 keys(x) = kg(hgroup("[1] [tooltip:Keys]", x)); 385 gg(x) = keys(hgroup("[0] [tooltip: Gates]",x)); 386 // leave slot 1 open for sustain (below) 387