desc:GFX E-Delay //tags: delay echo multi-tap ducking pitch time-stretch ping-pong european digital //author: JGWizrad / Garden FX (GFX) // // Requires gfx-bdelay, gfx-bverb, gfx-drift, gfx-input, gfx-output, gfx-ui, gfx-panel and gfx-lozenge (.jsfx-inc). // // E-Delay: four European studio and broadcast delays of the 1980s - spatial time design, pristine repeats, // transformer-coupled inputs. The MODEL keys change the algorithm. // CRYSTAL - an ultra-clean dual delay: feedback filters and a cross-feed matrix (ping-pong). Changing the // time crossfades to the new one - the pitch never bends. // DYNAMIC - the same line with a ducker: the repeats dip while you play and bloom back when you stop. // MULTI-TAP - up to 8 taps across the time, alternating sides, each gently chorused (MOD DEPTH / RATE). // TIME STRETCH - the feedback runs through a pitch shifter (PITCH, -12 to +12 semitones per repeat) and a soft // limiter: endless, rising or falling repeats that never run away. // TIME L / R in ms, or SYNC to the project tempo (straight, dotted, triplet). DUCK works on every model. // VINTAGE runs the line through the converters of the era. // DRIFT: the clock is not quite steady. The DRIFT display is the one drift dial: it scales the clock wander // (50% = the Drift / Drift speed settings, 100% = twice, 0 = none) and speeds it up with it. // IN / OUT: TRIM (with the GUARD key), LEVEL, SAT and the OUT stage (OFF / CLIP / LIMIT) on the panel. import gfx-drift.jsfx-inc import gfx-input.jsfx-inc import gfx-output.jsfx-inc import gfx-ui.jsfx-inc import gfx-panel.jsfx-inc import gfx-lozenge.jsfx-inc import gfx-bverb.jsfx-inc import gfx-bdelay.jsfx-inc slider1:0<0,3,1{Crystal,Dynamic,Multi-Tap,Time Stretch}>-Model slider2:375<1,2700,1>-Time L (ms) slider3:500<1,2700,1>-Time R (ms) slider4:0<0,1,1{Off,On}>-Sync to tempo slider5:8<0,13,1{1/1,1/2,1/2D,1/2T,1/4,1/4D,1/4T,1/8,1/8D,1/8T,1/16,1/16D,1/16T,1/32}>-Division L slider6:4<0,13,1{1/1,1/2,1/2D,1/2T,1/4,1/4D,1/4T,1/8,1/8D,1/8T,1/16,1/16D,1/16T,1/32}>-Division R slider7:35<0,99,1>-Feedback (%) slider8:0<0,100,1>-Cross-feed (%) slider9:80<20,1000,1>-Lo cut (Hz) slider10:9000<1000,20000,10>-Hi cut (Hz) slider11:15<0,100,1>-Drive (%) slider12:0<0,10,0.05>-Mod depth (ms) slider13:0.8<0.05,10,0.01>-Mod rate (Hz) slider14:0<-12,12,1>-Pitch (semitones) slider15:0<0,100,1>-Duck depth (%) slider16:400<20,2000,1>-Duck recovery (ms) slider17:6<1,8,1>-Taps slider18:0<0,1,1{Modern,Vintage}>-Clock slider19:3<0,3,1{10,12,14,16}>-Bits (vintage) slider20:100<0,150,1>-Width (%) slider21:20<0,100,1>-Drift amount (%) slider22:0.2<0.02,2,0.01>-Drift speed (Hz) slider23:30<0,100,1>-Mix (%) slider24:0<-24,12,0.1>-Output (dB) slider25:0<0,100,1>-Band sat (%) slider26:30<0,100,1>-Bias (%) slider27:120<40,400,1>-Sat low band (Hz) slider28:6000<2000,16000,10>-Sat high band (Hz) slider29:-0.3<-24,0,0.1>-Ceiling / limit threshold (dB) slider30:1<0,2,1{Off,Clip,Limit}>-Output stage slider31:0<-24,12,0.1>-Input trim (dB) slider32:0<0,1,1{Off,On}>-Input guard // DRIFT dial: scales the delay-clock wander (sliders 21-22, read in @block); 50 % = as set. slider33:50<0,100,1>-Drift (%) in_pin:left input in_pin:right input out_pin:left output out_pin:right output options:maxmem=4194304 @init #ui_man = "gfx-e-delay"; JDL = 0; // 0 = E-Delay, 1 = J-Delay (shared code below) bd_alloc(100000); xh_rate = 1 / (0.03 * srate); sl_c = 1 - exp(-1 / (0.09 * srate)); cmp_c = 1 - exp(-1 / (0.02 * srate)); ps_win = floor(0.05 * srate); function update() local(bt) ( mdl = slider1; bt = 60 / max(tempo, 20); tLs = (slider4 ? bd_beats(slider5) * bt * 1000 : slider2) * 0.001 * srate; tRs = (slider4 ? bd_beats(slider6) * bt * 1000 : slider3) * 0.001 * srate; tLs = min(max(tLs, 2), DL_N - 64); tRs = min(max(tRs, 2), DL_N - 64); fb = slider7 / 100; xf = slider8 / 100; lc_c = 1 - exp(-2 * $pi * slider9 / srate); hc_c = 1 - exp(-2 * $pi * min(JDL && mdl == 2 ? min(slider10, 8000) : slider10, srate * 0.45) / srate); drv = slider11 / 100; md_s = slider12 * 0.001 * srate; md_inc = slider13 / srate; ratio = 2 ^ (slider14 / 12); dk_d = slider15 / 100; dk_r = 1 - exp(-1 / (slider16 * 0.001 * srate)); ntap = slider17; hold = JDL && slider17; lofi_set(slider18 || (JDL && mdl == 1), JDL ? (mdl == 0 ? 36000 : mdl == 1 ? 32000 : mdl == 2 ? 25000 : 32000) : (mdl == 0 ? 48000 : mdl == 3 ? 40000 : 32000), JDL ? (slider19 == 0 ? 10 : slider19 == 1 ? 12 : slider19 == 2 ? 14 : 16) : 16); bd_near = lf_on && JDL; drift_macro(slider33); wid = slider20 / 100; dft = slider21 / 100 * dm_a; dspd = slider22 * dm_s; mix_t = slider23 / 100; out_t = 10 ^ (slider24 / 20); ost_setup(slider25 / 100, slider26 / 100, slider27, slider28, slider29, slider30); in_setup(slider31, slider32); ); // ---------- presets ---------- function setp(m, tl, tr, sy, dl, dr, fbk, xfd, lc, hc, md, mr, pt, dk, dr_, tp, mx) ( slider1 = m; slider2 = tl; slider3 = tr; slider4 = sy; slider5 = dl; slider6 = dr; slider7 = fbk; slider8 = xfd; slider9 = lc; slider10 = hc; slider12 = md; slider13 = mr; slider14 = pt; slider15 = dk; slider16 = dr_; slider17 = tp; slider23 = mx; ); function load_preset(p) ( p == 1 ? setp(0, 95, 110, 0, 8, 4, 10, 0, 120, 7000, 0, 0.8, 0, 0, 400, 6, 25) : // Slapback p == 2 ? setp(0, 375, 500, 1, 4, 4, 35, 0, 100, 8000, 0, 0.8, 0, 0, 400, 6, 25) : // Quarter Note Echo p == 3 ? setp(0, 250, 500, 1, 7, 4, 45, 70, 150, 7000, 0, 0.8, 0, 0, 400, 6, 30) : // Ping-Pong Eighths p == 4 ? setp(1, 375, 500, 1, 8, 4, 40, 20, 150, 9000, 0, 0.8, 0, 70, 500, 6, 35) : // Dynamic Vocal Echo p == 5 ? setp(0, 17, 29, 0, 8, 4, 0, 0, 120, 14000, 0, 0.8, 0, 0, 400, 6, 40) : // Wide Stereo Doubler p == 6 ? setp(2, 600, 600, 1, 4, 4, 25, 0, 150, 9000, 0.4, 0.6, 0, 0, 400, 6, 30) : // Rhythm Taps p == 7 ? setp(2, 120, 120, 0, 8, 4, 10, 0, 120, 12000, 3, 0.7, 0, 0, 400, 4, 40) : // Tap Chorus p == 8 ? setp(3, 400, 460, 0, 8, 4, 70, 30, 200, 10000, 0, 0.8, 12, 30, 600, 6, 30) : // Shimmer Up p == 9 ? setp(3, 330, 360, 0, 8, 4, 70, 30, 120, 7000, 0, 0.8, -5, 0, 400, 6, 30) : // Falling Echoes p == 10 ? setp(3, 700, 760, 0, 8, 4, 97, 40, 200, 6000, 0, 0.8, 0, 0, 400, 6, 40); // Endless Wash p >= 1 && p <= 10 ? ( lastp = p; i = 1; loop(17, ui_auto(i); i += 1; ); ui_auto(23); ); p == 11 ? ( setp(0, 375, 500, 0, 8, 4, 35, 0, 80, 9000, 0, 0.8, 0, 0, 400, 6, 30); slider11 = 15; slider18 = 0; slider19 = 3; slider20 = 100; slider21 = 20; slider22 = 0.2; slider24 = 0; slider25 = 0; slider26 = 30; slider27 = 120; slider28 = 6000; slider29 = -0.3; slider30 = 1; slider31 = 0; slider32 = 0; slider33 = 50; lastp = 0; i = 1; loop(33, ui_auto(i); i += 1; ); ); ); // ---------- the parameter table ---------- function pv_time() ( slider4 ? ( pv_def(0, 5, 0, 13, 1, 8, 0, 0); pv_def(1, 6, 0, 13, 1, 4, 0, 0); ) : ( pv_def(0, 2, 1, 2700, 1, 375, 1, 0); pv_def(1, 3, 1, 2700, 1, 500, 1, 0); ); ); pv_time(); pv_def(2, 7, 0, 99, 1, 35, 0, 0); pv_def(3, 8, 0, 100, 1, 0, 0, 0); pv_def(4, 9, 20, 1000, 1, 80, 1, 0); pv_def(5, 10, 1000, 20000, 10, 9000, 1, 1); pv_def(6, 11, 0, 100, 1, 15, 0, 0); pv_def(7, 12, 0, 10, 0.05, 0, 0, 2); pv_def(8, 13, 0.05, 10, 0.01, 0.8, 1, 2); pv_def(9, 14, -12, 12, 1, 0, 0, 0); pv_def(10, 15, 0, 100, 1, 0, 0, 0); pv_def(11, 16, 20, 2000, 1, 400, 1, 0); pv_def(12, 17, 1, 8, 1, 6, 0, 0); pv_def(13, 20, 0, 150, 1, 100, 0, 0); pv_def(14, 33, 0, 100, 1, 50, 0, 0); pv_def(15, 23, 0, 100, 1, 30, 0, 0); pv_def(16, 31, -24, 12, 0.1, 0, 0, 1); pv_def(17, 24, -24, 12, 0.1, 0, 0, 1); pv_def(18, 22, 0.02, 2, 0.01, 0.2, 1, 2); pv_def(19, 25, 0, 100, 1, 0, 0, 0); pv_def(20, 26, 0, 100, 1, 30, 0, 0); pv_def(21, 27, 40, 400, 1, 120, 1, 0); pv_def(22, 28, 2000, 16000, 10, 6000, 1, 1); pv_def(23, 29, -24, 0, 0.1, -0.3, 0, 1); function pv_text(k) local(v) ( v = pv_get(k); (k == 0 || k == 1) && slider4 ? strcpy(#pt, bd_divname(v)) : k == 5 || k == 22 ? sprintf(#pt, "%.1fk", v / 1000) : k == 7 || k == 8 || k == 18 ? sprintf(#pt, "%.2f", v) : k == 9 ? sprintf(#pt, v > 0 ? "+%d" : "%d", v) : k == 16 || k == 17 || k == 23 ? sprintf(#pt, "%.1f", v) : sprintf(#pt, "%d", floor(v + 0.5)); #pt; ); vsel = 0; drA.drift_init(0.8 + rand(0.5)); drB.drift_init(0.8 + rand(0.5)); ost_init(20000); ostL.ost_ch_init(); ostR.ost_ch_init(); in_init(); sm = 1 - exp(-1 / (0.03 * srate)); dk_a = 1 - exp(-1 / (0.005 * srate)); pkL = pkR = 0; piL = piR = 0; dk_e = 0; dk_g = 1; lph = 0; update(); mix_s = mix_t; out_s = out_t; pL = tLs; pR = tRs; @slider update(); @block // panel edits change the sliders directly, and the tempo can move: re-apply when anything changed chk = tempo * 0.731; ci = 1; loop(33, chk += slider(ci) * (ci * 1.37 + 1); ci += 1; ); chk != last_chk ? ( last_chk = chk; update(); ); vA = drA.drift_tick(dspd * samplesblock / srate); vB = drB.drift_tick(dspd * samplesblock / srate); // drift: the clock is not quite steady - a fraction of a millisecond (E) or more (J) dof_t = dft * (JDL ? 0.0015 : 0.0003) * srate; @serialize file_var(0, ui_sz); file_var(0, vsel); file_var(0, lastp); @sample in_process(spl0, spl1); inL = in_l; inR = in_r; piL = max(piL, abs(inL)); piR = max(piR, abs(inR)); mix_s += (mix_t - mix_s) * sm; out_s += (out_t - out_s) * sm; dofL += (dof_t * vA - dofL) * 0.0005; dofR += (dof_t * vB - dofR) * 0.0005; xl = xfL.xfm_run(inL, drv, !JDL); xr = xfR.xfm_run(inR, drv, !JDL); // ducker: follows the input, pulls the repeats down (up to 18 dB), lets them bloom back at DUCK RECOVERY dk_x = max(abs(xl), abs(xr)); dk_e += (dk_x - dk_e) * (dk_x > dk_e ? dk_a : dk_r); dk_t = 1 - dk_d * 0.875 * min(dk_e * 6, 1); dk_g += (dk_t - dk_g) * 0.002; lph += md_inc; lph >= 1 ? lph -= 1; !JDL ? ( // ---------- E-Delay ---------- mdl == 2 ? ( // multi-tap: taps spread across the longer time, alternating sides, chorused; feedback from the last tT = max(tLs, tRs); yl = yr = 0; k = 0; loop(ntap, t_ = tT * (k + 1) / ntap + md_s * (0.5 + 0.5 * sin(2 * $pi * (lph + k * 0.37))); g_ = 1 - 0.55 * k / max(ntap, 1); k & 1 ? yr += g_ * bd_rd(BD_R, t_ + dofR) : yl += g_ * bd_rd(BD_L, t_ + dofL); ntap == 1 ? yr = yl; k += 1; ); lastL = bd_rd(BD_L, tT + dofL); lastR = bd_rd(BD_R, tT + dofR); lcL += (lastL - lcL) * lc_c; hcL += (lastL - lcL - hcL) * hc_c; lcR += (lastR - lcR) * lc_c; hcR += (lastR - lcR - hcR) * hc_c; wl = xl + fb * ((1 - xf) * hcL + xf * hcR); wr = xr + fb * ((1 - xf) * hcR + xf * hcL); ) : ( yl = hdL.xh_run(BD_L, tLs + dofL); yr = hdR.xh_run(BD_R, tRs + dofR); lcL += (yl - lcL) * lc_c; hcL += (yl - lcL - hcL) * hc_c; lcR += (yr - lcR) * lc_c; hcR += (yr - lcR - hcR) * hc_c; mdl == 3 ? ( // time stretch: the repeats are pitch-shifted and softly limited inside the loop fl = psL.ps_run((1 - xf) * hcL + xf * hcR, ratio, 0); fr = psR.ps_run((1 - xf) * hcR + xf * hcL, ratio, 1); wl = xl + bv_tanh(fb * 1.01 * fl * 1.2) / 1.2; wr = xr + bv_tanh(fb * 1.01 * fr * 1.2) / 1.2; ) : ( wl = xl + fb * ((1 - xf) * hcL + xf * hcR); wr = xr + fb * ((1 - xf) * hcR + xf * hcL); wl = wl - 0.05 * wl * wl * wl; wr = wr - 0.05 * wr * wr * wr; // a little headroom curve in the loop ); yl = hcL; yr = hcR; ); ) : ( // ---------- J-Delay ---------- // the base time glides (pitch bends when you change it); the LFO rides on top (dual-phase: L sine, R cosine) pL += (tLs - pL) * sl_c; pR += (tRs - pR) * sl_c; mdl == 3 && hold ? ( // sample hold: nothing new is written; the last TIME L of audio plays round and round hph += 1; hph >= tLs ? hph = 0; env_ = min(1, min(hph, tLs - hph) / (0.004 * srate)); yl = bd_rd(BD_L, tLs - hph) * env_; yr = bd_rd(BD_R, tLs - hph) * env_; wl = wr = 0; ) : ( mL = md_s * (0.5 + 0.5 * sin(2 * $pi * lph)); mR = md_s * (0.5 + 0.5 * cos(2 * $pi * lph)); yl = bd_rd(BD_L, pL + mL + dofL); yr = bd_rd(BD_R, pR + mR + dofR); mdl == 2 ? ( yl = cpoL.cmp_out(yl); yr = cpoR.cmp_out(yr); ); lcL += (yl - lcL) * lc_c; hcL += (yl - lcL - hcL) * hc_c; lcR += (yr - lcR) * lc_c; hcR += (yr - lcR - hcR) * hc_c; fbl = (1 - xf) * hcL + xf * hcR; fbr = (1 - xf) * hcR + xf * hcL; mdl == 1 ? ( fbl = psL.ps_run(fbl, ratio, 0); fbr = psR.ps_run(fbr, ratio, 1); ); // every repeat shifts in pitch wl = xl + fb * fbl; wr = xr + fb * fbr; wl = wl - 0.06 * wl * wl * wl; wr = wr - 0.06 * wr * wr * wr; mdl == 2 ? ( nz = (rand(2) - 1) * 0.00004; wl = cpiL.cmp_in(wl + nz); wr = cpiR.cmp_in(wr - nz); ); // compander (with its hiss) yl = hcL; yr = hcR; hph = 0; ); ); !(JDL && mdl == 3 && hold) ? bd_put(lfiL.lofi_in(wl), lfiR.lofi_in(wr)); yl = lfoL.lofi_out(yl) * dk_g; yr = lfoR.lofi_out(yr) * dk_g; wm = 0.5 * (yl + yr); ws = 0.5 * (yl - yr) * wid; yl = wm + ws; yr = wm - ws; rvl = max(rvl, max(abs(yl), abs(yr))); oL = inL * (1 - mix_s) + yl * mix_s; oR = inR * (1 - mix_s) + yr * mix_s; oL = ostL.ost_band(oL) * out_s; oR = ostR.ost_band(oR) * out_s; ost_out(oL, oR); spl0 = ost_l; spl1 = ost_r; pkL = max(pkL, abs(spl0)); pkR = max(pkR, abs(spl1)); @gfx 1440 316 ui_begin(1440, 316); ui_thm = 5; ui_hw = 1; ui_theme(5); pv_time(); // brushed aluminium, a cream band, dark blue print; pale displays with dark blue characters; lit push buttons ALU = 0xCDCDCA; CRM = 0xE9E3D2; INK = 0x1C2A5C; WIN = 0xB9C9D6; CHR = 0x14246A; LIT = 0xFFB030; function txt(x, y, s, sz, a) ( gfx_setfont(6, "Arial", sz * sc, 'b'); ui_col(ui_mix(CRM, INK, a)); gfx_x = x * sc; gfx_y = y * sc; gfx_drawstr(s); ); function ctxt(x, y, s, sz, a) local(tw, th) ( gfx_setfont(6, "Arial", sz * sc, 'b'); gfx_measurestr(s, tw, th); ui_col(ui_mix(CRM, INK, a)); gfx_x = x * sc - tw * 0.5; gfx_y = y * sc; gfx_drawstr(s); ); function group(x, y, w, h, s) local(tw, th) ( ui_col(0x8E8C84); lz_rr(x, y, w, h, 6); ui_col(CRM); lz_rr(x + 1, y + 1, w - 2, h - 2, 5); gfx_setfont(6, "Arial", 10 * sc, 'b'); gfx_measurestr(s, tw, th); ui_col(INK); gfx_rect(x * sc, y * sc, tw + 16 * sc, 14 * sc); ui_col(CRM); gfx_x = (x + 8) * sc; gfx_y = (y + 2) * sc; gfx_drawstr(s); ); // an illuminated push button: square cap, the lamp lit behind its legend when on function lbtn(x, y, w, h, s, on) local(hit, inb, tw, th) ( inb = mouse_x >= x * sc && mouse_x < (x + w) * sc && mouse_y >= y * sc && mouse_y < (y + h) * sc; hit = pressed && drag_id == 0 && inb; hit ? drag_id = -1; ui_col(0x6A6862); lz_rr(x + 1, y + 2, w, h, 3); ui_col(0x2A2C30); lz_rr(x, y, w, h, 3); ui_col(on ? LIT : 0x5A5650); lz_rr(x + 3, y + 3, w - 6, h - 6, 2); on ? ( ui_col(ui_mix(LIT, 0xFFFFFF, 0.45)); gfx_rect((x + 5) * sc, (y + 4) * sc, (w - 10) * sc, 2 * sc); ); gfx_setfont(6, "Arial", 10 * sc, 'b'); gfx_measurestr(s, tw, th); ui_col(on ? 0x2A1A06 : 0xD8D4CA); gfx_x = (x + w * 0.5) * sc - tw * 0.5; gfx_y = (y + h * 0.5) * sc - th * 0.5; gfx_drawstr(s); hit; ); function key(x, y, w, h, s, rep) local(hit, inb, tw, th, id) ( id = 100000 + x * 1000 + y; inb = mouse_x >= x * sc && mouse_x < (x + w) * sc && mouse_y >= y * sc && mouse_y < (y + h) * sc; hit = pressed && drag_id == 0 && inb; hit ? ( drag_id = id; k_tn = time_precise() + 0.4; ); rep && drag_id == id && inb && time_precise() > k_tn ? ( hit = 1; k_tn = time_precise() + 0.07; ); ui_col(0x8A8780); lz_rr(x + 1, y + 2, w, h, 3); ui_col(0x3A3C42); lz_rr(x, y, w, h, 3); ui_col(drag_id == id && inb ? 0x55585E : 0x6A6E76); lz_rr(x + 1, y + 1, w - 2, h - 3, 3); gfx_setfont(6, "Arial", 15 * sc, 'b'); gfx_measurestr(s, tw, th); ui_col(0xF0F0F0); gfx_x = (x + w * 0.5) * sc - tw * 0.5; gfx_y = (y + h * 0.5 - 1) * sc - th * 0.5; gfx_drawstr(s); hit; ); // an alphanumeric display with - / + under it; act = the parameter does something on this model function disp(k, x, y, cap, unit, act) local(inb, did, tw, th, w) ( w = 94; did = 1900 + k; ctxt(x + w * 0.5, y, cap, 10, act ? 0.95 : 0.45); inb = mouse_x >= x * sc && mouse_x < (x + w) * sc && mouse_y >= (y + 14) * sc && mouse_y < (y + 52) * sc; pressed && inb && drag_id == 0 ? ( time_precise() - d_lt < 0.35 && d_lk == k ? ( pv_reset(k); drag_id = -1; ) : ( drag_id = did; d_y = mouse_y; d_lt = time_precise(); d_lk = k; ); vsel = k; ); drag_id == did && abs(mouse_y - d_y) >= 2 * sc ? ( pv_nudge(k, (d_y - mouse_y) / (((mouse_cap & 8) ? 1500 : 300) * sc)); d_y = mouse_y; ); inb && mouse_wheel != 0 && drag_id == 0 ? ( pv_step(k, sign(mouse_wheel)); mouse_wheel = 0; vsel = k; ); ui_col(vsel == k ? CHR : 0x5A6A78); lz_rr(x - 1, y + 13, w + 2, 40, 4); ui_col(act ? WIN : ui_mix(WIN, 0x707880, 0.4)); lz_rr(x, y + 14, w, 38, 3); ui_hw ? gfx_gradrect(x * sc, (y + 14) * sc, w * sc, 38 * sc, 1, 1, 1, 0.14, 0, 0, 0, 0, 0, 0, 0, -0.14 / (38 * sc)); gfx_setfont(6, "Courier New", 22 * sc, 'b'); gfx_measurestr(pv_text(k), tw, th); ui_col(act ? CHR : ui_mix(CHR, WIN, 0.55)); gfx_x = (x + w - 8) * sc - tw; gfx_y = (y + 33) * sc - th * 0.5; gfx_drawstr(#pt); gfx_setfont(6, "Arial", 9 * sc, 'b'); ui_col(ui_mix(WIN, CHR, 0.7)); gfx_x = (x + 4) * sc; gfx_y = (y + 17) * sc; gfx_drawstr(unit); key(x, y + 58, 45, 24, "-", 1) ? ( pv_step(k, -1); vsel = k; ); key(x + 49, y + 58, 45, 24, "+", 1) ? ( pv_step(k, 1); vsel = k; ); ); // the analog meter: cream face, black needle (v in dB, 0 VU = -18 dBFS) function vu(x, y, w, h, db, s) local(cx, cy, r, i, a, d, n) ( ui_col(0x2A2C30); lz_rr(x - 4, y - 4, w + 8, h + 8, 6); ui_col(0xF2E8C8); lz_rr(x, y, w, h, 3); ui_hw ? gfx_gradrect(x * sc, y * sc, w * sc, h * sc, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, -0.2 / (h * sc)); cx = x + w * 0.5; cy = y + h * 1.15; r = h * 0.95; i = 0; loop(9, d = i == 0 ? -20 : i == 1 ? -10 : i == 2 ? -7 : i == 3 ? -5 : i == 4 ? -3 : i == 5 ? -1 : i == 6 ? 0 : i == 7 ? 1 : 3; n = (d + 20) / 23; a = -0.62 + n * 1.24; ui_col(d > 0 ? 0xB82A1E : 0x2A1C10); gfx_line((cx + sin(a) * r) * sc, (cy - cos(a) * r) * sc, (cx + sin(a) * (r - 8)) * sc, (cy - cos(a) * (r - 8)) * sc, 1); i += 1; ); gfx_setfont(6, "Arial", 14 * sc, 'b'); ui_col(0x2A1C10); ui_text(cx * sc, (y + h * 0.42) * sc, "VU"); gfx_setfont(6, "Arial", 9 * sc, 'b'); gfx_x = (x + 6) * sc; gfx_y = (y + h - 14) * sc; gfx_drawstr(s); n = min(max((db + 20) / 23, -0.05), 1.06); a = -0.62 + n * 1.24; ui_col(0x141414); gfx_line(cx * sc, (cy - h * 0.3) * sc, (cx + sin(a) * (r - 3)) * sc, (cy - cos(a) * (r - 3)) * sc, 1); gfx_line(cx * sc + 1, (cy - h * 0.3) * sc, (cx + sin(a) * (r - 3)) * sc + 1, (cy - cos(a) * (r - 3)) * sc, 1); ui_col(0x2A2C30); gfx_rect(x * sc, (y + h - 5) * sc, w * sc, 5 * sc); ); // ---------- the panel ---------- fh = 308; ui_col(0x0A0A0B); gfx_rect(0, 0, gfx_w, gfx_h); ui_col(0x7A7A76); lz_rr(4, 4, 1432, fh, 8); ui_col(ALU); lz_rr(5, 5, 1430, fh - 2, 7); gi = 0; loop(floor(fh / 2), ui_col(ui_mix(ALU, gi % 3 ? 0xFFFFFF : 0x000000, gi % 2 ? 0.05 : 0.07)); gfx_rect(8 * sc, (6 + gi * 2) * sc, 1424 * sc, 1); gi += 1; ); gi = 0; loop(4, pk_screw((gi % 2 ? 1422 : 18) * sc, (gi < 2 ? 18 : fh - 10) * sc); gi += 1; ); // header ui_col(INK); gfx_circle(48 * sc, 26 * sc, 13 * sc, 1, 1); gfx_setfont(4); ui_col(ALU); ui_text(48 * sc, 21 * sc, "GFX"); gfx_setfont(6, "Arial", 24 * sc, 'bi'); ui_col(INK); gfx_x = 70 * sc; gfx_y = 12 * sc; gfx_drawstr("E-DELAY"); gfx_setfont(6, "Arial", 11 * sc, 'b'); ui_col(ui_mix(ALU, INK, 0.75)); gfx_x = 186 * sc; gfx_y = 21 * sc; gfx_drawstr(mdl == 0 ? "DIGITAL DELAY LINE - CRYSTAL" : mdl == 1 ? "DYNAMIC DELAY - DUCKING" : mdl == 2 ? "STEREO MULTI-TAP DELAY" : "AUDIO TIME PROCESSOR - PITCH LOOP"); ui_size_picker(1360 * sc, 14 * sc); // ---------- meters, models, keys ---------- group(16, 50, 290, 244, "DELAY PROCESSOR"); vdi = 20 * log10(max(max(piL, piR), 0.000001)) + 18; vin += (vdi - vin) * 0.3; vde = 20 * log10(max(rvl, 0.000001)) + 18; vec += (vde - vec) * 0.3; rvl = 0; piL *= 0.8; piR *= 0.8; vu(28, 74, 126, 70, vin, "INPUT"); vu(168, 74, 126, 70, vec, "ECHO"); lbtn(28, 156, 128, 30, "CRYSTAL", mdl == 0) ? ( slider1 = 0; ui_auto(1); update(); ); lbtn(166, 156, 128, 30, "DYNAMIC", mdl == 1) ? ( slider1 = 1; slider15 < 1 ? ( slider15 = 60; ui_auto(15); ); ui_auto(1); update(); ); lbtn(28, 192, 128, 30, "MULTI-TAP", mdl == 2) ? ( slider1 = 2; ui_auto(1); update(); ); lbtn(166, 192, 128, 30, "TIME STRETCH", mdl == 3) ? ( slider1 = 3; ui_auto(1); update(); ); lbtn(28, 230, 128, 26, "SYNC", slider4) ? ( slider4 = 1 - slider4; ui_auto(4); update(); ); lbtn(166, 230, 128, 26, "VINTAGE", slider18) ? ( slider18 = 1 - slider18; ui_auto(18); update(); ); lbtn(28, 262, 84, 26, "PRESETS", 0) ? ( gfx_x = 28 * sc; gfx_y = 288 * sc; load_preset(gfx_showmenu("Slapback|Quarter Note Echo|Ping-Pong Eighths|Dynamic Vocal Echo|Wide Stereo Doubler|Rhythm Taps|Tap Chorus|Shimmer Up|Falling Echoes|Endless Wash|Reset all to defaults")); ); // prev / next beside PRESETS: step through the ten presets (wrapping round) lbtn(116, 262, 28, 26, "", 0) ? load_preset(lastp <= 1 ? 10 : lastp - 1); lbtn(146, 262, 28, 26, "", 0) ? load_preset(lastp >= 10 ? 1 : lastp + 1); ui_col(0xD8D4CA); gfx_triangle(134.5 * sc, 269 * sc, 134.5 * sc, 281 * sc, 125.5 * sc, 275 * sc); gfx_triangle(155.5 * sc, 269 * sc, 155.5 * sc, 281 * sc, 164.5 * sc, 275 * sc); // the arrows ui_col(WIN); lz_rr(180, 262, 114, 26, 3); gfx_setfont(6, "Courier New", 15 * sc, 'b'); ui_col(CHR); gfx_x = 187 * sc; gfx_y = 267 * sc; lastp > 0 ? gfx_printf("P%d %s", lastp, slider4 ? "SYNC" : "FREE") : gfx_drawstr(slider4 ? "P-- SYNC" : "P-- FREE"); txt(196, 34, slider4 ? sprintf(#tb, "TEMPO %.1f BPM", tempo) : "TIME IN MILLISECONDS", 9, 0.7); // ---------- the displays ---------- group(316, 50, 210, 116, "TIME"); disp(0, 324, 70, "TIME L", slider4 ? "NOTE" : "ms", 1); disp(1, 424, 70, "TIME R", slider4 ? "NOTE" : "ms", 1); group(534, 50, 410, 116, "FEEDBACK"); disp(2, 542, 70, "FEEDBACK", "%", 1); disp(3, 642, 70, "CROSS-FEED", "%", 1); disp(4, 742, 70, "LO CUT", "Hz", 1); disp(5, 842, 70, "HI CUT", "Hz", 1); group(952, 50, 232, 116, "DUCK"); disp(10, 966, 70, "DEPTH", "%", 1); disp(11, 1076, 70, "RECOVERY", "ms", 1); group(316, 178, 410, 116, "MODULATION"); disp(7, 324, 198, "MOD DEPTH", "ms", mdl == 2); disp(8, 424, 198, "MOD RATE", "Hz", mdl == 2); disp(9, 524, 198, "PITCH", "semi", mdl == 3); disp(12, 624, 198, "TAPS", "", mdl == 2); group(734, 178, 450, 116, "CHARACTER"); disp(6, 748, 198, "DRIVE", "%", 1); disp(13, 856, 198, "WIDTH", "%", 1); disp(14, 964, 198, "DRIFT", "%", 1); disp(15, 1076, 198, "MIX", "%", 1); // ---------- IN / OUT: trim (+ guard), saturation, the output stage ---------- group(1192, 50, 232, 244, "IN / OUT"); disp(16, 1206, 70, "TRIM", "dB", 1); disp(17, 1312, 70, "LEVEL", "dB", 1); lbtn(1312, 166, 98, 24, "GUARD", slider32) ? ( slider32 = 1 - slider32; ui_auto(32); update(); ); io_grd = in_grd > 0.05 ? 1 : io_grd * 0.85; in_grd = 0; ctxt(1259, 176, io_grd > 0.1 && slider32 ? "GUARD CATCHING" : "", 9, 0.7); disp(19, 1206, 198, "SAT", "%", 1); io_clp = ost_clipped ? 1 : io_clp * 0.9; ost_clipped = 0; io_clp > 0.1 ? ( gfx_setfont(6, "Arial", 10 * sc, 'b'); gfx_measurestr("CLIP", tw_, th_); ui_col(0xC8321E); gfx_x = 1361 * sc - tw_ * 0.5; gfx_y = 198 * sc; gfx_drawstr("CLIP"); ) : ctxt(1361, 198, "OUT", 10, 0.95); lbtn(1312, 212, 98, 24, "OFF", slider30 == 0) ? ( slider30 = 0; ui_auto(30); update(); ); lbtn(1312, 238, 98, 24, "CLIP", slider30 == 1) ? ( slider30 = 1; ui_auto(30); update(); ); lbtn(1312, 264, 98, 24, "LIMIT", slider30 == 2) ? ( slider30 = 2; ui_auto(30); update(); ); pk_end(); ui_end();