desc:GFX Stage 8 //tags: summing mixer theatre console vca groups transformer stems //author: JGWizrad / Garden FX (GFX) // // Requires gfx-drift, gfx-output, gfx-ui, gfx-panel, gfx-instrument and gfx-dyn (.jsfx-inc) in the same folder. // // A big British theatre console's mix bus as a summing mixer, in the spirit of the legendary West End and // Broadway consoles famous for their clarity (personal use; the sound is designed from scratch). // // SET-UP (REAPER): set the bus track to 16 channels and send each stem to its own pair (1/2, 3/4 ... 15/16; // turn off the stems' master send). The mix leaves on 1/2. // // MODE VINTAGE - the earlier, transformer-coupled console: the lows thicken and saturate gently as the // level rises, a soft lift in the presence region, a silky top that rolls off just above // the audio band, a whisper of crosstalk and noise, and a gentle squeeze when driven. // MODERN - the later transformerless console: flat, wide, true stereo, enormous headroom. // DRIVE - level into the summing amps (level-matched: it changes the colour, not the volume). // STEMS - LEVEL, PAN (balance), VCA (none / A / B: the stem follows that VCA master), SOLO, MUTE. // VCA A / B - group masters: one knob rides every stem assigned to it; each has a MUTE. // MASTER level, 12-LED meters (0 = -18 dBFS). // PRESETS. // NAMES - click the name at the top of a channel strip and type (e.g. Drums); Enter keeps it, Escape cancels, // an empty name goes back to the number. The names are saved with the project. import gfx-drift.jsfx-inc import gfx-output.jsfx-inc import gfx-ui.jsfx-inc import gfx-panel.jsfx-inc import gfx-instrument.jsfx-inc import gfx-dyn.jsfx-inc slider1:0<0,1,1{Vintage,Modern}>-Mode slider2:0<-6,12,0.1>-Drive (dB) slider3:0<-60,10,0.1>-Master (dB) slider4:0<-60,10,0.1>-VCA A (dB) slider5:0<-60,10,0.1>-VCA B (dB) slider6:0<0,1,1{Off,On}>-VCA A mute slider7:0<0,1,1{Off,On}>-VCA B mute slider8:0<-60,10,0.1>-Stem 1 level (dB) slider9:0<-60,10,0.1>-Stem 2 level (dB) slider10:0<-60,10,0.1>-Stem 3 level (dB) slider11:0<-60,10,0.1>-Stem 4 level (dB) slider12:0<-60,10,0.1>-Stem 5 level (dB) slider13:0<-60,10,0.1>-Stem 6 level (dB) slider14:0<-60,10,0.1>-Stem 7 level (dB) slider15:0<-60,10,0.1>-Stem 8 level (dB) slider16:0<-100,100,1>-Stem 1 pan slider17:0<-100,100,1>-Stem 2 pan slider18:0<-100,100,1>-Stem 3 pan slider19:0<-100,100,1>-Stem 4 pan slider20:0<-100,100,1>-Stem 5 pan slider21:0<-100,100,1>-Stem 6 pan slider22:0<-100,100,1>-Stem 7 pan slider23:0<-100,100,1>-Stem 8 pan slider24:0<0,2,1{None,A,B}>-Stem 1 VCA slider25:0<0,2,1{None,A,B}>-Stem 2 VCA slider26:0<0,2,1{None,A,B}>-Stem 3 VCA slider27:0<0,2,1{None,A,B}>-Stem 4 VCA slider28:0<0,2,1{None,A,B}>-Stem 5 VCA slider29:0<0,2,1{None,A,B}>-Stem 6 VCA slider30:0<0,2,1{None,A,B}>-Stem 7 VCA slider31:0<0,2,1{None,A,B}>-Stem 8 VCA slider32:0<0,1,1{Off,On}>-Stem 1 mute slider33:0<0,1,1{Off,On}>-Stem 2 mute slider34:0<0,1,1{Off,On}>-Stem 3 mute slider35:0<0,1,1{Off,On}>-Stem 4 mute slider36:0<0,1,1{Off,On}>-Stem 5 mute slider37:0<0,1,1{Off,On}>-Stem 6 mute slider38:0<0,1,1{Off,On}>-Stem 7 mute slider39:0<0,1,1{Off,On}>-Stem 8 mute slider40:0<0,1,1{Off,On}>-Stem 1 solo slider41:0<0,1,1{Off,On}>-Stem 2 solo slider42:0<0,1,1{Off,On}>-Stem 3 solo slider43:0<0,1,1{Off,On}>-Stem 4 solo slider44:0<0,1,1{Off,On}>-Stem 5 solo slider45:0<0,1,1{Off,On}>-Stem 6 solo slider46:0<0,1,1{Off,On}>-Stem 7 solo slider47:0<0,1,1{Off,On}>-Stem 8 solo slider48:0<0,100,1>-Band sat (%) slider49:30<0,100,1>-Bias (%) slider50:120<40,400,1>-Sat low band (Hz) slider51:6000<2000,16000,10>-Sat high band (Hz) slider52:-0.3<-24,0,0.1>-Ceiling / limit threshold (dB) slider53:1<0,2,1{Off,Clip,Limit}>-Output stage in_pin:stem 1 L in_pin:stem 1 R in_pin:stem 2 L in_pin:stem 2 R in_pin:stem 3 L in_pin:stem 3 R in_pin:stem 4 L in_pin:stem 4 R in_pin:stem 5 L in_pin:stem 5 R in_pin:stem 6 L in_pin:stem 6 R in_pin:stem 7 L in_pin:stem 7 R in_pin:stem 8 L in_pin:stem 8 R out_pin:mix L out_pin:mix R @init #ui_man = "gfx-stage-8"; // the ? pop-up links to this manual LHD = 180; // signal-light hold per input (gfx) DEN = 0.00000000000000000001; SG = 100; SGT = 110; SPK = 120; PGL = 130; PGR = 140; // per stem: gain, target, peak, pan L / R memset(100, 0, 50); // the vintage bus: transformer iron in the lows, presence lift, silky top, gentle squeeze function vb_set() instance(pr, tp) ( pr.bq_set(2, 1500, 0.5, 0.5); tp.bq_set(0, 19000, 0.6, 0); ); function vbus(x) instance(lo, pr, tp) local(s) ( lo += (x - lo) * lo_c + DEN; s = ost_tanh(lo * 2.2) / 2.2; // iron: level-neutral when quiet x = pr.bq_run(x - lo + s); x = ost_tanh(x * 0.8) / 0.8; // a gentle squeeze as it is driven tp.bq_run(x); ); function update() local(i, any, a, g) ( modern = slider1; drv = 10 ^ (slider2 / 20); mst = slider3 <= -59.9 ? 0 : 10 ^ (slider3 / 20); vA = slider6 || slider4 <= -59.9 ? 0 : 10 ^ (slider4 / 20); vB = slider7 || slider5 <= -59.9 ? 0 : 10 ^ (slider5 / 20); bl.vb_set(); br.vb_set(); any = 0; i = 0; loop(8, any |= slider(40 + i); i += 1; ); i = 0; loop(8, g = slider(32 + i) || (any && !slider(40 + i)) || slider(8 + i) <= -59.9 ? 0 : 10 ^ (slider(8 + i) / 20); SGT[i] = g * (slider(24 + i) == 1 ? vA : slider(24 + i) == 2 ? vB : 1); a = (slider(16 + i) + 100) / 200; PGL[i] = min(1, 2 * (1 - a)); PGR[i] = min(1, 2 * a); i += 1; ); ost_setup(0, 0, 120, 6000, -0.3, 0); // no GFX output stage on this unit: its own character does that job ); // ---------- presets (stems and VCAs are left alone) ---------- function load_preset(p) ( p == 1 ? ( slider1 = 0; slider2 = 0; ) : // Vintage p == 2 ? ( slider1 = 0; slider2 = 6; ) : // Vintage, driven p == 3 ? ( slider1 = 0; slider2 = 10; ) : // Vintage, pushed hard p == 4 ? ( slider1 = 1; slider2 = 0; ) : // Modern p == 5 ? ( slider1 = 1; slider2 = 6; ); // Modern, driven p >= 1 && p <= 5 ? slider_automate(3); p == 6 ? ( // Reset all to defaults slider1 = 0; slider2 = 0; slider3 = 0; slider4 = 0; slider5 = 0; slider6 = 0; slider7 = 0; i = 0; loop(8, slider(8 + i) = 0; slider(16 + i) = 0; slider(24 + i) = 0; slider(32 + i) = 0; slider(40 + i) = 0; i += 1; ); slider48 = 0; slider49 = 30; slider50 = 120; slider51 = 6000; slider52 = -0.3; slider53 = 1; i = 1; loop(53, ui_auto(i); i += 1; ); ); ); ost_init(20000); ostL.ost_ch_init(); ostR.ost_ch_init(); sm = 1 - exp(-1 / (0.02 * srate)); lo_c = 1 - exp(-2 * $pi * 120 / srate); xt = 10 ^ (-55 / 20); nz_g = 10 ^ (-92 / 20); !ui_dflt ? ui_thm = 5; !ui_dflt ? ui_hw = 1; PNL = 0x25303C; INK = 0xE6EAF0; CYA = 0x40B8E0; ORA = 0xF0A030; CAPG = 0xC8CED6; update(); i = 0; loop(8, SG[i] = SGT[i]; i += 1; ); ui_dflt = 1; // theme defaults above apply once: @init re-runs (playback start, device reset) must not undo the saved theme @slider update(); @block update(); @serialize file_var(0, ui_sz); gn = 0; loop(8, file_avail(0) != 0 ? file_string(0, UI_NM + gn); gn += 1; ); // channel names (older projects have none) file_avail(0) != 0 ? file_var(0, pr_cur); // the preset in use (older projects end before it) @sample sl = 0; sr = 0; k = 0; loop(8, SG[k] += (SGT[k] - SG[k]) * sm; SPK[k] = max(SPK[k], max(abs(spl(2 * k)), abs(spl(2 * k + 1)))); sl += spl(2 * k) * SG[k] * PGL[k]; sr += spl(2 * k + 1) * SG[k] * PGR[k]; k += 1; ); sl *= drv; sr *= drv; modern ? ( // transformerless: flat, and only the far rails ever touch it sl = abs(sl) < 1.6 ? sl : sign(sl) * (1.6 + 0.2 * ost_tanh((abs(sl) - 1.6) / 0.2)); sr = abs(sr) < 1.6 ? sr : sign(sr) * (1.6 + 0.2 * ost_tanh((abs(sr) - 1.6) / 0.2)); ) : ( t_ = sl; sl += sr * xt; sr += t_ * xt; // a whisper of crosstalk sl += (rand(2) - 1) * nz_g; sr += (rand(2) - 1) * nz_g; sl = bl.vbus(sl); sr = br.vbus(sr); ); sl *= mst / drv; sr *= mst / drv; sl = ostL.ost_band(sl); sr = ostR.ost_band(sr); ost_out(sl, sr); spl0 = ost_l; spl1 = ost_r; k = 2; loop(14, spl(k) = 0; k += 1; ); pkL = max(pkL, abs(spl0)); pkR = max(pkR, abs(spl1)); @gfx 900 360 ui_begin(900, ins_show_out ? 480 : 360); dy_ink = INK; dy_bg = PNL; dy_kst = 7; dy_nov = 1; // ---------- panel ---------- fh = ins_show_out ? 472 : 352; ui_col(0x090B0E); gfx_rect(0, 0, gfx_w, gfx_h); pk_chassis(8 * sc, 4 * sc, 884 * sc, fh * sc, PNL); ui_col(INK); gfx_circle(40 * sc, 34 * sc, 14 * sc, 1, 1); gfx_setfont(4); ui_col(PNL); ui_text(40 * sc, 29 * sc, "GFX"); gfx_setfont(6, "Trebuchet MS", 24 * sc, 'b'); ui_col(INK); gfx_x = 62 * sc; gfx_y = 20 * sc; gfx_drawstr("STAGE 8"); gfx_setfont(4); ui_col(CYA); gfx_x = 200 * sc; gfx_y = 30 * sc; gfx_drawstr("THEATRE CONSOLE SUMMING"); pk_pextra = "Reset all to defaults"; // the house preset display: PRESETS, prev / next, LCD (click it for the list) k_ = pk_preset(470 * sc, 14 * sc, 346 * sc, 36 * sc, pr_cur, "Vintage|Vintage, driven|Vintage, pushed hard|Modern|Modern, driven"); k_ > 0 ? ( load_preset(k_); pr_cur = k_ > 5 ? 0 : k_; ); ui_size_picker(828 * sc, 22 * sc); ui_col(CYA); gfx_rect(14 * sc, 60 * sc, 872 * sc, 2 * sc); // ---------- stems ---------- gk = 0; loop(8, cx = 48 + gk * 62; ui_cola(0xFFFFFF, 0.05); gfx_rect((cx - 29) * sc, 70 * sc, 58 * sc, 274 * sc); sprintf(#cn, "%d", gk + 1); ui_namebox(cx, 71, 56, gk, #cn, INK); // click to name the stem sprintf(#cn, "%d-%d", 2 * gk + 1, 2 * gk + 2); dy_lbl(cx, 89, #cn, 0.5); LHD[gk] = max(SPK[gk], LHD[gk] * 0.9); SPK[gk] = 0; // held, so the light does not flicker between audio blocks pk_led(cx * sc, 104 * sc, 3.2 * sc, LHD[gk] > 0.001 && SGT[gk] > 0, dy_grn); slider(16 + gk) == 0 ? strcpy(#v, "C") : sprintf(#v, "%s%.0f", slider(16 + gk) < 0 ? "L" : "R", abs(slider(16 + gk))); dy_knob(cx, 134, 12, 16 + gk, -100, 100, 0, 0, CAPG, "PAN", #v); slider(8 + gk) <= -59.9 ? strcpy(#v, "OFF") : sprintf(#v, "%+.1f", slider(8 + gk)); dy_knob(cx, 200, 17, 8 + gk, -60, 10, 0, 0, 0xF2F4F8, "LEVEL", #v); // VCA assign: click to step none > A > B gfx_vsel = slider(24 + gk); pk_ledbtn((cx - 15) * sc, 248 * sc, 30 * sc, 20 * sc, gfx_vsel > 0, gfx_vsel == 2 ? CYA : ORA) ? ( slider(24 + gk) = (gfx_vsel + 1) % 3; ui_auto(24 + gk); ); dy_lbl(cx, 272, gfx_vsel == 0 ? "VCA -" : gfx_vsel == 1 ? "VCA A" : "VCA B", gfx_vsel ? 0.95 : 0.55); pk_ledbtn((cx - 24) * sc, 296 * sc, 22 * sc, 18 * sc, slider(40 + gk), 0xF2C21B) ? ( slider(40 + gk) = 1 - slider(40 + gk); ui_auto(40 + gk); ); pk_ledbtn((cx + 2) * sc, 296 * sc, 22 * sc, 18 * sc, slider(32 + gk), dy_red) ? ( slider(32 + gk) = 1 - slider(32 + gk); ui_auto(32 + gk); ); dy_lbl(cx - 13, 317, "SOLO", 0.6); dy_lbl(cx + 13, 317, "MUTE", 0.6); gk += 1; ); // ---------- VCA masters ---------- gk = 0; loop(2, cx = 556 + gk * 62; ui_cola(gk ? CYA : ORA, 0.14); gfx_rect((cx - 29) * sc, 70 * sc, 58 * sc, 274 * sc); gfx_setfont(5); ui_col(gk ? CYA : ORA); ui_text(cx * sc, 76 * sc, gk ? "VCA B" : "VCA A"); dy_lbl(cx, 89, "GROUP", 0.5); slider(4 + gk) <= -59.9 ? strcpy(#v, "OFF") : sprintf(#v, "%+.1f", slider(4 + gk)); dy_nov = 0; dy_knob(cx, 170, 20, 4 + gk, -60, 10, 0, 0, gk ? CYA : ORA, "LEVEL", #v); dy_nov = 1; pk_ledbtn((cx - 15) * sc, 296 * sc, 30 * sc, 18 * sc, slider(6 + gk), dy_red) ? ( slider(6 + gk) = 1 - slider(6 + gk); ui_auto(6 + gk); ); dy_lbl(cx, 317, "MUTE", 0.6); gk += 1; ); // ---------- master ---------- ui_cola(0xFFFFFF, 0.07); gfx_rect(656 * sc, 70 * sc, 228 * sc, 274 * sc); dy_title(668, 76, "MASTER"); dy_btn(668, 104, slider1 == 0, ORA, "VINTAGE") ? ( slider1 = 0; ui_auto(1); ); dy_btn(716, 104, slider1 == 1, CYA, "MODERN") ? ( slider1 = 1; ui_auto(1); ); dy_nov = 0; sprintf(#v, "%+.1f dB", slider2); dy_knob(696, 200, 18, 2, -6, 12, 0, 0, CAPG, "DRIVE", #v); slider3 <= -59.9 ? strcpy(#v, "OFF") : sprintf(#v, "%+.1f dB", slider3); dy_knob(780, 200, 24, 3, -60, 10, 0, 0, 0xF2F4F8, "MASTER", #v); dy_ledv(848, 96, 216, pkdL, 0); dy_ledv(864, 96, 216, pkdR, 0); pkdL = max(pkL, pkdL * 0.85); pkdR = max(pkR, pkdR * 0.85); pkL = pkR = 0; dy_lbl(854, 318, "L", 0.7); dy_lbl(870, 318, "R", 0.7); dy_lbl(738, 290, slider1 ? "TRANSFORMERLESS" : "TRANSFORMER-COUPLED", 0.55); pk_end(); ui_end();