desc:GFX G Tonewheel //tags: instrument synth organ tonewheel drawbar //author: JGWizrad / Garden FX (GFX) // // Requires gfx-drift, gfx-output, gfx-ui, gfx-panel and gfx-instrument (.jsfx-inc) in the same folder. // // Tonewheel organ (instrument: put it on a track and play it from MIDI): // - 91 always-spinning tonewheels, nine drawbars (16' to 1') with the classic foldback // - percussion: 2nd / 3rd harmonic, normal / soft, fast / slow decay, single-trigger // (it only re-strikes once all keys are lifted, as on the B3; percussion borrows the 1' drawbar) // - key click: the instant on/off of the contacts, plus a little contact noise // - scanner vibrato / chorus V1-V3, C1-C3, valve DRIVE, TONE, SWELL (also MIDI CC11) // - MIDI is passed on: put GFX Rotary after it and the mod wheel / sustain pedal switch its speed. // Drift: GFX instrument standard (motor pitch, tone, scanner speed). The tonewheels stay locked together. 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 slider1:8<0,8,1>-Drawbar 16' slider2:8<0,8,1>-Drawbar 5 1/3' slider3:8<0,8,1>-Drawbar 8' slider4:0<0,8,1>-Drawbar 4' slider5:0<0,8,1>-Drawbar 2 2/3' slider6:0<0,8,1>-Drawbar 2' slider7:0<0,8,1>-Drawbar 1 3/5' slider8:0<0,8,1>-Drawbar 1 1/3' slider9:0<0,8,1>-Drawbar 1' slider10:1<0,1,1{Off,On}>-Percussion slider11:1<0,1,1{Second,Third}>-Percussion harmonic slider12:0<0,1,1{Normal,Soft}>-Percussion volume slider13:0<0,1,1{Fast,Slow}>-Percussion decay slider14:6<0,6,1{Off,V1,V2,V3,C1,C2,C3}>-Vibrato / chorus slider15:40<0,100,1>-Key click (%) slider16:15<0,100,1>-Drive (%) slider17:70<0,100,1>-Tone (%) slider18:100<0,100,1>-Swell (%) slider19:0<-50,50,1>-Tuning (cents) slider20:0<0,16,1>-MIDI channel (0 = omni) slider21:25<0,100,1>-Drift (%) slider23:0<-24,12,0.1>-Output (dB) slider24:0<0,100,1>-Band sat (%) slider25:30<0,100,1>-Bias (%) slider26:120<40,400,1>-Sat low band (Hz) slider27:6000<2000,16000,10>-Sat high band (Hz) slider28:-0.3<-24,0,0.1>-Ceiling / limit threshold (dB) slider29:1<0,2,1{Off,Clip,Limit}>-Output stage slider30:40<0,100,1>-Room size (%) slider31:0<0,100,1>-Room mix (%) in_pin:none out_pin:left output out_pin:right output @init #ui_man = "gfx-g-tonewheel"; // the ? pop-up links to this manual // ---------- memory map ---------- ws = 0; // wheel sine (91) wc = 100; // wheel cosine (91) wsi = 200; // wheel step sin (91) wco = 300; // wheel step cos (91) tg = 400; // drawbar gain target (91) pg = 500; // percussion gain (91) cg = 600; // current gain (91) held = 700; // held keys (128) evq = 1000; // MIDI event queue (3 x 512) vbuf = 4096; vmask = 4095; // scanner delay line dbo = 3000; // drawbar semitone offsets (9) dgn = 3010; // drawbar gains (9) wdt = 3100; // per-wheel gear-ratio detune (91) dbo[0] = -12; dbo[1] = 7; dbo[2] = 0; dbo[3] = 12; dbo[4] = 19; dbo[5] = 24; dbo[6] = 28; dbo[7] = 31; dbo[8] = 36; i = 0; loop(91, ws[i] = 0; wc[i] = 1; cg[i] = 0; wdt[i] = (rand(2) - 1) * 0.0003; i += 1; ); // up to ~0.5 cent, like the gears memset(held, 0, 128); memset(vbuf, 0, 4096); nheld = 0; dirty = 1; penv = 0; clk = 0; vwp = 0; vph = 0; tl = 0; bpz1 = 0; bpz2 = 0; function fold(w) ( while (w < 0) ( w += 12; ); while (w > 90) ( w -= 12; ); w; ); // recompute wheel targets from the held keys and drawbars function rebuild() local(n, d, w, b) ( memset(tg, 0, 91); memset(pg, 0, 91); n = 0; loop(128, held[n] ? ( b = n - 24; // MIDI 24 (C1) = wheel 0; C2 key = 65 Hz on 8' d = 0; loop(9, dgn[d] > 0 ? ( w = fold(b + dbo[d]); tg[w] += dgn[d]; ); d += 1; ); perc_on ? ( w = fold(b + (perc_h ? 19 : 12)); pg[w] += 1; ); ); n += 1; ); ); function note_on(n) ( !held[n] ? ( nheld == 0 ? penv = 1; // single-trigger percussion held[n] = 1; nheld += 1; dirty = 1; clk = max(clk, click); ); ); function note_off(n) ( held[n] ? ( held[n] = 0; nheld -= 1; dirty = 1; clk = max(clk, click * 0.4); ); ); function all_off() ( memset(held, 0, 128); nheld = 0; dirty = 1; ); function update() local(d, v) ( perc_on = slider10; perc_h = slider11; d = 0; loop(9, v = slider(d + 1); dgn[d] = v > 0 ? 10 ^ (-(8 - v) * 3 / 20) : 0; // ~3 dB per drawbar step d += 1; ); perc_on ? ( dgn[8] = 0; // percussion borrows the 1' drawbar slider12 == 0 ? ( d = 0; loop(9, dgn[d] *= 0.708; d += 1; ); ); // NORMAL percussion: organ drops ~3 dB ); plev = slider12 ? 0.5 : 1; pdec = exp(-1 / ((slider13 ? 0.9 : 0.25) * srate)); vc = slider14; vdep = (vc == 1 || vc == 4 ? 0.15 : vc == 2 || vc == 5 ? 0.3 : 0.5) * 0.001 * srate; click = slider15 / 100; ksm = click > 0.98 ? 1 : 1 - exp(-1 / ((0.0002 + 0.004 * (1 - click)) * srate)); // contact speed dv = slider16 / 100; drv_g = 1 + 15 * dv * dv; drv_w = min(dv * 3, 1); drv_n = 1 / (1 - sqr(ost_tanh(0.25 * dv))); tone_f = 2000 * 6 ^ (slider17 / 100); swell_t = slider18 / 100; tune = 2 ^ (slider19 / 1200); chan = slider20; drift = slider21 / 100; out_t = 10 ^ (slider23 / 20); ins_output_setup(24); // ROOM: lengths in samples for the early reflections, tails and diffusers rm_sz = 0.4 + 1.2 * slider30 / 100; rm_mix = slider31 / 100; rm_fb = 0.3 + 0.35 * slider30 / 100; rm_dc = 1 - exp(-2 * $pi * (7000 - 3500 * slider30 / 100) / srate); rm_k = srate * 0.001 * rm_sz; rm_t0 = 7.3 * rm_k; rm_t1 = 11.9 * rm_k; rm_t2 = 17.1 * rm_k; rm_t3 = 23.7 * rm_k; rm_t4 = 31.3 * rm_k; rm_u0 = 8.9 * rm_k; rm_u1 = 13.7 * rm_k; rm_u2 = 19.9 * rm_k; rm_u3 = 27.1 * rm_k; rm_u4 = 35.3 * rm_k; rm_cl = min(41.3 * rm_k, 8000); rm_cr = min(47.9 * rm_k, 8000); rm_al = min(srate * 0.0051, 1000); rm_ar = min(srate * 0.0063, 1000); dirty = 1; ); // ---------- drawbar registrations ---------- function reg(a, b, c, d, e, f, g, h, i, pon, ph, pv, pd, vib, clv, drv) ( slider1 = a; slider2 = b; slider3 = c; slider4 = d; slider5 = e; slider6 = f; slider7 = g; slider8 = h; slider9 = i; slider10 = pon; slider11 = ph; slider12 = pv; slider13 = pd; slider14 = vib; slider15 = clv; slider16 = drv; ); function load_preset(p) ( p == 1 ? reg(8,8,8,0,0,0,0,0,0, 1,1,0,0, 6, 40, 15) : // Soul Jazz p == 2 ? reg(8,8,8,8,0,0,0,0,0, 0,1,0,0, 6, 30, 25) : // Memphis Groove p == 3 ? reg(8,8,8,8,8,8,8,8,8, 0,1,0,0, 6, 30, 30) : // Full Organ p == 4 ? reg(8,8,6,8,0,0,0,0,8, 0,1,0,0, 6, 35, 35) : // Gospel p == 5 ? reg(8,3,8,0,0,0,0,0,0, 1,1,1,1, 0, 25, 5) : // Modern Jazz p == 6 ? reg(8,0,8,0,0,0,0,0,0, 0,1,0,0, 4, 20, 5) : // Jazz Ballad p == 7 ? reg(8,8,8,8,8,8,0,0,0, 0,1,0,0, 6, 50, 70) : // Rock Overdrive p == 8 ? reg(0,0,8,8,0,0,0,0,0, 1,0,1,0, 0, 30, 10) : // Reggae Bubble p == 9 ? reg(0,0,8,0,0,0,0,0,0, 0,1,0,0, 1, 10, 0) : // Flute p == 10 ? reg(8,8,8,0,0,0,0,0,0, 1,1,0,1, 6, 40, 20); // Slow Perc p >= 1 && p <= 10 ? slider_automate(65535); // sliders 1-16 p == 11 ? ( // Reset all to defaults reg(8,8,8,0,0,0,0,0,0, 1,1,0,0, 6, 40, 15); slider17 = 70; slider18 = 100; slider19 = 0; slider20 = 0; slider21 = 25; slider23 = 0; slider24 = 0; slider25 = 30; slider26 = 120; slider27 = 6000; slider28 = -0.3; slider29 = 1; slider30 = 40; slider31 = 0; slider_automate(2147483647 - 2097152); // sliders 1-31 (22 unused) ); ); // ---------- drawbar drawing ---------- function drawbar(x, y0, step, d) local(v, col, tc, bw, ln, hov, by, i, k, ty, sw, st, sl) ( v = slider(d + 1); bw = 22 * sc; st = 10 * sc; ln = y0 + st + v * step; // st: the stub of stalk still showing when the bar is pushed home sl = 8 * step + 24 * sc + st; // slot length col = d < 2 ? 0x6B3A1E : (d == 4 || d == 6 || d == 7) ? 0x1C1C1E : 0xEDE8DC; tc = d < 2 ? 0xF2E6CC : (d == 4 || d == 6 || d == 7) ? 0xF0F0F0 : 0x1C1C1E; // slot: a deep channel in the felt, darker at the top where the rod disappears into the console ui_col(0x0A0706); ui_rrect(x - 3 * sc, y0 - 3 * sc, bw + 6 * sc, sl, 3 * sc); gfx_gradrect(x - 2 * sc, y0 - 2 * sc, bw + 4 * sc, 14 * sc, 0, 0, 0, 0.6, 0, 0, 0, 0, 0, 0, 0, -0.6 / (14 * sc)); ui_cola(0xFFFFFF, 0.10); gfx_line(x - 3 * sc, y0 + sl - 3 * sc, x + bw + 2 * sc, y0 + sl - 3 * sc); // rod: brushed steel, shaded as a cylinder, engraved 1 - 8 as it is pulled out ( sw = bw - 8 * sc; i = 0; loop(ceil(sw), k = i / sw; k = 0.55 + 0.45 * sin(k * $pi) - 0.25 * (k > 0.7) * (k - 0.7) / 0.3; ui_col(ui_mix(0x4A4A4C, 0xE8E6E0, k)); gfx_line(x + 4 * sc + i, y0, x + 4 * sc + i, ln); i += 1; ); ui_cola(0x000000, 0.45); gfx_rect(x + 4 * sc, y0, sw, 3 * sc); // shadow of the console lip i = 1; loop(v, ty = ln - i * step + 3 * sc; ty > y0 + 2 * sc ? ( ui_cola(0x000000, 0.55); gfx_line(x + 5 * sc, ty, x + bw - 6 * sc, ty); gfx_setfont(4); ui_cola(0x1A1A1C, 0.75); sprintf(#dr, "%d", v - i + 1); step > 14 * sc ? ui_text(x + bw * 0.5, ty + 2 * sc, #dr); ); i += 1; ); ); // tab: a rounded cap with a domed face; soft drop shadow onto the felt ui_cola(0x000000, 0.5); ui_rrect(x + 1 * sc, ln + 3 * sc, bw, 19 * sc, 4 * sc); ui_col(ui_mix(col, 0x000000, 0.35)); ui_rrect(x, ln, bw, 19 * sc, 4 * sc); ui_col(col); ui_rrect(x + 1 * sc, ln, bw - 2 * sc, 17 * sc, 4 * sc); gfx_gradrect(x + 2 * sc, ln + 1 * sc, bw - 4 * sc, 8 * sc, 1, 1, 1, 0.32, 0, 0, 0, 0, 0, 0, 0, -0.32 / (8 * sc)); gfx_gradrect(x + 2 * sc, ln + 10 * sc, bw - 4 * sc, 7 * sc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.25 / (7 * sc)); ui_cola(0xFFFFFF, 0.45); gfx_line(x + 4 * sc, ln + 1 * sc, x + bw - 5 * sc, ln + 1 * sc); gfx_setfont(2); sprintf(#dv, "%d", v); ui_cola(tc == 0x1C1C1E ? 0xFFFFFF : 0x000000, 0.35); ui_text(x + bw * 0.5, ln + 3 * sc, #dv); // engraved ui_col(tc); ui_text(x + bw * 0.5, ln + 2 * sc, #dv); // drag / wheel hov = mouse_x >= x - 4 * sc && mouse_x < x + bw + 4 * sc && mouse_y >= y0 - 4 * sc && mouse_y < y0 + sl; pressed && drag_id == 0 && hov ? drag_id = 300 + d; drag_id == 300 + d && mdown ? ( by = floor((mouse_y - y0 - st - 9 * sc) / step + 0.5); by = min(max(by, 0), 8); by != v ? ( slider(d + 1) = by; slider_automate(2 ^ d); ); ); hov && mouse_wheel != 0 && drag_id == 0 ? ( slider(d + 1) = min(max(v + (mouse_wheel < 0 ? 1 : -1), 0), 8); slider_automate(2 ^ d); mouse_wheel = 0; ); ); // the ivory panel with its grain (fine speckle, faint vertical fibre), rendered once into image 126 function ivory_panel(fh) local(i, n, xx, yy, k, x, y, w, h) ( (gfx_w != tw_bw || gfx_h != tw_bh || fh != tw_fh || sc != tw_sc) ? ( tw_bw = gfx_w; tw_bh = gfx_h; tw_fh = fh; tw_sc = sc; gfx_setimgdim(126, gfx_w, gfx_h); gfx_dest = 126; ui_col(0x0B0C0D); gfx_rect(0, 0, gfx_w, gfx_h); pk_chassis(16 * sc, 4 * sc, 608 * sc, fh * sc, IVR); x = 18 * sc; y = 6 * sc; w = 604 * sc; h = (fh - 6) * sc; i = 0; loop(floor(w / 3), k = ui_hash(i * 2.37 + 0.7); ui_cola(0x6A5034, 0.02 + 0.035 * k); gfx_line(x + i * 3 + k * 2, y, x + i * 3 + k * 2, y + h); i += 1; ); n = floor(w * h / 40); i = 0; loop(n, xx = x + ui_hash(i * 3.17 + 0.3) * w; yy = y + ui_hash(i * 7.91 + 2.1) * h; k = ui_hash(i * 1.61 + 5.2); ui_cola(k > 0.6 ? 0xFFFFFF : 0x6A5034, k > 0.6 ? 0.35 : 0.14); gfx_rect(xx, yy, 1, 1); i += 1; ); gfx_dest = -1; ); gfx_x = 0; gfx_y = 0; gfx_a = 1; gfx_mode = 0; gfx_blit(126, 1, 0); ); // ---------- state ---------- ins_drift_init(); ost_init(20000); ostL.ost_ch_init(); ostR.ost_ch_init(); // ROOM buffers (well clear of the output stage's memory) RMB = 200000; RCL = 210000; RCR = 220000; RAL = 230000; RAR = 232000; RMSK = 8191; RAMSK = 1023; memset(RMB, 0, 40000); rm_w = 0; rm_aw = 0; rm_ll = 0; rm_lr = 0; sm = 1 - exp(-1 / (0.03 * srate)); pkL = 0; pkR = 0; evn = 0; spos = 0; midi_led = 0; !ui_dflt ? ui_thm = 9; !ui_dflt ? ui_hw = 1; // fixed walnut-and-ivory faceplate !ui_dflt ? ui_kstyle = 0; // GFX standard knobs IVR = 0xF1EAD8; INKD = 0x3A2A1E; REDL = 0xFF3A2A; AMB = 0xE8A33A; FELT = 0x2A1F18; SH1 = 0xEEE6D2; SH2 = 0xE5DBC4; SH3 = 0xD9CDB3; // sound, voice, utility function mgroup(x, y, w, h, sec, title, shade) ( pk_inset(x * sc, y * sc, w * sc, h * sc, shade); pk_box(x * sc, y * sc, w * sc, h * sc, INKD, title); ui_section(sec == 0 ? 2 : sec); C_INK = INKD; ); update(); swell_s = swell_t; out_s = out_t; ui_dflt = 1; // theme defaults above apply once: @init re-runs (playback start, device reset) must not undo the saved theme @slider update(); @block // queue MIDI with sample offsets (everything is passed on, so a Rotary after this can switch speed) evn = 0; while (midirecv(mofs, m1, m2, m3)) ( stt = m1 & 0xF0; mch = (m1 & 0x0F) + 1; (chan == 0 || mch == chan) && evn < 512 ? ( evq[evn * 4] = mofs; evq[evn * 4 + 1] = stt; evq[evn * 4 + 2] = m2; evq[evn * 4 + 3] = m3; evn += 1; ); midisend(mofs, m1, m2, m3); ); evi = 0; spos = 0; panic_req ? ( all_off(); panic_req = 0; ); // PANIC from the screen update(); // wheel speeds (tuning + tiny motor drift) ins_drift_block(drift, samplesblock); // GFX instrument drift mot = tune * ins_pitch; tone_c = 1 - exp(-2 * $pi * min(tone_f * 2 ^ ins_tone, srate * 0.45) / srate); ai = 0; loop(91, wf = 2 * $pi * 32.7032 * 2 ^ (ai / 12) * mot * (1 + wdt[ai]) / srate; wsi[ai] = sin(wf); wco[ai] = cos(wf); // keep the oscillators exactly on their circle wr = sqrt(ws[ai] * ws[ai] + wc[ai] * wc[ai]); wr > 0 ? ( ws[ai] /= wr; wc[ai] /= wr; ); ai += 1; ); @serialize file_var(0, ui_sz); file_avail(0) != 0 ? file_var(0, pr_cur); // the preset in use (older projects end before it) @sample // MIDI events due at this sample while (evi < evn && evq[evi * 4] <= spos) ( e1 = evq[evi * 4 + 1]; e2 = evq[evi * 4 + 2]; e3 = evq[evi * 4 + 3]; e1 == 0x90 && e3 > 0 ? ( note_on(e2); midi_led = 1; ins_midi_hit(); ) : e1 == 0x80 || e1 == 0x90 ? note_off(e2) : e1 == 0xB0 ? ( e2 == 11 ? ( slider18 = floor(e3 / 127 * 100 + 0.5); swell_t = slider18 / 100; sliderchange(slider18); ); e2 == 120 || e2 == 123 ? all_off(); ); evi += 1; ); spos += 1; dirty ? ( rebuild(); dirty = 0; ); // tonewheels penv *= pdec; pk_ = penv * plev; sum = 0; ai = 0; loop(91, gt = tg[ai] + pg[ai] * pk_; (gt + cg[ai]) > 0.000001 ? ( cg[ai] += (gt - cg[ai]) * ksm; s_ = ws[ai] * wco[ai] + wc[ai] * wsi[ai]; wc[ai] = wc[ai] * wco[ai] - ws[ai] * wsi[ai]; ws[ai] = s_; sum += cg[ai] * s_; ) : cg[ai] = 0; ai += 1; ); sum *= 0.09; // key click: contact noise burst clk > 0.0005 ? ( nz = (rand(2) - 1) * clk * 0.05; bpz1 += (nz - bpz1) * 0.5; bpz2 += (bpz1 - bpz2) * 0.5; // tame the top sum += bpz1 - bpz2 * 0.7; clk *= 0.996; ); // scanner vibrato / chorus vbuf[vwp] = sum; vc > 0 ? ( vph += 2 * $pi * 6.9 * (1 + 0.1 * ins_fx) / srate; vph > 2 * $pi ? vph -= 2 * $pi; rp = vwp - (0.0012 * srate + vdep * sin(vph)); ri = floor(rp); rf = rp - ri; wet = vbuf[ri & vmask] * (1 - rf) + vbuf[(ri + 1) & vmask] * rf; sum = vc >= 4 ? 0.5 * (sum + wet) : wet; ); vwp = (vwp + 1) & vmask; // valve preamp, tone, swell dv > 0 ? ( y = (ost_tanh(drv_g * sum + 0.25 * dv) - ost_tanh(0.25 * dv)) / drv_g ^ 0.75 * drv_n; sum += drv_w * (y - sum); ); tl += (sum - tl) * tone_c; sum = tl; swell_s += (swell_t - swell_s) * sm; out_s += (out_t - out_s) * sm; sum *= swell_s * swell_s * out_s; // ROOM: stereo early reflections + a short damped tail, so the mono organ sits in a space rm_mix > 0 ? ( RMB[rm_w] = sum; rm_el = 0.70 * RMB[(rm_w - rm_t0) & RMSK] - 0.55 * RMB[(rm_w - rm_t1) & RMSK] + 0.45 * RMB[(rm_w - rm_t2) & RMSK] - 0.35 * RMB[(rm_w - rm_t3) & RMSK] + 0.28 * RMB[(rm_w - rm_t4) & RMSK]; rm_er = 0.70 * RMB[(rm_w - rm_u0) & RMSK] + 0.55 * RMB[(rm_w - rm_u1) & RMSK] - 0.45 * RMB[(rm_w - rm_u2) & RMSK] + 0.35 * RMB[(rm_w - rm_u3) & RMSK] - 0.28 * RMB[(rm_w - rm_u4) & RMSK]; rm_ol = RCL[(rm_w - rm_cl) & RMSK]; rm_or = RCR[(rm_w - rm_cr) & RMSK]; rm_ll += (rm_ol - rm_ll) * rm_dc; rm_lr += (rm_or - rm_lr) * rm_dc; RCL[rm_w] = 0.5 * rm_el + rm_lr * rm_fb; // tails cross-feed for a wider image RCR[rm_w] = 0.5 * rm_er + rm_ll * rm_fb; rm_xl = rm_el + 0.7 * rm_ol; rm_xr = rm_er + 0.7 * rm_or; rm_yl = RAL[(rm_aw - rm_al) & RAMSK]; rm_vl = rm_xl + 0.5 * rm_yl; RAL[rm_aw] = rm_vl; rm_xl = rm_yl - 0.5 * rm_vl; rm_yr = RAR[(rm_aw - rm_ar) & RAMSK]; rm_vr = rm_xr + 0.5 * rm_yr; RAR[rm_aw] = rm_vr; rm_xr = rm_yr - 0.5 * rm_vr; rm_w = (rm_w + 1) & RMSK; rm_aw = (rm_aw + 1) & RAMSK; rmL = sum * (1 - 0.3 * rm_mix) + rm_xl * rm_mix * 0.6; rmR = sum * (1 - 0.3 * rm_mix) + rm_xr * rm_mix * 0.6; ) : ( rmL = sum; rmR = sum; ); // output stage oL = ostL.ost_band(rmL); oR = ostR.ost_band(rmR); ost_out(oL, oR); spl0 = ost_l; spl1 = ost_r; pkL = max(pkL, abs(spl0)); pkR = max(pkR, abs(spl1)); @gfx 640 450 ui_thm = 9; ui_hw = 1; ui_begin(640, 450); // ---------- cabinet: ivory panel between walnut cheeks ---------- fh = 442; // the panel, with the IN / OUT strip along the bottom ivory_panel(fh); ui_col(INKD); gfx_circle(44 * sc, 24 * sc, 14 * sc, 1, 1); gfx_setfont(4); ui_col(IVR); ui_text(44 * sc, 19 * sc, "GFX"); gfx_setfont(6, "Trebuchet MS", 20 * sc, 'b'); ui_col(INKD); gfx_x = 66 * sc; gfx_y = 12 * sc; gfx_drawstr("TONEWHEEL"); gfx_setfont(4); ui_cola(INKD, 0.7); gfx_x = 216 * sc; gfx_y = 20 * sc; gfx_drawstr("DRAWBAR ORGAN"); pk_pextra = "Reset all to defaults"; // the house preset display: PRESETS, prev / next, LCD (click it for the list) k_ = pk_preset(306 * sc, 9 * sc, 236 * sc, 28 * sc, pr_cur, "Soul Jazz|Memphis Groove|Full Organ|Gospel|Modern Jazz|Jazz Ballad|Rock Overdrive|Reggae Bubble|Flute|Slow Perc"); k_ > 0 ? ( load_preset(k_); pr_cur = k_ > 10 ? 0 : k_; ); ui_size_picker(554 * sc, 14 * sc); // ---------- DRAWBARS ---------- pk_inset(24 * sc, 52 * sc, 376 * sc, 214 * sc, FELT); // felt nap i = 0; loop(floor(214 * sc / 2), ui_cola(i % 2 ? 0x000000 : 0xFFFFFF, 0.025); gfx_line(24 * sc, 52 * sc + i * 2, 400 * sc, 52 * sc + i * 2); i += 1; ); pk_box(24 * sc, 52 * sc, 376 * sc, 214 * sc, 0xE8DCC0, "DRAWBARS"); ui_section(2); C_INK = INKD; d = 0; loop(9, dx = (48 + d * 38) * sc; gfx_setfont(4); ui_col(0xE8DCC0); ui_text(dx + 11 * sc, 60 * sc, d == 0 ? "16'" : d == 1 ? "5 1/3" : d == 2 ? "8'" : d == 3 ? "4'" : d == 4 ? "2 2/3" : d == 5 ? "2'" : d == 6 ? "1 3/5" : d == 7 ? "1 1/3" : "1'"); drawbar(dx, 77 * sc, 19 * sc, d); d += 1; ); // ---------- PERCUSSION ---------- mgroup(408, 52, 208, 103, 0, "PERC", SH1); i = 0; loop(4, bx = (424 + (i % 2) * 98) * sc; by = (68 + floor(i / 2) * 40) * sc; pk_ledbtn(bx, by, 26 * sc, 24 * sc, i == 0 ? slider10 : slider10 && slider(10 + i), REDL) ? ( i == 0 ? ( slider10 = 1 - slider10; slider_automate(512); ) : slider10 ? ( slider(10 + i) = 1 - slider(10 + i); slider_automate(2 ^ (9 + i)); ); ); gfx_setfont(4); ui_cola(INKD, i == 0 || slider10 ? 1 : 0.4); gfx_x = bx + 32 * sc; gfx_y = by + 6 * sc; gfx_drawstr(i == 0 ? "ON" : i == 1 ? "THIRD" : i == 2 ? "SOFT" : "SLOW"); i += 1; ); // ---------- VIBRATO / CHORUS ---------- mgroup(408, 163, 208, 103, 0, "V/C", SH1); i = 0; loop(6, bx = (424 + (i % 3) * 66) * sc; by = (179 + floor(i / 3) * 40) * sc; pk_ledbtn(bx, by, 26 * sc, 24 * sc, slider14 == i + 1, AMB) ? ( slider14 = slider14 == i + 1 ? 0 : i + 1; slider_automate(8192); ); gfx_setfont(4); ui_col(INKD); gfx_x = bx + 30 * sc; gfx_y = by + 6 * sc; gfx_drawstr(i == 0 ? "V1" : i == 1 ? "V2" : i == 2 ? "V3" : i == 3 ? "C1" : i == 4 ? "C2" : "C3"); i += 1; ); // ---------- VOICE ---------- mgroup(24, 276, 592, 88, 0, "VOICE", SH2); ui_knob(110 * sc, 316 * sc, 15, 0, 100, 40, 0, "CLICK", "%.0f%%"); ui_knob(222 * sc, 316 * sc, 16, 0, 100, 15, 0, "DRIVE", "%.0f%%"); ui_knob(334 * sc, 316 * sc, 17, 0, 100, 70, 0, "TONE", "%.0f%%"); ui_knob(446 * sc, 316 * sc, 18, 0, 100, 100, 0, "SWELL", "%.0f%%"); ui_knob(558 * sc, 316 * sc, 19, -50, 50, 0, 0, "TUNE", "%+.0f cents"); // ---------- DRIFT ---------- mgroup(24, 374, 112, 66, 1, "DRIFT", SH3); ui_knob(80 * sc, 400 * sc, 21, 0, 100, 25, 0, "DRIFT", "%.0f%%"); // ---------- MIDI ---------- mgroup(144, 374, 252, 66, 3, "MIDI", SH3); knob_step = 1; ui_knob(186 * sc, 400 * sc, 20, 0, 16, 0, 0, "CHANNEL", slider20 == 0 ? "OMNI" : "CH %.0f"); pk_led(240 * sc, 392 * sc, 3 * sc, ins_midi_lit() || nheld > 0, REDL); gfx_setfont(4); ui_col(INKD); gfx_x = 250 * sc; gfx_y = 387 * sc; gfx_drawstr("MIDI IN"); sprintf(#nh, "%d key%s down", nheld, nheld == 1 ? "" : "s"); gfx_x = 234 * sc; gfx_y = 405 * sc; gfx_drawstr(#nh); gfx_x = 234 * sc; gfx_y = 421 * sc; gfx_drawstr("CC11 = swell"); pk_ledbtn(342 * sc, 386 * sc, 32 * sc, 28 * sc, 0, REDL) ? panic_req = 1; gfx_setfont(4); ui_col(INKD); ui_text(358 * sc, 420 * sc, "PANIC"); // ---------- MASTER ---------- mgroup(404, 374, 212, 66, 2, "MASTER", SH3); ui_knob(460 * sc, 400 * sc, 23, -24, 12, 0, 0, "OUTPUT", "%.1f dB"); mL.ui_ledm(530 * sc, 390 * sc, 70 * sc, 10 * sc, pkL); mR.ui_ledm(530 * sc, 406 * sc, 70 * sc, 10 * sc, pkR); pkL *= 0.85; pkR *= 0.85; gfx_setfont(4); ui_col(INKD); gfx_x = 510 * sc; gfx_y = 390 * sc; gfx_drawstr("L"); gfx_x = 510 * sc; gfx_y = 406 * sc; gfx_drawstr("R"); ui_text(565 * sc, 422 * sc, "OUT"); // walnut cheeks last, so the trays tuck under them pk_wood(2 * sc, 2 * sc, 18 * sc, (fh + 4) * sc); pk_wood(620 * sc, 2 * sc, 18 * sc, (fh + 4) * sc); pk_end(); ui_end();