desc:GFX G Pulse //tags: instrument drum machine synthesis sequencer fm physical modelling //author: JGWizrad / Garden FX (GFX) // // Requires gfx-drift, gfx-output, gfx-ui, gfx-panel and gfx-instrument (.jsfx-inc) in the same folder. // // Synthesis drum machine: eight voices, no samples, four character models for the whole kit. // CHARACTER - ANALOG 808 / 909-style virtual analog: pitch-swept sines, filtered noise, six-square metal // FM DrumSynth / DX-style two-operator FM with its own index envelope // PHYSICAL Kong / Ultrabeat-style modal synthesis: membranes, plates, wooden bars and hands, // struck by a mallet (SNAP = hardness), snare wires that buzz with the head // DIGITAL CZ-style phase distortion, sample-and-hold noise and bit / sample-rate reduction // VOICES - TUNE, DECAY, TONE (brightness / FM index / damping / warp), SNAP (attack), LEVEL, PAN. // Click a voice's name to hear it; M mutes it. The closed hat chokes the open hat. // SEQUENCER - 32 steps of 1/16. Click a step to set it (drag to paint), Shift-click for an accent, // right-click a step to end that row there: every row has its own length (polyrhythm). // Plays with Reaper's transport (PLAY on), locked to the project's tempo; SWING delays the // off-beat 16ths. // PATTERNS - eight slots, saved with the project and in Reaper presets. Pick a slot to load it (while // playing it changes at the next bar); SAVE stores your edits into the current slot // (right-click SAVE to store into another slot). COPY / PASTE / CLEAR / RANDOM, // FACTORY patterns, and LOAD from text files in REAPER/Data/gfx-drum-patterns // (one line per voice, e.g. KICK X...x...X...x... X = accent, x = hit, . = rest). // MIDI - plays from General MIDI drum notes; MIDI OUT sends the sequence as notes (record it to an item). // COMP - stereo-linked bus compressor; MIX blends it in parallel with the dry kit. // IN / OUT: the strip along the bottom holds SAT, the OUT stage (OFF / CLIP / LIM) and STEREO (MONO, WIDTH). 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:0<0,3,1{Analog,FM,Physical,Digital}>-Character slider2:1<0,1,1{Off,On}>-Play (with transport) slider3:0<0,75,1>-Swing (%) slider4:1<1,8,1>-Pattern slot slider5:0<0,1,1{Off,On}>-MIDI out slider6:0<0,16,1>-MIDI channel (0 = omni in, 10 out) slider7:0<-24,12,0.1>-Volume (dB) slider8:-12<-40,0,0.1>-Comp threshold (dB) slider9:4<1,20,0.1>-Comp ratio slider10:10<0.1,100,0.1>-Comp attack (ms) slider11:120<10,1000,1>-Comp release (ms) slider12:6<0,24,0.1>-Comp makeup (dB) slider13:0<0,100,1>-Comp mix (%) slider14:0<0,100,1>-Band sat (%) slider15:30<0,100,1>-Bias (%) slider16:120<40,400,1>-Sat low band (Hz) slider17:6000<2000,16000,10>-Sat high band (Hz) slider18:-0.3<-24,0,0.1>-Ceiling / limit threshold (dB) slider19:1<0,2,1{Off,Clip,Limit}>-Output stage slider20:20<20,500,1>-Mono below (Hz, 20 = off) slider21:0<0,100,1>-Stereo width (%) slider22:0<-24,24,0.1>-Kick tune (st) slider23:100<25,400,1>-Kick decay (%) slider24:50<0,100,1>-Kick tone (%) slider25:50<0,100,1>-Kick snap (%) slider26:85<0,100,1>-Kick level (%) slider27:0<-100,100,1>-Kick pan slider28:0<-24,24,0.1>-Snare tune (st) slider29:100<25,400,1>-Snare decay (%) slider30:50<0,100,1>-Snare tone (%) slider31:50<0,100,1>-Snare snap (%) slider32:75<0,100,1>-Snare level (%) slider33:0<-100,100,1>-Snare pan slider34:0<-24,24,0.1>-Clap tune (st) slider35:100<25,400,1>-Clap decay (%) slider36:50<0,100,1>-Clap tone (%) slider37:50<0,100,1>-Clap snap (%) slider38:70<0,100,1>-Clap level (%) slider39:-10<-100,100,1>-Clap pan slider40:0<-24,24,0.1>-Closed hat tune (st) slider41:100<25,400,1>-Closed hat decay (%) slider42:50<0,100,1>-Closed hat tone (%) slider43:50<0,100,1>-Closed hat snap (%) slider44:60<0,100,1>-Closed hat level (%) slider45:25<-100,100,1>-Closed hat pan slider46:0<-24,24,0.1>-Open hat tune (st) slider47:100<25,400,1>-Open hat decay (%) slider48:50<0,100,1>-Open hat tone (%) slider49:50<0,100,1>-Open hat snap (%) slider50:55<0,100,1>-Open hat level (%) slider51:25<-100,100,1>-Open hat pan slider52:0<-24,24,0.1>-Tom tune (st) slider53:100<25,400,1>-Tom decay (%) slider54:50<0,100,1>-Tom tone (%) slider55:50<0,100,1>-Tom snap (%) slider56:70<0,100,1>-Tom level (%) slider57:-20<-100,100,1>-Tom pan slider58:0<-24,24,0.1>-Perc tune (st) slider59:100<25,400,1>-Perc decay (%) slider60:50<0,100,1>-Perc tone (%) slider61:50<0,100,1>-Perc snap (%) slider62:60<0,100,1>-Perc level (%) slider63:-35<-100,100,1>-Perc pan slider64:0<-24,24,0.1>-Cymbal tune (st) slider65:100<25,400,1>-Cymbal decay (%) slider66:50<0,100,1>-Cymbal tone (%) slider67:50<0,100,1>-Cymbal snap (%) slider68:50<0,100,1>-Cymbal level (%) slider69:35<-100,100,1>-Cymbal pan slider70:/gfx-drum-patterns:none:-Pattern file (browse) in_pin:none out_pin:left output out_pin:right output @init #ui_man = "gfx-g-pulse"; // the ? pop-up links to this manual NV = 8; // ---------- memory map ---------- PAT = 1000; LEN = 1256; MUTE = 1264; // working pattern: 8 x 32 steps (0 rest, 1 hit, 2 accent), lengths, mutes CUR = 1280; // current step of each row (display), -1 when stopped CLIP = 1400; SLOTS = 2000; SLN = 264; // clipboard; 8 slots of 256 steps + 8 lengths MR = 5000; MA = 5030; // modal ratios and amplitudes (4 sets x 6) KB = 6000; // per-voice resonator memory (64 each) NMAP = 7000; // MIDI note -> voice EVQ = 7200; // MIDI events this block (4 x 512) NOFF = 9300; // MIDI out: pending note-off countdown per voice VLT = 9320; // voice trigger light (display) // modal sets: 0 membrane, 1 plate / metal, 2 wooden bar, 3 hands (clap) function mset(s, r0, r1, r2, r3, r4, r5, a0, a1, a2, a3, a4, a5) ( MR[s * 6] = r0; MR[s * 6 + 1] = r1; MR[s * 6 + 2] = r2; MR[s * 6 + 3] = r3; MR[s * 6 + 4] = r4; MR[s * 6 + 5] = r5; MA[s * 6] = a0; MA[s * 6 + 1] = a1; MA[s * 6 + 2] = a2; MA[s * 6 + 3] = a3; MA[s * 6 + 4] = a4; MA[s * 6 + 5] = a5; ); mset(0, 1, 1.594, 2.136, 2.296, 2.653, 2.918, 1, 0.65, 0.5, 0.42, 0.32, 0.25); mset(1, 1, 1.652, 2.338, 3.211, 4.282, 5.394, 1, 0.95, 0.85, 0.75, 0.65, 0.55); mset(2, 1, 2.756, 5.404, 8.933, 13.34, 18.64, 1, 0.45, 0.22, 0.11, 0.05, 0.025); mset(3, 1, 1.37, 2.08, 2.91, 3.62, 4.43, 1, 0.8, 0.6, 0.45, 0.3, 0.2); gi = 0; loop(128, NMAP[gi] = -1; gi += 1; ); NMAP[35] = NMAP[36] = 0; NMAP[38] = NMAP[40] = 1; NMAP[39] = 2; NMAP[42] = NMAP[44] = 3; NMAP[46] = 4; NMAP[41] = NMAP[43] = NMAP[45] = NMAP[47] = NMAP[48] = NMAP[50] = 5; NMAP[37] = NMAP[56] = NMAP[75] = NMAP[76] = NMAP[77] = 6; NMAP[49] = NMAP[51] = NMAP[52] = NMAP[55] = NMAP[57] = NMAP[59] = 7; function out_note(v) ( v == 0 ? 36 : v == 1 ? 38 : v == 2 ? 39 : v == 3 ? 42 : v == 4 ? 46 : v == 5 ? 45 : v == 6 ? 56 : 49; ); function vname(v) ( v == 0 ? "KICK" : v == 1 ? "SNARE" : v == 2 ? "CLAP" : v == 3 ? "CL HAT" : v == 4 ? "OP HAT" : v == 5 ? "TOM" : v == 6 ? "PERC" : "CYMBAL"; ); function cname(m) ( m == 0 ? "ANALOG" : m == 1 ? "FM" : m == 2 ? "PHYSICAL" : "DIGITAL"; ); // ---------- building blocks ---------- // TPT state-variable filter; f_run returns the band-pass, lp / hp are left in the instance function f_set(fc, q) instance(g, k, a1, a2, a3) ( g = tan($pi * min(max(fc, 20), srate * 0.45) / srate); k = 1 / q; a1 = 1 / (1 + g * (g + k)); a2 = g * a1; a3 = g * a2; ); function f_run(x) instance(g, k, a1, a2, a3, ic1, ic2, bp, lp, hp) local(v1, v2, v3) ( v3 = x - ic2; v1 = a1 * ic1 + a2 * v3; v2 = ic2 + a2 * ic1 + a3 * v3; ic1 = 2 * v1 - ic1; ic2 = 2 * v2 - ic2; bp = v1; lp = v2; hp = x - k * v1 - v2; bp; ); function f_out(t) instance(bp, lp, hp) ( t == 0 ? lp : t == 1 ? bp : hp; ); function f_clear() instance(ic1, ic2) ( ic1 = 0; ic2 = 0; ); function dcoef(t) ( exp(-6.9 / (max(t, 0.0005) * srate)); ); // per-sample multiplier: -60 dB after t seconds function sq(p) ( p - floor(p) < 0.5 ? 1 : -1; ); // ---------- recipes: what each voice is made of, per character model ---------- // osc: f base Hz, pd / pt pitch sweep depth (x) and time, om mode (0 sine, 1 FM, 2 PD, 3 two squares, 4 two sines), // or FM ratio / second osc ratio, ix FM index / PD warp, it index decay, ol level, od decay // noise: nl level, nt filter (0 LP, 1 BP, 2 HP), nf, nq, nd decay, nb clap bursts // metal: ml level, md decay; modal: kl level, ks set, kf base, kd decay, kdm upper-mode damping, kw snare wires // cl click; vt / vf / vq post filter (0 off, 1 HP, 2 BP); cr digital crush; tr level trim function recipe(m, k) ( rf = 100; rpd = 0; rpt = 0.03; rom = 0; ror = 1; rix = 0; rit = 0.05; rol = 0; rod = 0.2; rnl = 0; rnt = 1; rnf = 1000; rnq = 0.7; rnd = 0.1; rnb = 0; rml = 0; rmd = 0.1; rkl = 0; rks = 0; rkf = 100; rkd = 0.3; rkdm = 0.5; rkw = 0; rcl = 0; rvt = 0; rvf = 1000; rvq = 0.7; rcr = 0; rtr = 1; k == 0 ? ( // KICK m == 0 ? ( rf = 52; rpd = 3; rpt = 0.035; rol = 1; rod = 0.45; rcl = 0.35; ) : m == 1 ? ( rf = 50; rpd = 2; rpt = 0.04; rom = 1; ror = 1; rix = 4; rit = 0.02; rol = 1; rod = 0.5; rcl = 0.15; ) : m == 2 ? ( rf = 50; rpd = 0.6; rpt = 0.05; rol = 0.6; rod = 0.3; rkl = 0.8; rks = 0; rkf = 50; rkd = 0.55; rkdm = 1.2; rcl = 0.2; ) : ( rf = 50; rpd = 4; rpt = 0.03; rom = 2; rix = 0.9; rit = 0.05; rol = 1; rod = 0.4; rcr = 1; ); ) : k == 1 ? ( // SNARE m == 0 ? ( rf = 185; ror = 1.78; rom = 4; rpd = 0.3; rpt = 0.02; rol = 0.6; rod = 0.12; rnl = 0.8; rnt = 1; rnf = 5000; rnq = 0.6; rnd = 0.2; rcl = 0.2; ) : m == 1 ? ( rf = 200; rom = 1; ror = 1.47; rix = 3; rit = 0.03; rol = 0.6; rod = 0.1; rnl = 0.7; rnt = 1; rnf = 3500; rnq = 0.8; rnd = 0.18; ) : m == 2 ? ( rkl = 0.9; rks = 0; rkf = 190; rkd = 0.22; rkdm = 0.6; rkw = 1.2; rnl = 0.35; rnt = 1; rnf = 4500; rnq = 0.6; rnd = 0.16; rcl = 0.35; ) : ( rf = 210; rom = 2; rix = 0.7; rit = 0.04; rol = 0.5; rod = 0.1; rnl = 0.8; rnt = 1; rnf = 4000; rnq = 0.7; rnd = 0.2; rcr = 1; ); ) : k == 2 ? ( // CLAP m == 0 ? ( rnl = 1; rnt = 1; rnf = 1100; rnq = 1.4; rnd = 0.18; rnb = 1; ) : m == 1 ? ( rnl = 1; rnt = 1; rnf = 1300; rnq = 1; rnd = 0.16; rnb = 1; rf = 900; rom = 1; ror = 2.3; rix = 5; rit = 0.01; rol = 0.3; rod = 0.02; ) : m == 2 ? ( rkl = 0.6; rks = 3; rkf = 850; rkd = 0.05; rkdm = 0.3; rnl = 0.9; rnt = 1; rnf = 1400; rnq = 1.1; rnd = 0.17; rnb = 1; ) : ( rnl = 1; rnt = 1; rnf = 1500; rnq = 1; rnd = 0.15; rnb = 1; rcr = 1; ); ) : (k == 3 || k == 4) ? ( // CLOSED / OPEN HAT m == 0 ? ( rml = 1; rmd = 0.045; rnl = 0.15; rnt = 2; rnf = 9000; rnd = 0.03; rvt = 1; rvf = 7000; ) : m == 1 ? ( rf = 4300; rom = 1; ror = 1.4727; rix = 14; rit = 0.5; rol = 0.6; rod = 0.045; rnl = 0.5; rnt = 2; rnf = 8000; rnd = 0.04; rvt = 1; rvf = 6500; ) : m == 2 ? ( rkl = 0.4; rks = 1; rkf = 4200; rkd = 0.05; rkdm = 0.15; rnl = 0.8; rnt = 1; rnf = 9000; rnq = 0.9; rnd = 0.05; rcl = 0.15; rvt = 1; rvf = 6500; ) : ( rnl = 0.8; rnt = 2; rnf = 8000; rnd = 0.045; rml = 0.4; rmd = 0.04; rcr = 1; rvt = 1; rvf = 5500; ); k == 4 ? ( rmd *= 8; rod *= 8; rkd *= 8; rnd *= 8; ); ) : k == 5 ? ( // TOM m == 0 ? ( rf = 120; rpd = 0.6; rpt = 0.08; rol = 1; rod = 0.35; rnl = 0.1; rnt = 0; rnf = 2000; rnd = 0.05; rcl = 0.15; ) : m == 1 ? ( rf = 115; rpd = 0.4; rpt = 0.08; rom = 1; ror = 1.5; rix = 2; rit = 0.05; rol = 1; rod = 0.35; ) : m == 2 ? ( rkl = 1; rks = 0; rkf = 110; rkd = 0.45; rkdm = 0.8; rcl = 0.1; ) : ( rf = 125; rpd = 0.8; rpt = 0.06; rom = 2; rix = 0.6; rit = 0.1; rol = 1; rod = 0.3; rcr = 1; ); ) : k == 6 ? ( // PERC (cowbell / bell / wood) m == 0 ? ( rf = 540; rom = 3; ror = 1.48; rol = 0.7; rod = 0.25; rvt = 2; rvf = 800; rvq = 1.5; ) : m == 1 ? ( rf = 620; rom = 1; ror = 3.5; rix = 2.5; rit = 0.15; rol = 1; rod = 0.3; ) : m == 2 ? ( rkl = 1; rks = 2; rkf = 800; rkd = 0.2; rkdm = 0.5; rcl = 0.2; ) : ( rf = 700; rom = 2; rix = 0.8; rit = 0.08; rol = 0.8; rod = 0.15; rcr = 1; rvt = 2; rvf = 1000; rvq = 1; ); ) : ( // CYMBAL m == 0 ? ( rml = 1; rmd = 1.2; rnl = 0.3; rnt = 2; rnf = 9000; rnd = 0.9; rvt = 2; rvf = 8000; rvq = 0.7; ) : m == 1 ? ( rf = 3100; rom = 1; ror = 2.4311; rix = 12; rit = 1.5; rol = 0.6; rod = 1.2; rnl = 0.45; rnt = 2; rnf = 7500; rnd = 1; rvt = 1; rvf = 4000; ) : m == 2 ? ( rkl = 0.6; rks = 1; rkf = 1150; rkd = 1.6; rkdm = 0.05; rnl = 0.5; rnt = 2; rnf = 7000; rnd = 1.1; rcl = 0.2; rvt = 1; rvf = 2500; ) : ( rnl = 0.7; rnt = 2; rnf = 7000; rnd = 1.1; rml = 0.6; rmd = 0.9; rcr = 1; rvt = 1; rvf = 3500; ); ); rtr = TRIM[m * 8 + k]; ); TRIM = 9400; // per model and voice: balances the kit (set below) // ---------- one voice ---------- function vtrig(k, vel) instance(f0, pd, pe, pec, om, orr, ix, ie, iec, ol, oe, oec, pc, pm, nl, ne, nec, nb, nbt, nbv, nbc, nbi, ml, me, mec, mt, kl, kset, kmx, kmc, kw, kenv, kfc, xe, xec, xlc, xlp, ximp, xt, xn, cl, ce, cec, vt, cr, crh, crc, crn, crq, amp, gl, gr, act, kbs, nfl, pfl, nt, mp1, mp2, mp3, mp4, mp5, mp6) local(b, tm, dm, tn, sn, i, f, t, r, mm) ( b = 22 + k * 6; tm = 2 ^ (slider(b) / 12); dm = slider(b + 1) / 100; tn = slider(b + 2) / 100; sn = slider(b + 3) / 100; recipe(model, k); // tone: model-specific brightness; snap: attack model == 1 ? rix *= 0.25 + 1.5 * tn : model == 3 ? rix = min(rix * (0.4 + 1.2 * tn), 0.97) : 0; rnf *= 2 ^ ((tn - 0.5) * 1.5); rvf *= 2 ^ ((tn - 0.5) * (model == 0 ? 1 : 0.5)); rpd *= 0.3 + 1.4 * sn; rcl *= 2 * sn; f0 = rf * tm; pd = rpd; pe = 1; pec = dcoef(rpt * dm); om = rom; orr = ror; ix = rix * (model == 1 ? 1 + sn : 1); ie = 1; iec = dcoef(rit * dm); ol = rol; oe = rol > 0; oec = dcoef(rod * dm); pc = 0; pm = 0; nl = rnl; ne = rnl > 0 || rkw > 0; nec = dcoef(rnd * dm); nb = rnb; nbt = 0; nbv = 1; nbc = dcoef(0.006); nbi = floor(0.011 * srate); nt = rnt; nfl.f_set(rnf * tm, rnq); nfl.f_clear(); ml = rml; me = rml > 0; mec = dcoef(rmd * dm); mt = tm; mp1 = 0; mp2 = 0.17; mp3 = 0.41; mp4 = 0.63; mp5 = 0.29; mp6 = 0.83; // the metal starts the same way every hit cl = rcl; ce = rcl > 0; cec = dcoef(0.004); vt = rvt; pfl.f_set(rvf * tm, rvq); cr = rcr; crc = 0; crn = max(1, floor(srate / (11000 + 29000 * tn))); crq = 128; // modal bank (resonators in KB memory: a1, a2, g, y1, y2 for 6 modes) kl = rkl; kset = rks; kw = rkw; kenv = 0; kbs = KB + k * 64; kmx = 0; rkl > 0 ? ( i = 0; loop(6, f = rkf * tm * MR[kset * 6 + i]; t = rkd * dm / (1 + rkdm * (1.6 - tn) * (MR[kset * 6 + i] - 1)); r = f < srate * 0.45 ? exp(-6.9 / (max(t, 0.002) * srate)) : 0; kbs[i] = 2 * r * cos(2 * $pi * min(f, srate * 0.45) / srate); kbs[6 + i] = r * r; kbs[12 + i] = r > 0 ? MA[kset * 6 + i] * (1 - r * r) * 6 : 0; kbs[18 + i] = 0; kbs[24 + i] = 0; kmx = max(kmx, r); i += 1; ); kmc = kmx; kmx = 1; // mallet: a half-sine pulse (shorter = harder with SNAP) plus a little filtered noise for texture xt = 0; xn = max(floor((0.006 - 0.0052 * sn) * srate), 2); xe = 1; xec = dcoef(0.012 - 0.01 * sn); xlc = 1 - exp(-2 * $pi * (1500 + 10000 * sn) / srate); xlp = 0; ximp = 0; ) : ( kmx = 0; xe = 0; ); mm = (slider(b + 4) / 100); amp = mm * mm * vel * rtr; t = (slider(b + 5) / 100 + 1) * $pi * 0.25; gl = cos(t) * 1.4142; gr = sin(t) * 1.4142; act = 1; ); function vrun() instance(f0, pd, pe, pec, om, orr, ix, ie, iec, ol, oe, oec, pc, pm, nl, ne, nec, nb, nbt, nbv, nbc, nbi, ml, me, mec, mt, kl, kset, kmx, kmc, kw, kenv, kfc, xe, xec, xlc, xlp, ximp, xt, xn, cl, ce, cec, vt, cr, crh, crc, crn, crq, amp, gl, gr, act, kbs, nfl, pfl, nt, mp1, mp2, mp3, mp4, mp5, mp6, p2) local(y, f, nz, w, d, x, i, a, env) ( act ? ( y = 0; nz = rand(2) - 1; // oscillator oe > 0.0001 ? ( f = f0 * (1 + pd * pe); pe *= pec; om == 0 ? ( pc += f / srate; pc -= floor(pc); x = sin(2 * $pi * pc); ) : om == 1 ? ( pc += f / srate; pc -= floor(pc); pm += f * orr / srate; pm -= floor(pm); x = sin(2 * $pi * pc + ix * ie * sin(2 * $pi * pm)); ie *= iec; ) : om == 2 ? ( pc += f / srate; pc -= floor(pc); w = ix * ie; d = 0.5 * (1 - w); ie *= iec; // phase distortion x = sin(2 * $pi * (pc < d ? pc * 0.5 / d : 0.5 + (pc - d) * 0.5 / (1 - d))); ) : om == 3 ? ( pc += f / srate; pc -= floor(pc); p2 += f * orr / srate; p2 -= floor(p2); x = 0.5 * (sq(pc) + sq(p2)); ) : ( pc += f / srate; pc -= floor(pc); p2 += f * orr / srate; p2 -= floor(p2); x = 0.6 * sin(2 * $pi * pc) + 0.4 * sin(2 * $pi * p2); ); y += x * ol * oe; oe *= oec; ) : oe = 0; // noise (with clap bursts and snare wires) ne > 0.0001 || kenv > 0.0001 ? ( nb && nbt < 3 * nbi ? ( (nbt % nbi) == 0 ? nbv = 1; env = nbv; nbv *= nbc; nbt += 1; ) : ( env = ne; ne *= nec; ); a = nl * env + kw * kenv; nfl.f_run(nz * a); y += nfl.f_out(nt); ) : ne = 0; // metal: six squares at the 808 ratios me > 0.0001 ? ( mp1 += 205.3 * mt / srate; mp2 += 304.4 * mt / srate; mp3 += 369.6 * mt / srate; mp4 += 522.7 * mt / srate; mp5 += 540 * mt / srate; mp6 += 800 * mt / srate; x = (sq(mp1) + sq(mp2) + sq(mp3) + sq(mp4) + sq(mp5) + sq(mp6)) / 6; y += x * ml * me; me *= mec; ) : me = 0; // modal bank, struck by a mallet (impulse + filtered noise); clap hands are driven by the bursts kmx > 0.0001 ? ( kset == 3 ? ( x = nz * (nb && nbt < 3 * nbi ? nbv : xe * 0.3); xe *= xec; ) : ( xlp += (nz * xe - xlp) * xlc; x = xlp * 0.15 + (xt < xn ? sin($pi * xt / xn) * 2 / xn * 48000 / srate * 8 : 0); xt += 1; xe *= xec; ); a = 0; i = 0; loop(6, w = kbs[i] * kbs[18 + i] - kbs[6 + i] * kbs[24 + i] + kbs[12 + i] * x; kbs[24 + i] = kbs[18 + i]; kbs[18 + i] = w; a += w; i += 1; ); kenv = max(abs(a) * 0.5, kenv * 0.9995); y += a * kl; kmx *= kmc; ) : ( kmx = 0; kenv *= 0.999; ); // click ce > 0.0001 ? ( y += nz * cl * ce; ce *= cec; ) : ce = 0; // post filter, digital crush vt == 1 ? ( pfl.f_run(y); y = pfl.hp; ) : vt == 2 ? ( y = pfl.f_run(y); ); cr ? ( crc -= 1; crc <= 0 ? ( crc += crn; crh = floor(y * crq + 0.5) / crq; ); y = crh; ); act = oe > 0.0001 || ne > 0.0001 || me > 0.0001 || kmx > 0.0001 || ce > 0.0001 || kenv > 0.0001; y * amp; ) : 0; ); function v_trig(i, vel) ( VLT[i] = 1; i == 0 ? v0.vtrig(0, vel) : i == 1 ? v1.vtrig(1, vel) : i == 2 ? v2.vtrig(2, vel) : i == 3 ? ( v3.vtrig(3, vel); v4.ne = 0; v4.me = 0; v4.kmx = 0; v4.oe = 0; ) : i == 4 ? v4.vtrig(4, vel) : i == 5 ? v5.vtrig(5, vel) : i == 6 ? v6.vtrig(6, vel) : v7.vtrig(7, vel); ); // level trims per model and voice, so every character model comes out balanced (measured offline) TRIM[0] = 1.084; TRIM[1] = 1.413; TRIM[2] = 2.661; TRIM[3] = 3.759; TRIM[4] = 3.759; TRIM[5] = 1.012; TRIM[6] = 0.881; TRIM[7] = 4.316; TRIM[8] = 1.023; TRIM[9] = 1.274; TRIM[10] = 2.786; TRIM[11] = 0.766; TRIM[12] = 0.782; TRIM[13] = 1.012; TRIM[14] = 0.902; TRIM[15] = 0.779; TRIM[16] = 0.104; TRIM[17] = 0.241; TRIM[18] = 0.953; TRIM[19] = 1.473; TRIM[20] = 1.466; TRIM[21] = 0.234; TRIM[22] = 5.821; TRIM[23] = 1.442; TRIM[24] = 1.149; TRIM[25] = 1.367; TRIM[26] = 2.918; TRIM[27] = 0.872; TRIM[28] = 0.878; TRIM[29] = 1.095; TRIM[30] = 1.565; TRIM[31] = 1.033; // ---------- patterns ---------- // a pattern block is 256 steps (voice * 32 + step) followed by 8 row lengths function pset(base, v, str) local(i, c, n) ( n = min(strlen(str), 32); i = 0; loop(32, base[v * 32 + i] = 0; i += 1; ); i = 0; loop(n, c = str_getchar(str, i); base[v * 32 + i] = c == 'X' ? 2 : (c == 'x' || c == 'o') ? 1 : 0; i += 1; ); base[256 + v] = n > 0 ? n : 16; ); function factory_name(p) ( p == 0 ? "HOUSE" : p == 1 ? "TECHNO" : p == 2 ? "BREAKBEAT" : p == 3 ? "ELECTRO" : p == 4 ? "HIP-HOP" : p == 5 ? "TRAP (32)" : p == 6 ? "POLYRHYTHM" : "EMPTY"; ); function factory(base, p) local(i) ( memset(base, 0, 264); i = 0; loop(8, base[256 + i] = 16; i += 1; ); p == 0 ? ( pset(base, 0, "X...X...X...X..."); pset(base, 2, "....X.......X..."); pset(base, 3, "xx.xxx.xxx.xxx.x"); pset(base, 4, "..x...x...x...x."); pset(base, 6, "...x......x....."); pset(base, 7, "X..............."); ) : p == 1 ? ( pset(base, 0, "X...X...X...X..."); pset(base, 2, "....x.......x..."); pset(base, 3, "x.x.x.x.x.x.x.x."); pset(base, 4, "..X...X...X...X."); pset(base, 5, "......x.......x."); pset(base, 6, "x..x..x..x..x..x"); ) : p == 2 ? ( pset(base, 0, "X.x.......xx...."); pset(base, 1, "....X..x.x..X..x"); pset(base, 3, "x.x.x.x.x.x.x.x."); pset(base, 4, "..............x."); pset(base, 7, "X..............."); ) : p == 3 ? ( pset(base, 0, "X......X..X....."); pset(base, 1, "....X.......X..."); pset(base, 2, "....X.......X..x"); pset(base, 3, "x.xxx.x.x.xxx.x."); pset(base, 5, "..........x..x.."); ) : p == 4 ? ( pset(base, 0, "X......x..X....."); pset(base, 1, "....X.......X..."); pset(base, 3, "x.x.x.x.x.x.x.x."); pset(base, 4, "..............x."); pset(base, 2, "............x..."); pset(base, 6, "...x......x....."); ) : p == 5 ? ( pset(base, 0, "X.........X.......X.....x.X....."); pset(base, 1, "........X...............X......."); pset(base, 2, "........X...............X......."); pset(base, 3, "x.x.x.x.x.x.xxxxx.x.x.x.x.x.xxxx"); pset(base, 7, "X..............................."); ) : p == 6 ? ( pset(base, 0, "X...X...X...X..."); pset(base, 2, "....X.......X..."); pset(base, 3, "x.xx.xx.xx.x"); pset(base, 4, "..x..."); pset(base, 5, "x....x..x."); pset(base, 6, "x..x.x."); pset(base, 7, "X..............................."); ); ); function load_slot(s) ( memcpy(PAT, SLOTS + s * SLN, 264); cur_slot = s; modified = 0; slider4 = s + 1; last_s4 = slider4; ); function save_slot(s) ( memcpy(SLOTS + s * SLN, PAT, 264); cur_slot = s; modified = 0; slider4 = s + 1; last_s4 = slider4; ); // a musical random pattern: each voice has its own idea of where it likes to play function random_pattern() local(v, s, p, q) ( v = 0; loop(8, s = 0; loop(32, q = s % 16; p = v == 0 ? (q % 4 == 0 ? 0.85 : q % 2 == 0 ? 0.15 : 0.06) : v == 1 ? (q % 8 == 4 ? 0.9 : 0.05) : v == 2 ? (q % 8 == 4 ? 0.35 : 0.03) : v == 3 ? (q % 2 == 0 ? 0.75 : 0.3) : v == 4 ? (q % 4 == 2 ? 0.35 : 0.02) : v == 5 ? 0.07 : v == 6 ? 0.12 : (q == 0 ? 0.4 : 0.01); PAT[v * 32 + s] = rand(1) < p ? (rand(1) < 0.25 ? 2 : 1) : 0; s += 1; ); v += 1; ); modified = 1; ); // pattern files: one line per voice, a name then the steps (X accent, x / o hit, . - _ rest); '#' starts a comment function is_step(c) ( c == 'X' || c == 'x' || c == 'O' || c == 'o' || c == '*' || c == '1' || c == '.' || c == '-' || c == '_' || c == '0'; ); function parse_line(row) local(n, i, c, st, v, k) ( n = strlen(#ln); i = 0; while (i < n && ((c = str_getchar(#ln, i)) == ' ' || c == 9)) ( i += 1; ); (i >= n || c == '#' || c == ';') ? row : ( st = i; while (i < n && (c = str_getchar(#ln, i)) != ' ' && c != 9 && c != ':' && c != '=') ( i += 1; ); strcpy_substr(#tok, #ln, st, i - st); v = matchi("k*", #tok) || matchi("bd*", #tok) ? 0 : matchi("cla*", #tok) || matchi("cp*", #tok) ? 2 : matchi("ch*", #tok) || matchi("clo*", #tok) || matchi("cl*", #tok) || matchi("hh*", #tok) ? 3 : matchi("oh*", #tok) || matchi("op*", #tok) ? 4 : matchi("rim*", #tok) || matchi("rs*", #tok) || matchi("p*", #tok) || matchi("cb*", #tok) || matchi("cow*", #tok) ? 6 : matchi("cy*", #tok) || matchi("cr*", #tok) || matchi("ri*", #tok) ? 7 : matchi("s*", #tok) ? 1 : matchi("t*", #tok) || matchi("lt*", #tok) || matchi("mt*", #tok) || matchi("ht*", #tok) ? 5 : -1; // no voice name: a line of steps goes to the next row in order; anything else is ignored v < 0 ? ( c = str_getchar(#ln, st); is_step(c) ? ( v = row; i = st; ) : v = 99; ); v < 8 ? ( k = 0; memset(PAT + v * 32, 0, 32); while (i < n && k < 32 && (is_step(c = str_getchar(#ln, i)) || c == ' ' || c == 9 || c == '|' || c == ':' || c == '=')) ( c == 'X' || c == 'O' ? ( PAT[v * 32 + k] = 2; k += 1; ) : c == 'x' || c == 'o' || c == '*' || c == '1' ? ( PAT[v * 32 + k] = 1; k += 1; ) : c == '.' || c == '-' || c == '_' || c == '0' ? ( k += 1; ); i += 1; ); k > 0 ? LEN[v] = k; v < 8 ? row = v + 1; ); row; ); ); function load_file() local(h, row, guard) ( h = file_open(slider70); h >= 0 ? ( file_text(h); memset(PAT, 0, 256); row = 0; guard = 0; while (file_avail(h) > 0 && guard < 64) ( file_string(h, #ln); row = parse_line(row); guard += 1; ); file_close(h); modified = 1; file_msg = 1; ) : file_msg = 2; ); // ---------- parameters ---------- function update() ( model = slider1; seq_on = slider2; swing = slider3 / 100; midi_out = slider5; chan = slider6; out_ch = chan == 0 ? 9 : chan - 1; vol_t = 10 ^ (slider7 / 20); cthr = slider8; crat = max(slider9, 1); catc = 1 - exp(-1 / (max(slider10, 0.05) * 0.001 * srate)); crlc = 1 - exp(-1 / (slider11 * 0.001 * srate)); cmk = 10 ^ (slider12 / 20); cmix_t = slider13 / 100; ins_output_setup(14); ins_stereo_setup(20); ); ext_noinit = 1; // keep the voices and the playhead when playback starts !pat_init ? ( pat_init = 1; gi = 0; loop(8, factory(SLOTS + gi * SLN, gi); gi += 1; ); load_slot(0); memset(MUTE, 0, 8); ); gi = 0; loop(8, CUR[gi] = -1; NOFF[gi] = 0; gi += 1; ); ost_init(20000); ostL.ost_ch_init(); ostR.ost_ch_init(); sm = 1 - exp(-1 / (0.03 * srate)); lastg = -1; qslot = -1; cenv = 0; cgrm = 0; pkL = pkR = 0; !ui_dflt ? ui_thm = 5; !ui_dflt ? ui_hw = 1; !ui_dflt ? ui_kstyle = 0; PNL = 0x26272B; INKL = 0xE8E6E0; ACC = 0xE0603A; REDL = 0xFF3A2A; AMBL = 0xFFB030; GRNL = 0x3ADF6A; SH1 = 0x202125; SH2 = 0x1D1E22; SH3 = 0x1A1B1F; function vcol(v) ( v == 0 ? 0xE0603A : v == 1 ? 0xE8B23A : v == 2 ? 0xD8D25A : v == 3 ? 0x6FCB7A : v == 4 ? 0x4FB8B0 : v == 5 ? 0x4A9AD8 : v == 6 ? 0x8E7AE0 : 0xD070B8; ); // sequencer grid: x of step s, y of row v (scaled) function sx(s) ( (184 + s * 20 + (s >= 16 ? 6 : 0)) * sc; ); function ry(v) ( (106 + v * 30) * sc; ); function pgroup(x, y, w, h, title, shade) ( pk_inset(x * sc, y * sc, w * sc, h * sc, shade); pk_box(x * sc, y * sc, w * sc, h * sc, ACC, title); C_INK = INKL; ); update(); vol_s = vol_t; cmix_s = cmix_t; last_s4 = slider4; 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(); // knobs moved on the panel do not run @slider, so read them here // MIDI in: queue the drum notes for @sample, pass everything through evn = 0; while (midirecv(mofs, m1, m2, m3)) ( (m1 & 0xF0) == 0x90 && m3 > 0 && (chan == 0 || (m1 & 0x0F) + 1 == chan) && evn < 512 && NMAP[m2] >= 0 ? ( EVQ[evn * 2] = mofs; EVQ[evn * 2 + 1] = NMAP[m2] + 8 * m3; evn += 1; ); midisend(mofs, m1, m2, m3); ); evi = 0; spos = 0; // a slot picked from automation / the parameter list: at the next bar while playing, else now slider4 != last_s4 ? ( last_s4 = slider4; seq_play ? qslot = slider4 - 1 : load_slot(slider4 - 1); ); seq_play = (play_state & 1) && seq_on; bp0 = beat_position; bps = tempo / 60 / srate; !seq_play ? ( lastg = -1; pb_i = 0; loop(8, CUR[pb_i] = -1; pb_i += 1; ); // (not gi: @gfx loops with it) qslot >= 0 ? ( load_slot(qslot); qslot = -1; ); ); aud_v > 0 ? ( v_trig(aud_v - 1, 0.9); aud_v = 0; ); // a voice clicked on screen load_req ? ( load_req = 0; load_file(); ); @serialize file_var(0, ui_sz); file_var(0, pat_init); file_var(0, cur_slot); file_var(0, modified); file_mem(0, PAT, 272); file_mem(0, SLOTS, 8 * SLN); @sample // MIDI drum notes while (evi < evn && EVQ[evi * 2] <= spos) ( e2 = EVQ[evi * 2 + 1]; v_trig(e2 % 8, floor(e2 / 8) / 127); ins_midi_hit(); evi += 1; ); // sequencer, locked to the project's beat position seq_play ? ( sbeat = bp0 + spos * bps; sbeat >= 0 ? ( s16 = sbeat * 4; sg = floor(s16); sfr = s16 - sg; (sg & 1) && sfr < swing * 0.5 ? sg -= 1; // swing: the off-beat 16ths come later sg != lastg ? ( lastg = sg; (sg % 16) == 0 && qslot >= 0 ? ( load_slot(qslot); qslot = -1; ); sv = 0; loop(NV, sst = sg % max(LEN[sv], 1); CUR[sv] = sst; sval = PAT[sv * 32 + sst]; sval > 0 && !MUTE[sv] ? ( v_trig(sv, sval == 2 ? 1 : 0.78); midi_out ? ( NOFF[sv] > 0 ? midisend(spos, 0x80 | out_ch, out_note(sv), 0); midisend(spos, 0x90 | out_ch, out_note(sv), sval == 2 ? 127 : 100); NOFF[sv] = floor(0.03 * srate); ); ); sv += 1; ); ); ); ); // MIDI out: note-offs 30 ms after each hit midi_out ? ( sv = 0; loop(NV, NOFF[sv] > 0 ? ( NOFF[sv] -= 1; NOFF[sv] == 0 ? midisend(spos, 0x80 | out_ch, out_note(sv), 0); ); sv += 1; ); ); spos += 1; // voices y = v0.vrun(); oL = y * v0.gl; oR = y * v0.gr; y = v1.vrun(); oL += y * v1.gl; oR += y * v1.gr; y = v2.vrun(); oL += y * v2.gl; oR += y * v2.gr; y = v3.vrun(); oL += y * v3.gl; oR += y * v3.gr; y = v4.vrun(); oL += y * v4.gl; oR += y * v4.gr; y = v5.vrun(); oL += y * v5.gl; oR += y * v5.gr; y = v6.vrun(); oL += y * v6.gl; oR += y * v6.gr; y = v7.vrun(); oL += y * v7.gl; oR += y * v7.gr; // bus compressor: stereo-linked, 6 dB soft knee; MIX blends it in parallel with the dry kit clv = 20 * log10(max(max(abs(oL), abs(oR)), 0.000001)); cov = clv - cthr; cgr = cov < -3 ? 0 : cov > 3 ? (1 / crat - 1) * cov : (1 / crat - 1) * (cov + 3) * (cov + 3) / 12; cenv += (cgr - cenv) * (cgr < cenv ? catc : crlc); cgrm = min(cgrm, cenv); cmix_s += (cmix_t - cmix_s) * sm; cg = 10 ^ (cenv / 20) * cmk; oL += (oL * cg - oL) * cmix_s; oR += (oR * cg - oR) * cmix_s; vol_s += (vol_t - vol_s) * sm; oL = ostL.ost_band(oL * 0.5) * vol_s; oR = ostR.ost_band(oR * 0.5) * vol_s; ins_stereo(oL, oR); ost_out(ins_sl, ins_sr); // mono-maker / widener, then the output stage spl0 = ost_l; spl1 = ost_r; pkL = max(pkL, abs(spl0)); pkR = max(pkR, abs(spl1)); @gfx 1260 520 ui_begin(1260, 520); // ---------- chassis ---------- fh = 512; // the panel, with the IN / OUT strip along the bottom ui_col(0x0B0C0D); gfx_rect(0, 0, gfx_w, gfx_h); pk_chassis(8 * sc, 8 * sc, 1244 * sc, fh * sc, PNL); C_INK = INKL; // header: name, CHARACTER, display ui_col(INKL); gfx_circle(40 * sc, 38 * sc, 14 * sc, 1, 1); gfx_setfont(4); ui_col(PNL); ui_text(40 * sc, 33 * sc, "GFX"); gfx_setfont(6, "Trebuchet MS", 24 * sc, 'b'); ui_col(INKL); gfx_x = 62 * sc; gfx_y = 24 * sc; gfx_drawstr("G-PULSE"); gfx_setfont(4); ui_cola(INKL, 0.6); gfx_x = 186 * sc; gfx_y = 34 * sc; gfx_drawstr("SYNTHESIS DRUM MACHINE"); gi = 0; loop(4, pk_ledbtn((420 + gi * 70) * sc, 16 * sc, 52 * sc, 32 * sc, slider1 == gi, ACC) ? ( slider1 = gi; ui_auto(1); ); gfx_setfont(4); ui_col(INKL); ui_text((446 + gi * 70) * sc, 52 * sc, cname(gi)); gi += 1; ); pk_lcd(730 * sc, 16 * sc, 270 * sc, 44 * sc, 0); pk_lcdfont(7, 13 * sc); ui_col(pk_lcdc); sprintf(#l1, "%-9s SLOT %d%s", cname(slider1), cur_slot + 1, modified ? "*" : ""); gfx_x = 740 * sc; gfx_y = 22 * sc; gfx_drawstr(#l1); pk_lcdfont(8, 10 * sc); seq_play ? sprintf(#l2, "BAR %d STEP %02d %.1f BPM", floor(max(lastg, 0) / 16) + 1, (max(lastg, 0) % 16) + 1, tempo) : slider2 ? sprintf(#l2, "WAITING FOR TRANSPORT %.1f BPM", tempo) : strcpy(#l2, "SEQUENCER OFF - MIDI ONLY"); gfx_x = 740 * sc; gfx_y = 42 * sc; gfx_drawstr(#l2); ui_size_picker(1050 * sc, 28 * sc); ui_col(ACC); gfx_rect(14 * sc, 70 * sc, 1232 * sc, 3 * sc); // ---------- sequencer ---------- pk_inset(24 * sc, 84 * sc, 1212 * sc, 274 * sc, SH1); gfx_setfont(4); gi = 0; loop(32, ui_cola(INKL, gi % 4 == 0 ? 0.8 : 0.35); sprintf(#sn, "%d", gi + 1); gi % 4 == 0 || gi == 31 ? ui_text(sx(gi) + 8.5 * sc, 90 * sc, #sn); gi += 1; ); ui_cola(INKL, 0.7); gi = 0; loop(6, ui_text((866 + gi * 58) * sc, 90 * sc, gi == 0 ? "TUNE" : gi == 1 ? "DECAY" : gi == 2 ? "TONE" : gi == 3 ? "SNAP" : gi == 4 ? "LEVEL" : "PAN"); gi += 1; ); ui_text(104 * sc, 90 * sc, "VOICE M LEN"); // where the mouse is in the grid gmx = mouse_x / sc - 184; gmx >= 326 ? gmx -= 6; gms = floor(gmx / 20); gmv = floor((mouse_y / sc - 106) / 30); gin = gms >= 0 && gms < 32 && gmv >= 0 && gmv < 8 && (gmx - gms * 20) < 18 && (mouse_y / sc - 106 - gmv * 30) < 27 && mouse_x / sc < 830; // click / drag to paint steps, Shift for accents, right-click to end the row there pressed && drag_id == 0 && gin ? ( drag_id = 4000; gpv = PAT[gmv * 32 + gms] > 0 && !(mouse_cap & 8) ? 0 : (mouse_cap & 8) ? 2 : 1; ); drag_id == 4000 && mdown && gin ? ( PAT[gmv * 32 + gms] != gpv ? ( PAT[gmv * 32 + gms] = gpv; modified = 1; ); ); (mouse_cap & 2) && !pk_prevr && gin ? ( LEN[gmv] = gms + 1; modified = 1; ); gv = 0; loop(8, gy = ry(gv); gc = vcol(gv); // row header: colour bar, name (click to hear it), mute, length (wheel / drag) ui_cola(gc, MUTE[gv] ? 0.3 : 1); gfx_rect(32 * sc, gy, 4 * sc, 27 * sc); VLT[gv] > 0.05 ? ( ui_cola(gc, VLT[gv] * 0.35); gfx_rect(36 * sc, gy, 92 * sc, 27 * sc); ); VLT[gv] *= 0.8; gfx_setfont(2); ui_cola(INKL, MUTE[gv] ? 0.4 : 1); gfx_x = 44 * sc; gfx_y = gy + 6 * sc; gfx_drawstr(vname(gv)); pressed && drag_id == 0 && mouse_x >= 36 * sc && mouse_x < 126 * sc && mouse_y >= gy && mouse_y < gy + 27 * sc ? ( drag_id = -1; aud_v = gv + 1; ); ui_col(MUTE[gv] ? AMBL : 0x3A3B40); ui_rrect(130 * sc, gy + 4 * sc, 20 * sc, 19 * sc, 3 * sc); gfx_setfont(4); ui_col(MUTE[gv] ? 0x1A1A1A : INKL); ui_text(140 * sc, gy + 8 * sc, "M"); pressed && drag_id == 0 && mouse_x >= 130 * sc && mouse_x < 150 * sc && mouse_y >= gy + 4 * sc && mouse_y < gy + 23 * sc ? ( drag_id = -1; MUTE[gv] = !MUTE[gv]; ); ui_col(0x111215); ui_rrect(154 * sc, gy + 4 * sc, 24 * sc, 19 * sc, 3 * sc); sprintf(#ln2, "%d", LEN[gv]); ui_col(gc); ui_text(166 * sc, gy + 8 * sc, #ln2); ghl = mouse_x >= 154 * sc && mouse_x < 178 * sc && mouse_y >= gy + 4 * sc && mouse_y < gy + 23 * sc; ghl && mouse_wheel != 0 && drag_id == 0 ? ( LEN[gv] = min(max(LEN[gv] + sign(mouse_wheel), 1), 32); mouse_wheel = 0; modified = 1; ); pressed && drag_id == 0 && ghl ? ( drag_id = 4100 + gv; glen_y = mouse_y; glen0 = LEN[gv]; ); drag_id == 4100 + gv ? ( LEN[gv] = min(max(glen0 + floor((glen_y - mouse_y) / (6 * sc)), 1), 32); modified = 1; ); // steps gs = 0; loop(32, gx = sx(gs); gval = PAT[gv * 32 + gs]; gon = gs < LEN[gv]; ui_col(gon ? (floor(gs / 4) % 2 ? 0x2E2F34 : 0x34353B) : 0x24252A); ui_rrect(gx, gy, 17 * sc, 27 * sc, 3 * sc); gval > 0 ? ( ui_cola(gc, gon ? (gval == 2 ? 1 : 0.7) : 0.18); ui_rrect(gx + 1 * sc, gy + 1 * sc, 15 * sc, 25 * sc, 2 * sc); gval == 2 && gon ? ( ui_col(0xFFFFFF); gfx_rect(gx + 3 * sc, gy + 3 * sc, 11 * sc, 3 * sc); ); ); CUR[gv] == gs ? ( ui_cola(0xFFFFFF, gval > 0 ? 0.55 : 0.18); ui_rrect(gx, gy, 17 * sc, 27 * sc, 3 * sc); ); gs += 1; ); // the row's end marker ui_col(gc); gx = sx(LEN[gv] - 1) + 18 * sc; gfx_rect(gx, gy + 2 * sc, 2 * sc, 23 * sc); // voice knobs, on a band in the voice's colour (brighter on the row you're touching) gb = 22 + gv * 6; gky = gy + 13.5 * sc; ghot = (drag_id >= gb && drag_id <= gb + 5) || (drag_id == 0 && mouse_x >= 834 * sc && mouse_x < 1222 * sc && mouse_y >= gy && mouse_y < gy + 27 * sc); ui_cola(gc, ghot ? 0.34 : 0.16); ui_rrect(834 * sc, gy, 388 * sc, 27 * sc, 4 * sc); ui_col(gc); gfx_rect(1219 * sc, gy + 3 * sc, 3 * sc, 21 * sc); ui_kmini = 1; ui_section(gv % 2 ? 1 : 3); C_INK = INKL; ui_knob(866 * sc, gky, gb, -24, 24, 0, 0, "", "%+.1f st"); ui_knob(924 * sc, gky, gb + 1, 25, 400, 100, 1, "", "%.0f%%"); ui_knob(982 * sc, gky, gb + 2, 0, 100, 50, 0, "", "%.0f%%"); ui_knob(1040 * sc, gky, gb + 3, 0, 100, 50, 0, "", "%.0f%%"); ui_knob(1098 * sc, gky, gb + 4, 0, 100, gv == 0 ? 85 : gv == 1 ? 75 : gv == 2 ? 70 : gv == 3 ? 60 : gv == 4 ? 55 : gv == 5 ? 70 : gv == 6 ? 60 : 50, 0, "", "%.0f%%"); ui_knob(1156 * sc, gky, gb + 5, -100, 100, gv == 0 || gv == 1 ? 0 : gv == 2 ? -10 : gv == 3 || gv == 4 ? 25 : gv == 5 ? -20 : gv == 6 ? -35 : 35, 0, "", "%+.0f"); ui_kmini = 0; gv += 1; ); gfx_setfont(4); ui_cola(INKL, 0.5); ui_text(506 * sc, 345 * sc, "click a step to set it - drag to paint - shift-click: accent - right-click: end the row there - click a voice to hear it"); // ---------- PATTERN ---------- pgroup(24, 372, 400, 136, "PATTERN", SH2); gi = 0; loop(8, gblink = qslot == gi && (time_precise() * 4 % 2) < 1; pk_ledbtn((38 + gi * 47) * sc, 384 * sc, 38 * sc, 30 * sc, cur_slot == gi || gblink, qslot == gi ? AMBL : ACC) ? ( seq_play ? qslot = gi : ( load_slot(gi); ui_auto(4); ); ); sprintf(#sn, "%d", gi + 1); gfx_setfont(4); ui_col(INKL); ui_text((57 + gi * 47) * sc, 416 * sc, #sn); gi += 1; ); gfx_setfont(4); ui_cola(INKL, 0.75); qslot >= 0 ? sprintf(#ps, "SLOT %d -> SLOT %d AT THE NEXT BAR", cur_slot + 1, qslot + 1) : sprintf(#ps, modified ? "SLOT %d - EDITED (SAVE TO KEEP)" : "SLOT %d", cur_slot + 1); file_msg == 2 ? strcpy(#ps, "COULDN'T OPEN THE PATTERN FILE"); ui_text(224 * sc, 430 * sc, #ps); gi = 0; loop(7, gx = (38 + gi * 54) * sc; pk_ledbtn(gx, 448 * sc, 46 * sc, 28 * sc, gi == 0 && modified, gi == 0 ? AMBL : ACC) ? ( gi == 0 ? save_slot(cur_slot) : gi == 1 ? ( memcpy(CLIP, PAT, 264); clip_ok = 1; ) : gi == 2 ? ( clip_ok ? ( memcpy(PAT, CLIP, 264); modified = 1; ); ) : gi == 3 ? ( memset(PAT, 0, 256); modified = 1; ) : gi == 4 ? random_pattern() : gi == 5 ? ( gfx_x = gx; gfx_y = 476 * sc; gsel = gfx_showmenu("House|Techno|Breakbeat|Electro|Hip-hop|Trap (32 steps)|Polyrhythm|Empty"); gsel > 0 ? ( factory(PAT, gsel - 1); modified = 1; ); ) : ( // pattern files in REAPER/Data/gfx-drum-patterns gcur = slider70; strcpy(#menu, ""); gnf = 0; strcpy(#cprev, ""); gmore = 1; while (gmore && gnf < 64) ( slider70 = gnf; strcpy_fromslider(#cfn, slider70); (strlen(#cfn) == 0 || (gnf > 0 && strcmp(#cfn, #cprev) == 0)) ? gmore = 0 : ( gnf > 0 ? strcat(#menu, "|"); strcat(#menu, #cfn); strcpy(#cprev, #cfn); gnf += 1; ); ); slider70 = gcur; gnf == 0 ? strcpy(#menu, "#No pattern files in REAPER/Data/gfx-drum-patterns"); gfx_x = gx; gfx_y = 476 * sc; gsel = gfx_showmenu(#menu); gsel > 0 && gsel <= gnf ? ( slider70 = gsel - 1; load_req = 1; file_msg = 0; ); ); ); gi == 0 && pk_rclick ? ( // right-click SAVE: into another slot gfx_x = gx; gfx_y = 476 * sc; gsel = gfx_showmenu("Save into slot 1|Save into slot 2|Save into slot 3|Save into slot 4|Save into slot 5|Save into slot 6|Save into slot 7|Save into slot 8"); gsel > 0 ? ( save_slot(gsel - 1); ui_auto(4); ); ); gfx_setfont(4); ui_col(INKL); ui_text(gx + 23 * sc, 480 * sc, gi == 0 ? "SAVE" : gi == 1 ? "COPY" : gi == 2 ? "PASTE" : gi == 3 ? "CLEAR" : gi == 4 ? "RANDOM" : gi == 5 ? "FACTORY" : "LOAD"); gi += 1; ); // ---------- TRANSPORT ---------- pgroup(432, 372, 200, 136, "TRANSPORT", SH2); pk_ledbtn(452 * sc, 384 * sc, 40 * sc, 30 * sc, slider2, GRNL) ? ( slider2 = 1 - slider2; ui_auto(2); ); pk_ledbtn(518 * sc, 384 * sc, 40 * sc, 30 * sc, slider5, REDL) ? ( slider5 = 1 - slider5; ui_auto(5); ); pk_led(596 * sc, 392 * sc, 3 * sc, ins_midi_lit(), GRNL); gfx_setfont(4); ui_col(INKL); ui_text(472 * sc, 418 * sc, "PLAY"); ui_text(538 * sc, 418 * sc, "MIDI OUT"); ui_text(604 * sc, 400 * sc, "IN"); ui_section(2); C_INK = INKL; ui_knob(482 * sc, 464 * sc, 3, 0, 75, 0, 0, "SWING", "%.0f%%"); ui_section(3); C_INK = INKL; knob_step = 1; ui_knob(580 * sc, 464 * sc, 6, 0, 16, 0, 0, "CH", slider6 == 0 ? "OMNI" : "CH %.0f"); // ---------- COMP ---------- pgroup(640, 372, 380, 136, "COMP", SH2); ui_section(1); C_INK = INKL; ui_knob(680 * sc, 436 * sc, 8, -40, 0, -12, 0, "THRESH", "%.1f dB"); ui_knob(736 * sc, 436 * sc, 9, 1, 20, 4, 1, "RATIO", "%.1f:1"); ui_knob(792 * sc, 436 * sc, 10, 0.1, 100, 10, 1, "ATTACK", "%.1f ms"); ui_knob(848 * sc, 436 * sc, 11, 10, 1000, 120, 1, "RELEASE", "%.0f ms"); ui_knob(904 * sc, 436 * sc, 12, 0, 24, 6, 0, "MAKEUP", "+%.1f dB"); ui_section(2); C_INK = INKL; ui_knob(960 * sc, 436 * sc, 13, 0, 100, 0, 0, "MIX", "%.0f%%"); gcg = cgrm; cgrm = 0; gcd = min(gcg, gcd * 0.85); ui_col(0x0A0A0B); gfx_rect(996 * sc, 402 * sc, 12 * sc, 86 * sc); ui_cola(ACC, 0.9); gfx_rect(997 * sc, 402 * sc, 10 * sc, min(-gcd / 20, 1) * 86 * sc); gfx_setfont(4); ui_col(INKL); ui_text(1002 * sc, 492 * sc, "GR"); sprintf(#cg, "%.1f", gcd); ui_text(1002 * sc, 388 * sc, #cg); // ---------- MASTER ---------- pgroup(1028, 372, 208, 136, "MASTER", SH2); ui_section(2); C_INK = INKL; ui_knob(1090 * sc, 436 * sc, 7, -24, 12, 0, 0, "VOLUME", "%+.1f dB"); meL.ui_ledm(1170 * sc, 384 * sc, 12 * sc, 108 * sc, pkL); meR.ui_ledm(1188 * sc, 384 * sc, 12 * sc, 108 * sc, pkR); pkL *= 0.85; pkR *= 0.85; gfx_setfont(4); ui_col(INKL); ui_text(1185 * sc, 496 * sc, "OUT"); // ---------- IN / OUT strip: SAT, OUT, STEREO ---------- // ins_strip(24, 522, 1212, 14, 20, INKL, SH3); pk_end(); ui_end();