desc:GFX Phaser //tags: phaser flanger tape modulation //author: JGWizrad / Garden FX (GFX) // // Requires gfx-drift, gfx-lfo, gfx-input, gfx-output, gfx-ui, gfx-panel, gfx-dyn and gfx-faceplate (.jsfx-inc) in the same folder. import gfx-drift.jsfx-inc import gfx-lfo.jsfx-inc import gfx-input.jsfx-inc import gfx-output.jsfx-inc import gfx-ui.jsfx-inc import gfx-panel.jsfx-inc import gfx-dyn.jsfx-inc import gfx-faceplate.jsfx-inc import gfx-instrument.jsfx-inc slider1:0<0,2,1{90,Opto,Tape}>-Voicing slider2:1<0,4,1{2,4,6,8,12}>-Stages slider3:1<0,3,1{Sine,Triangle,Square,Random}>-Shape slider4:0<0,2,1{Free,Sync,Flow}>-Rate mode slider5:0.5<0.02,10,0.01>-Free rate (Hz) slider6:2<0,15,1{4 bars,2 bars,1 bar,1/2,1/2 D,1/2 T,1/4,1/4 D,1/4 T,1/8,1/8 D,1/8 T,1/16,1/16 D,1/16 T,1/32}>-Sync rate slider7:50<0,100,1>-Centre (%) slider8:70<0,100,1>-Depth (%) slider9:0<-95,95,1>-Feedback (%) slider10:0<0,180,1>-Spread (deg) slider11:50<0,100,1>-Mix (%) slider12:5<0,50,1>-Rate drift (%) slider13:10<0,50,1>-Depth drift (%) slider14:10<0,50,1>-Centre drift (%) slider15:0.25<0.02,2,0.01>-Drift speed (Hz) slider16:0<-24,12,0.1>-Output (dB) slider17:1<0,2,1{LFO,Drift,Both}>-Tape motion slider18:20<0,100,1>-Band sat (%) slider19:30<0,100,1>-Bias (%) slider20:120<40,400,1>-Sat low band (Hz) slider21:6000<2000,16000,10>-Sat high band (Hz) slider22:-0.3<-24,0,0.1>-Ceiling / limit threshold (dB) slider23:1<0,2,1{Off,Clip,Limit}>-Output stage slider24:0<-24,12,0.1>-Input trim (dB) slider25:1<0,1,1{Off,On}>-Input guard slider26:20<20,500,1>-Wet low cut (Hz, 20 = off) slider27:50<0,100,1>-Drift (%) in_pin:left input in_pin:right input out_pin:left output out_pin:right output @init #ui_man = "gfx-phaser"; function sat(x) local(a) ( a = abs(x); a < 0.7 ? x : sign(x) * (0.7 + 0.3 * lfo_tanh((a - 0.7) / 0.3)); ); function ap_chain(x, base, n, a) local(i, y) ( i = 0; loop(n, y = a * x + apx[base + i] - a * apy[base + i]; apx[base + i] = x; apy[base + i] = y; x = y; i += 1; ); x; ); function dread(buf, d) local(rp, i, f, xm1, x0, x1, x2) ( rp = wp - d; rp < 0 ? rp += tsize; i = floor(rp); f = rp - i; xm1 = buf[(i + tsize - 1) & tmask]; x0 = buf[i & tmask]; x1 = buf[(i + 1) & tmask]; x2 = buf[(i + 2) & tmask]; ((((0.5 * (x2 - xm1) + 1.5 * (x0 - x1)) * f + (xm1 - 2.5 * x0 + 2 * x1 - 0.5 * x2)) * f + 0.5 * (x1 - xm1)) * f + x0); ); function wlc(x) instance(x1, x2, y1, y2) local(y) ( y = wl_b0 * (x - 2 * x1 + x2) - wl_a1 * y1 - wl_a2 * y2; x2 = x1; x1 = x; y2 = y1; y1 = y; y; ); function wlc_set(f) local(w0, al, n) ( w0 = 2 * $pi * min(f, srate * 0.45) / srate; al = sin(w0) / (2 * 0.7071); n = 1 + al; wl_b0 = (1 + cos(w0)) * 0.5 / n; wl_a1 = -2 * cos(w0) / n; wl_a2 = (1 - al) / n; ); function update() ( wl_on = slider26 > 20.5; wl_on ? wlc_set(slider26); voice = slider1; nst = slider2 == 0 ? 2 : slider2 == 1 ? 4 : slider2 == 2 ? 6 : slider2 == 3 ? 8 : 12; shape = slider3; syncmode = slider4; frate = slider5; div = floor(slider6 + 0.5); cent_t = slider7 / 100; depth_t = slider8 / 100; fb_t = slider9 / 100; spread = slider10 / 360; mix_t = slider11 / 100; drift_macro(slider27); rd = slider12 / 100 * dm_a; dd = min(slider13 / 100 * dm_a, 1); cd = slider14 / 100 * dm_a; dspd = slider15 * dm_s; out_t = 10 ^ (slider16 / 20); motion = slider17; in_setup(slider24, slider25); ost_setup(slider18 / 100, slider19 / 100, slider20, slider21, slider22, slider23); lfoL.lfo_setup(shape, 0.2); lfoR.lfo_setup(shape, 0.2); voice == 1 ? ( fmin = 80; fmax = 2000; oct_rng = 3.5; ) : ( fmin = 120; fmax = 2400; oct_rng = 2.5; ); ); function setp(v, st, sh, sy, fr, dv, ce, de, fb, sp, mx, r_, d_, c_, mo) ( slider1 = v; slider2 = st; slider3 = sh; slider4 = sy; slider5 = fr; slider6 = dv; slider7 = ce; slider8 = de; slider9 = fb; slider10 = sp; slider11 = mx; slider12 = r_; slider13 = d_; slider14 = c_; slider17 = mo; ); function load_preset(p) ( p == 1 ? setp(0, 1, 1, 0, 0.6, 2, 50, 80, 0, 0, 50, 5, 5, 5, 1) : p == 2 ? setp(0, 1, 1, 0, 0.6, 2, 50, 80, 40, 0, 50, 5, 5, 5, 1) : p == 3 ? setp(0, 1, 1, 0, 0.15, 2, 45, 90, 0, 0, 50, 8, 10, 10, 1) : p == 4 ? setp(1, 3, 0, 0, 0.3, 2, 45, 90, 30, 90, 50, 10, 10, 15, 1) : p == 5 ? setp(1, 2, 2, 1, 0.5, 9, 50, 70, 20, 0, 50, 0, 5, 5, 1) : p == 6 ? setp(1, 4, 0, 0, 0.2, 2, 50, 85, 50, 180, 50, 8, 10, 10, 1) : p == 7 ? setp(1, 1, 0, 0, 4.5, 2, 55, 50, 0, 0, 100, 5, 5, 0, 1) : p == 8 ? setp(2, 1, 0, 0, 0.15, 2, 30, 80, 0, 30, 50, 0, 15, 10, 1) : p == 9 ? setp(2, 1, 0, 0, 0.1, 2, 40, 90, 60, 0, 50, 5, 10, 5, 2) : p == 10 ? setp(2, 1, 0, 0, 0.08, 2, 15, 100, 0, 0, 50, 0, 0, 0, 0); p >= 1 && p <= 10 ? slider_automate(81919); p == 11 ? ( slider1 = 0; slider2 = 1; slider3 = 1; slider4 = 0; slider5 = 0.5; slider6 = 2; slider7 = 50; slider8 = 70; slider9 = 0; slider10 = 0; slider11 = 50; slider12 = 5; slider13 = 10; slider14 = 10; slider15 = 0.25; slider16 = 0; slider17 = 1; slider18 = 20; slider19 = 30; slider20 = 120; slider21 = 6000; slider22 = -0.3; slider23 = 1; slider24 = 0; slider25 = 1; slider26 = 20; slider27 = 50; slider_automate(134217727); ); ); apx = 0; apy = 32; tsize = 8192; tmask = tsize - 1; tbL = 1024; tbR = tbL + tsize; cbL = tbR + tsize; cbR = cbL + tsize; memset(0, 0, cbR + tsize); wp = 0; sm = 1 - exp(-1 / (0.03 * srate)); a_fast = 1 - exp(-1 / (0.008 * srate)); a_slow = 1 - exp(-1 / (0.12 * srate)); lpc = 1 - exp(-2 * $pi * min(12000, srate * 0.45) / srate); dryD = 0.001 * srate; lfoL.lfo_init(); lfoR.lfo_init(); drR.drift_init(0.8 + rand(0.5)); drD.drift_init(0.8 + rand(0.5)); drC.drift_init(0.8 + rand(0.5)); dhL.drift_init(0.8 + rand(0.5)); dhR.drift_init(0.8 + rand(0.5)); optoL = 0.5; optoR = 0.5; fbL = 0; fbR = 0; tfbL = 0; tfbR = 0; lpL = 0; lpR = 0; fbc = 1; dispFbc = 1; ost_init(40000); ostL.ost_ch_init(); ostR.ost_ch_init(); in_init(); vuc = 1 - exp(-1 / (0.01 * srate)); vu_ms = 0; !ui_dflt ? ui_thm = 4; !ui_dflt ? ui_hw = 1; pkL = 0; pkR = 0; update(); cent_s = cent_t; depth_s = depth_t; fb_s = fb_t; mix_s = mix_t; out_s = out_t; ui_dflt = 1; @slider update(); @block update(); lfoL.lfo_block(syncmode, div, frate); lfoR.lfo_block(syncmode, div, frate); @serialize file_var(0, ui_thm); file_var(0, ui_sz); file_var(0, ui_hw); file_avail(0) != 0 ? file_var(0, pr_cur); // the preset in use (older projects end before it) @sample in_process(spl0, spl1); inL = in_l; inR = in_r; vu_ms += (0.5 * (inL * inL + inR * inR) - vu_ms) * vuc; dinc = dspd / srate; vR = drR.drift_tick(dinc); vD = drD.drift_tick(dinc); vC = drC.drift_tick(dinc); cent_s += (cent_t - cent_s) * sm; depth_s += (depth_t - depth_s) * sm; fb_s += (fb_t - fb_s) * sm; mix_s += (mix_t - mix_s) * sm; out_s += (out_t - out_s) * sm; fbc = sqrt(1 - fb_s * fb_s); lvL = lfoL.lfo_tick(0, rd, vR); lvR = lfoR.lfo_tick(spread, rd, vR); de = depth_s * (1 - dd * (0.5 + 0.5 * vD)); coct = cd * vC * 1.5; voice == 2 ? ( hinc = frate / srate; hL = dhL.drift_tick(hinc); hR = dhR.drift_tick(hinc); hR = hL + (hR - hL) * spread * 2; motion == 0 ? ( mL = lvL; mR = lvR; ) : ( motion == 1 ? ( mL = hL; mR = hR; ) : ( mL = 0.5 * (lvL + hL); mR = 0.5 * (lvR + hR); ); ); c_ms = 0.6 + cent_s * 7.4; mdL = max(min(max(c_ms * 2 ^ (2 * de * mL + coct), 0.05), 10) * srate / 1000, 2); mdR = max(min(max(c_ms * 2 ^ (2 * de * mR + coct), 0.05), 10) * srate / 1000, 2); cbL[wp] = inL; cbR[wp] = inR; tbL[wp] = inL + fb_s * tfbL; tbR[wp] = inR + fb_s * tfbR; lpL += (dread(tbL, mdL) - lpL) * lpc; lpR += (dread(tbR, mdR) - lpR) * lpc; tfbL = sat(lpL); tfbR = sat(lpR); dL = dread(cbL, dryD); dR = dread(cbR, dryD); oL = wl_on ? dL + mix_s * (WLL.wlc(lpL * fbc) - WDL.wlc(dL)) : (1 - mix_s) * dL + mix_s * lpL * fbc; oR = wl_on ? dR + mix_s * (WLR.wlc(lpR * fbc) - WDR.wlc(dR)) : (1 - mix_s) * dR + mix_s * lpR * fbc; wp = (wp + 1) & tmask; dispMd = mdL / srate * 1000; ) : ( uL = 0.5 + 0.5 * lvL; uR = 0.5 + 0.5 * lvR; voice == 1 ? ( optoL += (uL - optoL) * (uL > optoL ? a_fast : a_slow); optoR += (uR - optoR) * (uR > optoR ? a_fast : a_slow); uL = max(optoL, 0) ^ 0.8; uR = max(optoR, 0) ^ 0.8; ) : ( uL = max(uL, 0) ^ 1.5; uR = max(uR, 0) ^ 1.5; ); fcc = fmin * (fmax / fmin) ^ cent_s; fcL = min(max(fcc * 2 ^ (de * oct_rng * (2 * uL - 1) + coct), 20), srate * 0.45); fcR = min(max(fcc * 2 ^ (de * oct_rng * (2 * uR - 1) + coct), 20), srate * 0.45); tL = tan($pi * fcL / srate); aL = (tL - 1) / (tL + 1); tR = tan($pi * fcR / srate); aR = (tR - 1) / (tR + 1); yL = ap_chain(inL + fb_s * fbL, 0, nst, aL); yR = ap_chain(inR + fb_s * fbR, 12, nst, aR); fbL = sat(yL); fbR = sat(yR); oL = wl_on ? inL + mix_s * (WLL.wlc(yL * fbc) - WDL.wlc(inL)) : (1 - mix_s) * inL + mix_s * yL * fbc; oR = wl_on ? inR + mix_s * (WLR.wlc(yR * fbc) - WDR.wlc(inR)) : (1 - mix_s) * inR + mix_s * yR * fbc; voice == 0 ? ( oL = sat(oL); oR = sat(oR); ); dispFc = fcL; dispA = aL; ); oL = ostL.ost_band(oL) * in_comp * out_s; oR = ostR.ost_band(oR) * in_comp * out_s; ost_out(oL, oR); spl0 = ost_l; spl1 = ost_r; pkL = max(pkL, abs(spl0)); pkR = max(pkR, abs(spl1)); dispN = nst; dispFb = fb_s; dispMix = mix_s; dispFbc = fbc; @gfx 1060 360 ui_begin(1060, 360); FACE = 0x2A2C30; INK = 0xE6E1D6; DIM = 0x8A867E; C90 = 0xF07A2A; COPT = 0xE8C040; CTAP = 0x3A9CFF; ORG = slider1 == 0 ? C90 : slider1 == 1 ? COPT : CTAP; dy_ink = INK; dy_bg = FACE; function rsp(x, y, w, h, lc, gc) local(i, gf, w0, dw, er, ei, nr, ni, den, wr, wi, hr, hi, ar, ai, ph1, gr, gi, mag, mdb, qx, qy, px, py) ( ui_cola(gc, 0.35); gf = 100; loop(3, qx = (x + log(gf / 20) / log(1000) * w) * sc; gfx_line(qx, y * sc, qx, (y + h) * sc); gf *= 10; ); gfx_line(x * sc, (y + h * 12 / 42) * sc, (x + w) * sc, (y + h * 12 / 42) * sc); ui_col(lc); i = 0; loop(121, gf = 20 * 1000 ^ (i / 120); w0 = 2 * $pi * gf / srate; slider1 == 2 ? ( dw = dispMd * srate / 1000; er = cos(w0 * dw); ei = -sin(w0 * dw); nr = 1 - dispFb * er; ni = -dispFb * ei; den = nr * nr + ni * ni; wr = (er * nr + ei * ni) / den; wi = (ei * nr - er * ni) / den; hr = (1 - dispMix) * cos(w0 * dryD) + dispMix * dispFbc * wr; hi = -(1 - dispMix) * sin(w0 * dryD) + dispMix * dispFbc * wi; ) : ( ph1 = atan2(-sin(w0), dispA + cos(w0)) - atan2(-dispA * sin(w0), 1 + dispA * cos(w0)); ar = cos(dispN * ph1); ai = sin(dispN * ph1); nr = 1 - dispFb * ar; ni = -dispFb * ai; den = nr * nr + ni * ni; gr = (ar * nr + ai * ni) / den; gi = (ai * nr - ar * ni) / den; hr = (1 - dispMix) + dispMix * dispFbc * gr; hi = dispMix * dispFbc * gi; ); mag = sqrt(hr * hr + hi * hi); mdb = mag > 0.00001 ? 20 * log10(mag) : -100; mdb = min(max(mdb, -30), 12); qx = (x + i / 120 * w) * sc; qy = (y + (12 - mdb) / 42 * h) * sc; i > 0 ? ( gfx_line(px, py, qx, qy, 1); gfx_line(px, py + 1, qx, qy + 1, 1); ); px = qx; py = qy; i += 1; ); ); function setv(idx, v) ( slider(idx) = v; ui_auto(idx); ); ui_col(0x0A0A0B); gfx_rect(0, 0, gfx_w, gfx_h); ui_col(FACE); gfx_rect(26 * sc, 0, 1008 * sc, 360 * sc); rd_rack(0, 0, 1060, 360, 0x2A2B2E); rd_badge(60, 36, 15, INK, FACE); rd_title(84, 20, "PHASER", 26, INK); rd_sub(216, 32, "STEREO PHASE SHIFTER", DIM, 1); ui_col(ORG); gfx_rect(26 * sc, 66 * sc, 1008 * sc, 2 * sc); rd_sq(400, 22, 56, 26, slider1 == 0, C90, "90") ? setv(1, 0); rd_sq(462, 22, 56, 26, slider1 == 1, COPT, "OPTO") ? setv(1, 1); rd_sq(524, 22, 56, 26, slider1 == 2, CTAP, "TAPE") ? setv(1, 2); rd_lbl(490, 52, "VOICING", DIM, 1); pk_pextra = "Reset all to defaults"; // the house preset display: PRESETS, prev / next, LCD (click it for the list) k_ = pk_preset(610 * sc, 16 * sc, 300 * sc, 34 * sc, pr_cur, "90 Script|90 Block|90 Slow Swell|Opto Lush|Opto Square Throb|Opto Stereo Swirl|Opto Vibrato|Hand Tape Phasing|Tape Jet|Through-Zero Sweep"); k_ > 0 ? ( load_preset(k_); pr_cur = k_ > 10 ? 0 : k_; ); ui_size_picker(930 * sc, 24 * sc); ui_col(0x050505); ui_rrect(44 * sc, 84 * sc, 290 * sc, 108 * sc, 4 * sc); ui_col(ui_mix(ORG, 0x000000, 0.84)); gfx_rect(48 * sc, 88 * sc, 282 * sc, 100 * sc); gfx_gradrect(48 * sc, 88 * sc, 282 * sc, 50 * sc, 1, 1, 1, 0.05, 0, 0, 0, 0, 0, 0, 0, -0.05 / (50 * sc)); pk_lcdc = ui_mix(ORG, 0xFFFFFF, 0.1); rsp(54, 102, 270, 80, pk_lcdc, pk_lcdc); gfx_setfont(4); ui_col(pk_lcdc); gfx_x = 54 * sc; gfx_y = 88 * sc; slider1 == 2 ? sprintf(#inf, "TAPE %.2f ms", dispMd) : sprintf(#inf, "%d STAGES %.0f Hz", dispN, dispFc); gfx_drawstr(#inf); rd_sq(44, 212, 52, 22, slider3 == 0, ORG, "SINE") ? setv(3, 0); rd_sq(102, 212, 52, 22, slider3 == 1, ORG, "TRI") ? setv(3, 1); rd_sq(160, 212, 52, 22, slider3 == 2, ORG, "SQR") ? setv(3, 2); rd_sq(218, 212, 52, 22, slider3 == 3, ORG, "RND") ? setv(3, 3); rd_sq(44, 246, 52, 22, slider4 == 0, ORG, "FREE") ? setv(4, 0); rd_sq(102, 246, 52, 22, slider4 == 1, ORG, "SYNC") ? setv(4, 1); rd_sq(160, 246, 52, 22, slider4 == 2, ORG, "FLOW") ? setv(4, 2); slider1 == 0 ? ( rd_sq(218, 246, 52, 22, slider9 != 0, ORG, slider9 == 0 ? "SCRIPT" : slider9 == 40 ? "BLOCK" : "CUSTOM") ? setv(9, slider9 == 0 ? 40 : 0); ); rd_lbl(157, 196, "LFO SHAPE", DIM, 1); rd_lbl(128, 274, "RATE MODE", DIM, 1); slider1 == 2 ? ( dy_kst = 0; dy_step = 3; dy_steps = "LFO|HAND|BOTH"; rd_stepclick(310, 285, 14, 17, 3); dy_knob(310, 285, 14, 17, 0, 2, 1, 0, ORG, "MOTION", ""); ) : ( dy_kst = 0; dy_step = 5; dy_steps = "2|4|6|8|12"; rd_stepclick(310, 285, 14, 2, 5); dy_knob(310, 285, 14, 2, 0, 4, 1, 0, ORG, "STAGES", ""); ); dy_kst = 0; rd_led(396, 92, 4, lvL > 0, ORG); rd_lbl(396, 100, "RATE", DIM, 0); slider1 == 2 && slider17 == 1 ? ( sprintf(#v, "%.2f Hz", slider5); dy_knob(420, 150, 32, 5, 0.02, 10, 0.5, 1, ORG, "HAND SPEED", ""); ) : slider4 ? ( dy_step = 16; dy_steps = ""; dy_knob(420, 150, 32, 6, 0, 15, 2, 0, ORG, "RATE", ""); ) : ( sprintf(#v, "%.2f Hz", slider5); dy_knob(420, 150, 32, 5, 0.02, 10, 0.5, 1, ORG, "RATE", ""); ); sprintf(#v, "%.0f%%", slider7); dy_knob(515, 144, 22, 7, 0, 100, 50, 0, ORG, "CENTRE", ""); sprintf(#v, "%.0f%%", slider8); dy_knob(595, 144, 22, 8, 0, 100, 70, 0, ORG, "DEPTH", ""); sprintf(#v, "%+.0f%%", slider9); dy_knob(675, 144, 22, 9, -95, 95, 0, 0, ORG, "FEEDBACK", ""); sprintf(#v, "%.0f deg", slider10); dy_knob(755, 144, 22, 10, 0, 180, 0, 0, ORG, "SPREAD", ""); ui_cola(INK, 0.12); gfx_rect(360 * sc, 230 * sc, 490 * sc, 1 * sc); rd_lbl(440, 238, "DRIFT", ORG, 1); rd_lbl(730, 238, "IN / OUT", ORG, 1); ui_cola(INK, 0.12); gfx_rect(530 * sc, 230 * sc, 1 * sc, 110 * sc); sprintf(#v, "%.0f%%", slider27); dy_knob(440, 285, 17, 27, 0, 100, 50, 0, ORG, "DRIFT", ""); ui_cola(INK, 0.12); gfx_rect(866 * sc, 84 * sc, 1 * sc, 256 * sc); sprintf(#v, "%.0f%%", slider11); dy_knob(904, 122, 18, 11, 0, 100, 50, 0, ORG, "MIX", ""); slider26 <= 20.5 ? strcpy(#v, "off") : sprintf(#v, "%.0f Hz", slider26); dy_kf = 1; dy_knob(970, 122, 15, 26, 20, 500, 20, 1, ORG, "LOW CUT", ""); dy_kst = 0; sprintf(#v, "%+.1f dB", slider16); dy_knob(904, 224, 18, 16, -24, 12, 0, 0, ORG, "OUTPUT", ""); mL.ui_ledm(962 * sc, 190 * sc, 9 * sc, 68 * sc, pkL); mR.ui_ledm(976 * sc, 190 * sc, 9 * sc, 68 * sc, pkR); pkL *= 0.85; pkR *= 0.85; rd_lbl(976, 264, "OUT", DIM, 1); rd_io(620, 256, 24, 25, 18, 23); pk_end(); ui_end();