desc:GFX G PCM 12 //tags: instrument drum machine pcm 12-bit digital 80s rhythm //author: JGWizrad / Garden FX (GFX) // // Requires gfx-drift, gfx-output, gfx-ui, gfx-panel and gfx-instrument (.jsfx-inc) in the same folder. // // 12-bit PCM drum machine in the spirit of the big mid-80s digital rhythm programmers. // SOUND ROM: 24 drum sounds, synthesized once when the plugin loads and stored as 12-bit PCM at 40 kHz // (no sample files: punchy and gated kicks and snares, rim, clap, four toms, hats, ride, crash, cowbell, // shaker, tambourine, conga, timbale, an electronic tom and a finger snap). // 12 PAD CHANNELS, each playing one ROM sound (one at a time - a new hit restarts the channel), with the // classic voice edit: PITCH, LEVEL, PAN, EG (ATTACK, DECAY 1, DECAY POINT, DECAY 2 - shaping the stored // sound), BEND (the pitch sweeps up or down while the sound plays), REVERSE (the sound runs backwards) // and the MIDI NOTE that plays it. A closed or pedal hat chokes an open hat. // Click a pad to hear it and select it; the EDIT knobs and the LCD follow the selected pad. // DAC: the 12-bit sounds are pitched by resampling, then a gentle output low-pass (DAC FILTER). // KITS (TIGHT FUNK, ELECTRO) sets all 12 pads (sounds and voice edits). The pad set is saved with the project. // PATTERN SEQUENCER (always on the panel: the steps in the voice matrix, PATTERN below): 8 patterns (A-H) x 12 rows (one per pad) x 16 steps, // click a step for off / hit / accent; plays in time with Reaper's transport, SWING; a new pattern starts at // the next bar; COPY / CLEAR; factory patterns for the TIGHT FUNK kit. EXPORT MIDI hands the pattern (with each // pad's note) to the companion ReaScript "GFX G-PCM 12 - export MIDI.lua", which drops it at the edit cursor. // IN / OUT: the strip along the bottom holds SAT, the OUT stage (OFF / CLIP / LIM) and STEREO (MONO, WIDTH). options:gmem=GFX_GPCM12 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-drum.jsfx-inc slider1:0<-24,12,0.1>-Volume (dB) slider2:0<0,16,1>-MIDI channel (0 = omni) slider3:60<0,100,1>-Velocity (%) slider4:14000<4000,20000,10>-DAC filter (Hz) slider5:0<0,100,1>-Band sat (%) slider6:30<0,100,1>-Bias (%) slider7:120<40,400,1>-Sat low band (Hz) slider8:6000<2000,16000,10>-Sat high band (Hz) slider9:-0.3<-24,0,0.1>-Ceiling / limit threshold (dB) slider10:1<0,2,1{Off,Clip,Limit}>-Output stage slider11:20<20,500,1>-Mono below (Hz, 20 = off) slider12:0<0,100,1>-Stereo width (%) // EDIT: the selected pad's voice (the panel loads and stores these per pad) slider13:0<0,23,1>-Edit: sound slider14:0<-24,24,0.1>-Edit: pitch (st) slider15:80<0,100,1>-Edit: level (%) slider16:0<-100,100,1>-Edit: pan (%) slider17:0<0,200,0.1>-Edit: attack (ms) slider18:4000<10,4000,1>-Edit: decay 1 (ms) slider19:100<0,100,1>-Edit: decay point (%) slider20:4000<10,4000,1>-Edit: decay 2 (ms) slider21:0<-100,100,1>-Edit: bend slider22:0<0,1,1{Off,On}>-Edit: reverse slider23:36<24,96,1>-Edit: MIDI note // PATTERN SEQUENCER slider24:0<0,1,1{Off,On}>-Sequencer play (with transport) slider25:0<0,75,1>-Sequencer swing (%) slider26:0<0,7,1{A,B,C,D,E,F,G,H}>-Pattern slider27:0<0,3,1{1 bar,2 bars,4 bars,8 bars}>-Export length in_pin:none out_pin:left output out_pin:right output @init #ui_man = "gfx-g-pcm12"; // the ? pop-up links to this manual NCH = 12; NSND = 24; RS = 40000; // the ROM's sample rate ROM = 200000; // the 12-bit sound ROM (about 17 s of sound) ROFF = 1000; RLEN = 1040; // each sound's start and length in the ROM CHP = 2000; // pad channel settings: ch * 16 (sound, pitch, level, pan, att, d1, point, d2, bend, rev, note) VS = 2300; // voice state: ch * 16 NMAP = 2600; // MIDI note -> channel (128) MQO = 2800; MQN = 3100; MQV = 3400; // this block's note-ons PAT = 4000; // patterns: pat * 192 + row * 16 + step (0 off, 1 hit, 2 accent) ost_init(150000); ostL.ost_ch_init(); ostR.ost_ch_init(); memset(VS, 0, NCH * 16); // --------------------------------------------------------------------------------------------------------- // SOUND ROM: each sound is synthesized into the ROM at RS, peak-normalised, then quantised to 12 bits // --------------------------------------------------------------------------------------------------------- function snd_name(s) ( s == 0 ? "BD 1" : s == 1 ? "BD 2" : s == 2 ? "BD GATED" : s == 3 ? "SD 1" : s == 4 ? "SD GATED" : s == 5 ? "SD TIGHT" : s == 6 ? "RIM" : s == 7 ? "CLAP" : s == 8 ? "TOM 1" : s == 9 ? "TOM 2" : s == 10 ? "TOM 3" : s == 11 ? "TOM 4" : s == 12 ? "HH CLOSED" : s == 13 ? "HH OPEN" : s == 14 ? "HH PEDAL" : s == 15 ? "RIDE" : s == 16 ? "CRASH" : s == 17 ? "COWBELL" : s == 18 ? "SHAKER" : s == 19 ? "TAMBOURINE" : s == 20 ? "CONGA" : s == 21 ? "TIMBALE" : s == 22 ? "ELEC TOM" : "SNAP"; ); // offline helpers (time t in seconds, at RS) function oe(t, tau) ( exp(-t / tau); ); function sqw(ph) ( ph - floor(ph) < 0.5 ? 1 : -1; ); // state-variable filter for building sounds: returns lp / bp / hp in its instance function bset(f, q) instance(g, k, a1, a2, a3) ( g = tan($pi * min(f, RS * 0.45) / RS); k = 1 / q; a1 = 1 / (1 + g * (g + k)); a2 = g * a1; a3 = g * a2; ); function brun(x) instance(a1, a2, a3, k, ic1, ic2, lp, bp, 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; lp = v2; bp = v1; hp = x - k * v1 - v2; bp; ); function breset() instance(ic1, ic2) ( ic1 = ic2 = 0; ); // metal: six squares at inharmonic ratios (r scales them) function metal(t, r) ( (sqw(t * 205.3 * r) + sqw(t * 304.4 * r) + sqw(t * 369.6 * r) + sqw(t * 522.7 * r) + sqw(t * 540 * r) + sqw(t * 800 * r)) / 6; ); // kick: sine with an exponential pitch drop; gate > 0 holds the level then cuts hard (gated) function mk_kick(b, n, f0, f1, tp, ta, click, drive, gate) local(i, t, ph, y, a, g) ( ph = 0; i = 0; loop(n, t = i / RS; ph += (f0 + f1 * oe(t, tp)) / RS; a = gate > 0 ? (t < gate ? 1 - 0.3 * t / gate : max(0, 0.7 - (t - gate) * 60)) : oe(t, ta); y = sin(2 * $pi * ph); drive > 0 ? ( g = 1 + drive; y = (y * g) / (1 + abs(y * g)) * (1 + 1 / g); ); y = y * a + (rand(2) - 1) * click * oe(t, 0.0015); b[i] = y; i += 1; ); ); // snare: two tone modes + filtered noise; gate > 0 holds the noise (gated reverb), room adds a short tail function mk_snare(b, n, f, tt, nt, nlo, nhi, tone, gate) local(i, t, y, nz, a, an) ( sN1.bset(nhi, 0.7); sN2.bset(nlo, 0.7); sN1.breset(); sN2.breset(); i = 0; loop(n, t = i / RS; nz = rand(2) - 1; sN1.brun(nz); sN2.brun(sN1.lp); nz = sN2.hp; an = gate > 0 ? (t < 0.004 ? 1.4 : t < gate ? 0.75 - 0.25 * t / gate : max(0, 0.5 - (t - gate) * 40)) : oe(t, nt); a = oe(t, tt); y = (sin(2 * $pi * f * t * (1 + 0.25 * oe(t, 0.01))) * 0.7 + sin(2 * $pi * f * 1.84 * t) * 0.35) * a * tone + nz * an; b[i] = y; i += 1; ); ); // tom: sine with a pitch fall and a second mode, a little stick noise function mk_tom(b, n, f, ta, fall) local(i, t, ph, ph2, y) ( ph = ph2 = 0; i = 0; loop(n, t = i / RS; ph += f * (1 + fall * oe(t, 0.08)) / RS; ph2 += f * 1.52 * (1 + fall * oe(t, 0.05)) / RS; y = (sin(2 * $pi * ph) + 0.25 * sin(2 * $pi * ph2) * oe(t, ta * 0.4)) * oe(t, ta) + (rand(2) - 1) * 0.3 * oe(t, 0.004); b[i] = y; i += 1; ); ); // hats and cymbals: metal + noise through band-pass / high-pass function mk_cym(b, n, r, nzm, fc, q, ta, att, bell) local(i, t, x, y, e) ( cF.bset(fc, q); cF.breset(); i = 0; loop(n, t = i / RS; x = metal(t, r) * (1 - nzm) + (rand(2) - 1) * nzm; cF.brun(x); e = oe(t, ta) * (att > 0 ? min(t / att, 1) : 1); y = (cF.bp + 0.7 * cF.hp) * e; bell > 0 ? y += bell * (sin(2 * $pi * 2350 * t) + 0.6 * sin(2 * $pi * 3520 * t)) * oe(t, ta * 0.6); b[i] = y; i += 1; ); ); // ---------- acoustic models (cymbals, toms, kicks) ---------- function tanh_(x) ( x = min(max(x, -12), 12); x = exp(2 * x); (x - 1) / (x + 1); ); // a repeatable random generator, so the ROM is the same every time it is built function lcg() ( seed = (seed * 16807) % 2147483647; seed / 2147483647; ); SCR = 1500000; MOD = 1400000; // scratch for the room, partial tables // a short room baked into a sound (the originals were recorded in a studio): a few early reflections, // darkened, mixed in at amt function add_room(b, n, amt) local(i, t, d, g, k, y, lp) ( memset(SCR, 0, n); k = 0; loop(7, d = floor(RS * 0.001 * (k == 0 ? 6.3 : k == 1 ? 9.7 : k == 2 ? 13.1 : k == 3 ? 17.9 : k == 4 ? 23.5 : k == 5 ? 29.4 : 37.0)); g = (k == 0 ? 0.42 : k == 1 ? -0.33 : k == 2 ? 0.28 : k == 3 ? -0.22 : k == 4 ? 0.17 : k == 5 ? -0.12 : 0.08); i = d; loop(n - d, SCR[i] += b[i - d] * g; i += 1; ); k += 1; ); lp = 0; i = 0; loop(n, lp += (SCR[i] - lp) * 0.35; b[i] += lp * amt; i += 1; ); ); // MODAL CYMBAL: np inharmonic partials between flo and fhi (log-spread, random), each a decaying sinusoid // (a two-pole resonator, so it costs two multiplies a sample); high partials decay toward thi, low ones // toward tlo. A noise "wash" (band-passed around nzf) and the stick's click sit on top; att swells it in. function mk_mcym(b, n, np, flo, fhi, tlo, thi, nzamt, nzf, att, click, sd) local(k, f, a, tau, r, w, c1, c2, y0, y1, y2, i, m, u, t, e) ( memset(b, 0, n); seed = sd; k = 0; loop(np, u = lcg(); f = flo * (fhi / flo) ^ u; f < RS * 0.45 ? ( a = (0.4 + 0.6 * lcg()) * (0.5 + 0.5 * u); tau = tlo * (thi / tlo) ^ u * (0.6 + 0.8 * lcg()); r = exp(-1 / (tau * RS)); w = 2 * $pi * f / RS; c1 = 2 * r * cos(w); c2 = -r * r; y1 = 0; y2 = -a * sin(w) / r; // starts at zero, rises as a * r^n * sin(w n) m = min(n, floor(tau * 7 * RS)); i = 0; loop(m, y0 = c1 * y1 + c2 * y2; b[i] += y1; y2 = y1; y1 = y0; i += 1; ); ); k += 1; ); // scale the partials to about unit level, add wash and click, apply the swell cF.bset(nzf, 0.8); cF.breset(); i = 0; loop(n, t = i / RS; u = lcg() * 2 - 1; cF.brun(u); e = att > 0 ? 1 - exp(-t / att) : 1; b[i] = (b[i] / sqrt(np) * 1.6 + (cF.bp + 0.6 * cF.hp) * nzamt * exp(-t / (tlo * 0.5 + thi * 0.5))) * e + (lcg() * 2 - 1) * click * exp(-t / 0.0012); i += 1; ); ); // thin: a 12 dB/oct high-pass over a whole sound (takes the body out of the cymbals) function thin(b, n, fc) local(i) ( cF.bset(fc, 0.707); cF.breset(); i = 0; loop(n, cF.brun(b[i]); b[i] = cF.hp; i += 1; ); ); // TOM: the struck circular head's modes (1, 1.59, 2.14, 2.30, 2.65, 2.92), higher ones dying faster; the // pitch starts a little sharp and settles as the head relaxes; a stick attack and a little room function mk_tom2(b, n, f, ta, rm) local(i, t, k, g, p0, p1, p2, p3, p4, p5, y) ( p0 = p1 = p2 = p3 = p4 = p5 = 0; cF.bset(3800, 1.2); cF.breset(); seed = floor(f * 13); i = 0; loop(n, t = i / RS; g = 1 + 0.07 * exp(-t / 0.07); p0 += f * g / RS; p1 += f * 1.594 * g / RS; p2 += f * 2.136 * g / RS; p3 += f * 2.296 * g / RS; p4 += f * 2.653 * g / RS; p5 += f * 2.918 * g / RS; y = sin(2 * $pi * p0) * exp(-t / ta) + 0.5 * sin(2 * $pi * p1) * exp(-t / (ta * 0.55)) + 0.32 * sin(2 * $pi * p2) * exp(-t / (ta * 0.4)) + 0.22 * sin(2 * $pi * p3) * exp(-t / (ta * 0.35)) + 0.15 * sin(2 * $pi * p4) * exp(-t / (ta * 0.3)) + 0.1 * sin(2 * $pi * p5) * exp(-t / (ta * 0.25)); y += cF.brun(lcg() * 2 - 1) * 0.9 * exp(-t / 0.004); // the stick b[i] = y; i += 1; ); add_room(b, n, rm); ); // GATED REVERB: a small dense room (4 damped combs into 2 all-passes) of the sound itself, held for 'hold' // seconds then cut in 20 ms - the 80s gate - mixed in at mix RVB = 1410000; function rev_gate(b, n, hold, mix) local(i, k, x, y, t, e, c0, c1, c2, c3, f0, f1, f2, f3, i0, i1, i2, i3, a0, a1, j0, j1, l0, l1, l2, l3, m0, m1, w) ( memset(RVB, 0, 8192); l0 = 1116 * 0.8; l1 = 1188 * 0.8; l2 = 1277 * 0.8; l3 = 1356 * 0.8; m0 = 556; m1 = 441; c0 = RVB; c1 = RVB + 1200; c2 = RVB + 2400; c3 = RVB + 3600; a0 = RVB + 4800; a1 = RVB + 5600; f0 = f1 = f2 = f3 = 0; i0 = i1 = i2 = i3 = j0 = j1 = 0; i = 0; loop(n, t = i / RS; x = b[i] * 0.3; y = c0[i0]; f0 = y * 0.6 + f0 * 0.4; c0[i0] = x + f0 * 0.86; i0 += 1; i0 >= l0 ? i0 = 0; w = y; y = c1[i1]; f1 = y * 0.6 + f1 * 0.4; c1[i1] = x + f1 * 0.86; i1 += 1; i1 >= l1 ? i1 = 0; w += y; y = c2[i2]; f2 = y * 0.6 + f2 * 0.4; c2[i2] = x + f2 * 0.86; i2 += 1; i2 >= l2 ? i2 = 0; w += y; y = c3[i3]; f3 = y * 0.6 + f3 * 0.4; c3[i3] = x + f3 * 0.86; i3 += 1; i3 >= l3 ? i3 = 0; w += y; y = a0[j0]; a0[j0] = w + y * 0.5; w = y - w; j0 += 1; j0 >= m0 ? j0 = 0; y = a1[j1]; a1[j1] = w + y * 0.5; w = y - w; j1 += 1; j1 >= m1 ? j1 = 0; e = t < hold ? 1 : max(0, 1 - (t - hold) * 50); SCR[i] = w * e; i += 1; ); i = 0; loop(n, b[i] += SCR[i] * mix; i += 1; ); ); // KICK: a felt beater on a big head - fundamental with a short pitch fall, one shell mode, the beater's // soft click; light saturation only (drv); gated > 0 adds a gated reverb tail (dense, dark, held, then cut) function mk_kick2(b, n, f0, drop, ta, drv, gated, gl) local(i, t, p0, p1, y, e, g) ( p0 = p1 = 0; cF.bset(2600, 0.7); cF.breset(); sN1.bset(2400, 0.7); sN1.breset(); sN2.bset(160, 0.7); sN2.breset(); seed = 4711; i = 0; loop(n, t = i / RS; p0 += f0 * (1 + drop * exp(-t / 0.014)) / RS; p1 += f0 * 1.59 * (1 + drop * exp(-t / 0.01)) / RS; y = sin(2 * $pi * p0) * exp(-t / ta) + 0.25 * sin(2 * $pi * p1) * exp(-t / 0.035); cF.brun(lcg() * 2 - 1); y += cF.lp * 0.7 * exp(-t / 0.003); // the beater drv > 0 ? ( g = 1 + drv * 3; y = tanh_(y * g) / tanh_(g) ; ); b[i] = y; i += 1; ); add_room(b, n, 0.12); gated > 0 ? rev_gate(b, n, gated, gl); ); function build_rom() local(s, o, n, b, i, t, y, pk, ph, x, k) ( o = 0; s = 0; loop(NSND, n = floor(RS * (s == 0 ? 0.5 : s == 1 ? 0.9 : s == 2 ? 0.4 : s == 3 ? 0.5 : s == 4 ? 0.6 : s == 5 ? 0.35 : s == 6 ? 0.15 : s == 7 ? 0.5 : s == 8 ? 0.6 : s == 9 ? 0.7 : s == 10 ? 0.8 : s == 11 ? 1.0 : s == 12 ? 0.15 : s == 13 ? 0.9 : s == 14 ? 0.2 : s == 15 ? 2.5 : s == 16 ? 3.0 : s == 17 ? 0.5 : s == 18 ? 0.25 : s == 19 ? 0.6 : s == 20 ? 0.45 : s == 21 ? 0.7 : s == 22 ? 1.0 : 0.25)); b = ROM + o; ROFF[s] = o; RLEN[s] = n; s == 0 ? mk_kick2(b, n, 56, 0.45, 0.16, 0.25, 0, 0) : // BD 1: punchy studio kick s == 1 ? mk_kick2(b, n, 46, 0.35, 0.32, 0.1, 0, 0) : // BD 2: deep and long s == 2 ? mk_kick2(b, n, 60, 0.5, 0.11, 0.3, 0.24, 1.6) : // BD GATED: tight kick + gated reverb s == 3 ? mk_snare(b, n, 185, 0.05, 0.11, 1800, 9000, 0.8, 0) : // SD 1: studio snare s == 4 ? mk_snare(b, n, 170, 0.06, 0, 900, 8000, 0.7, 0.32) : // SD GATED: the 80s gated-reverb snare s == 5 ? mk_snare(b, n, 240, 0.03, 0.06, 2500, 11000, 0.6, 0) : // SD TIGHT s == 6 ? ( // RIM i = 0; loop(n, t = i / RS; b[i] = sin(2 * $pi * 1700 * t) * 0.7 * oe(t, 0.012) + sin(2 * $pi * 460 * t) * 0.5 * oe(t, 0.01) + (rand(2) - 1) * 0.5 * oe(t, 0.0008); i += 1; ); ) : s == 7 ? ( // CLAP: four strikes, then a tail cF.bset(1250, 1.4); cF.breset(); i = 0; loop(n, t = i / RS; x = rand(2) - 1; cF.brun(x); k = t < 0.009 ? oe(t, 0.003) : t < 0.018 ? oe(t - 0.009, 0.003) : t < 0.028 ? oe(t - 0.018, 0.003) : oe(t - 0.028, 0.12); b[i] = cF.bp * k * 2; i += 1; ); ) : s == 8 ? mk_tom2(b, n, 210, 0.2, 0.25) : s == 9 ? mk_tom2(b, n, 160, 0.26, 0.25) : s == 10 ? mk_tom2(b, n, 120, 0.32, 0.25) : s == 11 ? mk_tom2(b, n, 88, 0.42, 0.25) : s == 12 ? ( mk_mcym(b, n, 70, 3000, 16000, 0.12, 0.07, 1.1, 9000, 0, 0.5, 101); // HH CLOSED: a choked pair i = 0; loop(n, b[i] *= exp(-i / RS / 0.05); i += 1; ); ) : s == 13 ? mk_mcym(b, n, 100, 2800, 16000, 0.9, 0.5, 1.0, 8500, 0, 0.4, 202) : // HH OPEN s == 14 ? ( mk_mcym(b, n, 60, 2200, 11000, 0.15, 0.1, 1.1, 6000, 0.003, 0, 303); // HH PEDAL: the "chick" i = 0; loop(n, b[i] *= exp(-i / RS / 0.06); i += 1; ); ) : s == 15 ? ( mk_mcym(b, n, 110, 1800, 15000, 2.6, 1.6, 0.35, 8000, 0, 0.5, 404); // RIDE: thin and pingy, a light bell i = 0; loop(n, t = i / RS; b[i] += 0.12 * (sin(2 * $pi * 2360 * t) + 0.6 * sin(2 * $pi * 3540 * t)) * exp(-t / 0.7); i += 1; ); thin(b, n, 1200); ) : s == 16 ? ( mk_mcym(b, n, 150, 2000, 16000, 1.8, 1.2, 1.1, 7500, 0.008, 0.3, 505); // CRASH: thin, bright, swelling thin(b, n, 1500); ) : s == 17 ? ( // COWBELL cF.bset(2600, 2.2); cF.breset(); i = 0; loop(n, t = i / RS; cF.brun(sqw(t * 562) + sqw(t * 845)); b[i] = cF.bp * (0.6 * oe(t, 0.01) + 0.4 * oe(t, 0.12)); i += 1; ); ) : s == 18 ? ( // SHAKER cF.bset(7000, 0.8); cF.breset(); i = 0; loop(n, t = i / RS; cF.brun(rand(2) - 1); b[i] = cF.hp * min(t / 0.012, 1) * oe(t, 0.045); i += 1; ); ) : s == 19 ? ( // TAMBOURINE: jingle resonators + shake i = 0; ph = 0; loop(n, t = i / RS; x = (rand(2) - 1) * (t < 0.003 ? 1 : (t >= 0.009 && t < 0.012) ? 0.7 : (t >= 0.02 && t < 0.023) ? 0.5 : 0); b[i] = x; i += 1; ); // (the jingles ring through a resonant band-pass bank) jA.bset(5200, 30); jB.bset(7000, 30); jC.bset(8700, 30); jD.bset(11200, 25); jE.bset(9000, 1); jA.breset(); jB.breset(); jC.breset(); jD.breset(); jE.breset(); i = 0; loop(n, t = i / RS; x = b[i]; b[i] = (jA.brun(x) + jB.brun(x) * 0.8 + jC.brun(x) * 0.7 + jD.brun(x) * 0.5 + jE.brun(x) * 0.3) * oe(t, 0.25); i += 1; ); ) : s == 20 ? ( // CONGA ph = 0; i = 0; loop(n, t = i / RS; ph += 340 * (1 + 0.1 * oe(t, 0.02)) / RS; b[i] = sin(2 * $pi * ph) * oe(t, 0.12) + (rand(2) - 1) * 0.25 * oe(t, 0.003); i += 1; ); ) : s == 21 ? ( // TIMBALE: a ringing shell ph = 0; i = 0; loop(n, t = i / RS; ph += 460 / RS; b[i] = (sin(2 * $pi * ph) + 0.5 * sin(2 * $pi * ph * 1.58) + 0.3 * sin(2 * $pi * ph * 2.45)) * oe(t, 0.18) + (rand(2) - 1) * 0.4 * oe(t, 0.002); i += 1; ); ) : s == 22 ? ( // ELEC TOM: the big downward sweep ph = 0; i = 0; loop(n, t = i / RS; ph += (110 + 260 * oe(t, 0.12)) / RS; b[i] = (sin(2 * $pi * ph) * 0.8 + sqw(ph) * 0.2) * oe(t, 0.3); i += 1; ); ) : ( // SNAP cF.bset(2200, 2); cF.breset(); i = 0; loop(n, t = i / RS; cF.brun(rand(2) - 1); b[i] = cF.bp * 2.5 * oe(t, 0.018) + (rand(2) - 1) * oe(t, 0.0006); i += 1; ); ); // normalise and quantise to 12 bits (a little headroom; the last 15% fades out, so no sound ends with a click) pk = 0; i = 0; loop(n, pk = max(pk, abs(b[i])); i += 1; ); pk > 0 ? ( i = 0; loop(n, y = b[i] * 0.9 / pk * min((n - i) / (n * 0.15), 1); b[i] = floor(y * 2047 + 0.5) / 2047; i += 1; ); ); o += n + 16; s += 1; ); rom_ok = 1; ); !rom_ok ? build_rom(); // ---------- pad channels ---------- function chset(c, snd, pitch, lvl, pan, note) local(b) ( b = CHP + c * 16; b[0] = snd; b[1] = pitch; b[2] = lvl; b[3] = pan; b[4] = 0; b[5] = 4000; b[6] = 100; b[7] = 4000; b[8] = 0; b[9] = 0; b[10] = note; ); function cheg(c, att, d1, pt, d2, bend, rev) local(b) ( b = CHP + c * 16; b[4] = att; b[5] = d1; b[6] = pt; b[7] = d2; b[8] = bend; b[9] = rev; ); function kit_name(k) ( k == 1 ? "ELECTRO" : "TIGHT FUNK"; ); // a kit sets all 12 pads; MIDI notes follow General MIDI where they can NKIT = 2; function load_kit(k) local(c) ( k == 1 ? ( // ELECTRO: electronic toms (raised, shorter, gentle bend), short sounds chset(0, 0, -3, 90, 0, 36); chset(1, 5, 2, 80, 0, 38); chset(2, 7, 0, 75, 10, 39); chset(3, 23, 0, 65, -20, 37); chset(4, 22, 7, 75, 30, 50); chset(5, 22, 3, 75, 0, 47); chset(6, 22, -1, 75, -30, 45); chset(7, 17, 0, 60, 25, 56); chset(8, 12, 2, 55, 20, 42); chset(9, 13, 2, 50, 20, 46); chset(10, 18, 0, 55, -25, 70); chset(11, 2, -5, 85, 0, 35); cheg(0, 0, 160, 0, 4000, -20, 0); cheg(4, 0, 380, 0, 4000, -10, 0); cheg(5, 0, 420, 0, 4000, -10, 0); cheg(6, 0, 480, 0, 4000, -10, 0); ) : ( // TIGHT FUNK: short, dry, snappy chset(0, 0, 1, 90, 0, 36); chset(1, 5, 0, 80, 0, 38); chset(2, 3, 3, 70, 0, 40); chset(3, 6, 0, 65, -20, 37); chset(4, 7, 2, 65, 15, 39); chset(5, 8, 2, 70, 40, 50); chset(6, 9, 2, 70, 10, 47); chset(7, 10, 2, 70, -20, 45); chset(8, 12, 0, 60, 30, 42); chset(9, 13, 0, 55, 30, 46); chset(10, 14, 0, 55, 30, 44); chset(11, 17, 0, 55, -30, 56); cheg(0, 0, 220, 30, 120, 0, 0); cheg(1, 0, 150, 40, 90, 0, 0); cheg(9, 0, 200, 40, 160, 0, 0); ); cur_kit = k; ed_load = 1; ); // ---------- voices ---------- function dcoef(ms) ( exp(-4.6 / (max(ms, 1) * 0.001 * srate)); ); // ms = time to fall 40 dB function trig(c, vel) local(b, v, s, j, w) ( b = CHP + c * 16; v = VS + c * 16; s = b[0]; // a closed or pedal hat chokes any ringing open hat s == 12 || s == 14 ? ( j = 0; loop(NCH, w = VS + j * 16; w[0] && CHP[j * 16] == 13 ? w[11] = 1; j += 1; ); ); v[0] = 1; v[11] = 0; v[2] = RLEN[s]; v[8] = ROM + ROFF[s]; v[9] = b[9]; // reverse v[1] = b[9] ? RLEN[s] - 1 : 0; // position v[3] = 2 ^ (b[1] / 12) * RS / srate; // step per output sample v[4] = b[8] / 25 / srate; // bend: octaves per output sample v[5] = 0; // bend so far (octaves) v[6] = (1 - velsens + velsens * vel) * sqr(b[2] / 100); // level v[10] = b[3] / 100; // pan // EG: attack ramp, decay 1 toward the point, decay 2 to silence ("FULL" decays leave the sound as it is) b[4] > 0 ? ( v[12] = 1 / (b[4] * 0.001 * srate); v[14] = 1; v[15] = 0; ) : ( v[14] = 2; v[15] = 1; ); v[7] = b[5] >= 4000 ? 1 : dcoef(b[5]); EGP[c] = b[6] / 100; EG2[c] = b[7] >= 4000 ? 1 : dcoef(b[7]); lt[c] = 1; ); EGP = 3800; EG2 = 3820; lt = 3840; // per pad: EG point, decay-2 coefficient, pad light // ---------- parameters ---------- function update() ( vol_t = 10 ^ (slider1 / 20); chan = slider2; velsens = slider3 / 100; dac_g = tan($pi * min(slider4, srate * 0.45) / srate); dac_a1 = 1 / (1 + dac_g * (dac_g + 1.414)); dac_a2 = dac_g * dac_a1; dac_a3 = dac_g * dac_a2; chk_c = exp(-1 / (0.001 * srate)); sq_play = slider24; sq_swing = slider25 / 100 * 0.5; ins_output_setup(5); ins_stereo_setup(11); ); // the EDIT sliders <-> the selected pad (the panel calls these) function ed_to_sliders() local(b) ( b = CHP + sel * 16; slider13 = b[0]; slider14 = b[1]; slider15 = b[2]; slider16 = b[3]; slider17 = b[4]; slider18 = b[5]; slider19 = b[6]; slider20 = b[7]; slider21 = b[8]; slider22 = b[9]; slider23 = b[10]; ); function sliders_to_ed() local(b) ( b = CHP + sel * 16; b[0] = slider13; b[1] = slider14; b[2] = slider15; b[3] = slider16; b[4] = slider17; b[5] = slider18; b[6] = slider19; b[7] = slider20; b[8] = slider21; b[9] = slider22; b[10] = slider23; ); // ---------- factory patterns (rows follow the TIGHT FUNK kit: BD, SD TIGHT, SD 1, RIM, CLAP, TOM 1-3, HH C, HH O, // HH PEDAL, COWBELL; X accent, x hit, . rest) ---------- function prow(p, r, str) local(i, c) ( i = 0; loop(16, c = str_getchar(str, i); PAT[p * 192 + r * 16 + i] = c == 'X' ? 2 : c == 'x' ? 1 : 0; i += 1; ); ); function factory_patterns() ( memset(PAT, 0, 8 * 192); // A 80s ROCK prow(0, 0, "X.......X.x....."); prow(0, 2, "....X.......X..."); prow(0, 8, "x.x.x.x.x.x.x.x."); prow(0, 10, "X..............."); // B FUNK prow(1, 0, "X..x..x...X..x.."); prow(1, 1, "....X..x.x..X..."); prow(1, 8, "xxXxxxXxxxXxxxXx"); prow(1, 9, "..............x."); // C ELECTRO prow(2, 0, "X.....X...X....."); prow(2, 4, "....X.......X..."); prow(2, 8, "x.x.x.x.x.x.x.x."); prow(2, 11, "..x..x....x..x.."); // D DISCO prow(3, 0, "X...X...X...X..."); prow(3, 1, "....X.......X..."); prow(3, 8, "x...x...x...x..."); prow(3, 9, "..X...X...X...X."); // E GATED BALLAD prow(4, 0, "X.......x.X....."); prow(4, 2, "....X.......X..."); prow(4, 8, "x.x.x.x.x.x.x.x."); prow(4, 3, "..............x."); // F TOM FILL prow(5, 0, "X..............."); prow(5, 5, "X.x.x..........."); prow(5, 6, "......X.x.x....."); prow(5, 7, "............X.XX"); prow(5, 10, "X..............."); // G CLAPS + COWBELL prow(6, 0, "X...X...X...X..."); prow(6, 4, "....X.......X..x"); prow(6, 11, "x.xx.x.xx.xx.x.x"); prow(6, 8, "..x...x...x...x."); // H (empty) ); function pat_name(p) ( p == 0 ? "80S ROCK" : p == 1 ? "FUNK" : p == 2 ? "ELECTRO" : p == 3 ? "DISCO" : p == 4 ? "GATED BALLAD" : p == 5 ? "TOM FILL" : p == 6 ? "CLAPS + BELL" : "USER"; ); // export: gmem[1] bars, [2] steps (16), [3] swing, [4] pending, [5] rows (12), [10 + row * 16 + step] steps, // [300 + row] each pad's MIDI note function export_midi() local(i) ( i = 0; loop(192, gmem[10 + i] = PAT[cur_pat * 192 + i]; i += 1; ); i = 0; loop(NCH, gmem[300 + i] = CHP[i * 16 + 10]; i += 1; ); gmem[1] = slider27 == 0 ? 1 : slider27 == 1 ? 2 : slider27 == 2 ? 4 : 8; gmem[2] = 16; gmem[3] = sq_swing; gmem[5] = NCH; gmem[0] += 1; gmem[4] = 1; exp_t = time_precise(); ); !init_done ? ( load_kit(0); sel = 0; factory_patterns(); init_done = 1; ); lastkey = -1; cur_step = -1; cur_pat = slider26; sm = 1 - exp(-1 / (0.03 * srate)); pkL = pkR = 0; dz1L = dz2L = dz1R = dz2R = 0; !ui_dflt ? ui_thm = 5; !ui_dflt ? ui_hw = 1; !ui_dflt ? ui_kstyle = 0; BLK = 0x16171A; SILK = 0xE6E8EC; CYAN = 0x5CC8E0; AMBR = 0xFFB040; REDL = 0xFF4A3A; SH1 = 0x1E2024; SH2 = 0x1B1D21; 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, ui_mix(CYAN, SILK, 0.35), title); ui_section(sec == 0 ? 2 : sec); C_INK = SILK; ); update(); vol_s = vol_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 update(); // MIDI note -> pad (the first pad set to that note) gi_b = 0; loop(128, NMAP[gi_b] = -1; gi_b += 1; ); gi_b = NCH - 1; loop(NCH, NMAP[CHP[gi_b * 16 + 10]] = gi_b; gi_b -= 1; ); m_cnt = 0; while (midirecv(mofs, m1, m2, m3)) ( (m1 & 0xF0) == 0x90 && m3 > 0 && ins_chan_ok(m1, chan) && m_cnt < 256 ? ( MQO[m_cnt] = mofs; MQN[m_cnt] = m2; MQV[m_cnt] = m3 / 127; m_cnt += 1; ins_midi_hit(); ); midisend(mofs, m1, m2, m3); ); m_idx = 0; spos = 0; aud > 0 ? ( trig(aud - 1, aud_vel); aud = 0; ); // a pad clicked on the panel playing = (play_state & 1) && sq_play; bp0 = beat_position; bps = tempo / 60 / srate; !playing ? ( lastkey = -1; cur_step = -1; cur_pat = slider26; ); // stopped: a new pattern applies at once @serialize file_var(0, ui_sz); file_var(0, cur_kit); file_var(0, sel); file_mem(0, CHP, NCH * 16); // (show_seq: the old fold-out flag, kept so saved projects still load) file_var(0, show_seq); file_mem(0, PAT, 8 * 192); ed_load = 1; @sample while (m_idx < m_cnt && MQO[m_idx] <= spos) ( NMAP[MQN[m_idx]] >= 0 ? trig(NMAP[MQN[m_idx]], MQV[m_idx]); m_idx += 1; ); // pattern sequencer, locked to the project's beat position (a new pattern takes over at the next bar) playing ? ( q_b = bp0 + spos * bps; q_b >= 0 ? ( q_bar = floor(q_b / 4); q_s = (q_b - q_bar * 4) * 4; q_st = floor(q_s); q_fr = q_s - q_st; q_eff = (q_st & 1) && q_fr < sq_swing ? q_st - 1 : q_st; q_key = q_bar * 32 + q_eff; q_key != lastkey ? ( q_eff == 0 ? cur_pat = slider26; lastkey = q_key; cur_step = q_eff; q_r = 0; loop(NCH, q_v = PAT[cur_pat * 192 + q_r * 16 + q_eff]; q_v ? trig(q_r, q_v == 2 ? 1 : 0.7); q_r += 1; ); ); ); ); spos += 1; oL = 0; oR = 0; sc_i = 0; loop(NCH, sv = VS + sc_i * 16; sv[0] ? ( // resampled read of the 12-bit sound (linear interpolation), forwards or backwards sp_ = sv[1]; si_ = floor(sp_); sf_ = sp_ - si_; sb_ = sv[8]; sy_ = sb_[si_] + sf_ * (sb_[min(si_ + 1, sv[2] - 1)] - sb_[si_]); // EG sv[11] ? sv[15] *= chk_c : // choked: a 1 ms fade sv[14] == 1 ? ( sv[15] += sv[12]; sv[15] >= 1 ? ( sv[15] = 1; sv[14] = 2; ); ) : sv[14] == 2 ? ( sv[15] = EGP[sc_i] + (sv[15] - EGP[sc_i]) * sv[7]; abs(sv[15] - EGP[sc_i]) < 0.002 ? sv[14] = 3; ) : sv[15] *= EG2[sc_i]; sy_ *= sv[15] * sv[6]; // step on: pitch x bend sv[5] += sv[4]; sst_ = sv[3] * 2 ^ sv[5]; sv[1] += sv[9] ? -sst_ : sst_; (sv[1] < 0 || sv[1] >= sv[2] - 1 || (sv[15] < 0.0001 && sv[14] != 1)) ? sv[0] = 0; // pan (constant power, 0 dB centre) oL += sy_ * sqrt(1 - sv[10]); oR += sy_ * sqrt(1 + sv[10]); ); sc_i += 1; ); // the DAC's output low-pass (12 dB/oct) dv3 = oL - dz2L; dv1 = dac_a1 * dz1L + dac_a2 * dv3; dv2 = dz2L + dac_a2 * dz1L + dac_a3 * dv3; dz1L = 2 * dv1 - dz1L; dz2L = 2 * dv2 - dz2L; oL = dv2; dv3 = oR - dz2R; dv1 = dac_a1 * dz1R + dac_a2 * dv3; dv2 = dz2R + dac_a2 * dz1R + dac_a3 * dv3; dz1R = 2 * dv1 - dz1R; dz2R = 2 * dv2 - dz2R; oR = dv2; vol_s += (vol_t - vol_s) * sm; oL = ostL.ost_band(oL * 0.7) * vol_s; oR = ostR.ost_band(oR * 0.7) * vol_s; ins_stereo(oL, oR); ost_out(ins_sl, ins_sr); spl0 = ost_l; spl1 = ost_r; pkL = max(pkL, abs(spl0)); pkR = max(pkR, abs(spl1)); @gfx 1180 588 // --------------------------------------------------------------------------------------------------------- // GFX drum machine panel (the G-Pulse family): header with the LCD, the voice matrix (pad tab, 16 steps, the // pad's PITCH / LEVEL / PAN / DECAY / BEND / REVERSE on a band in its colour), then VOICE EDIT (the selected // pad's full EG and note), PATTERN and MASTER. Click a pad's name to hear and select it; its arrow picks the sound. // --------------------------------------------------------------------------------------------------------- fh = 582; ui_thm = 5; ui_hw = 1; ui_begin(1180, fh + 6); C_ACC = CYAN; C_S0 = 0x4FB8D8; C_S2 = 0xA8E2F0; C_KC = 0xF0F2F4; ed_load ? ( ed_to_sliders(); ed_load = 0; ); ui_col(0x090A0B); gfx_rect(0, 0, gfx_w, gfx_h); pk_chassis(8 * sc, 8 * sc, 1164 * sc, fh * sc, BLK); C_INK = SILK; dr_header("G-PCM 12", "12-BIT PCM DRUM MACHINE", SILK, BLK, CYAN, 1180); // LCD (cool backlight): the selected pad and the pattern ui_col(0x0A0B0C); ui_rrect(420 * sc, 14 * sc, 330 * sc, 48 * sc, 4 * sc); gfx_gradrect(425 * sc, 18 * sc, 320 * sc, 40 * sc, 0.50, 0.80, 0.78, 1, 0, 0, 0, 0, -0.12 / (40 * sc), -0.14 / (40 * sc), -0.14 / (40 * sc), 0); gb = CHP + sel * 16; pk_lcdfont(7, 13 * sc); ui_col(0x0C2228); sprintf(#l1, "PAD %02d %-10s KIT %s", sel + 1, snd_name(gb[0]), kit_name(cur_kit)); gfx_x = 432 * sc; gfx_y = 21 * sc; gfx_drawstr(#l1); pk_lcdfont(8, 10 * sc); gpl = slider26; sprintf(#l2, "PAT %s %-12s %s", gpl == 0 ? "A" : gpl == 1 ? "B" : gpl == 2 ? "C" : gpl == 3 ? "D" : gpl == 4 ? "E" : gpl == 5 ? "F" : gpl == 6 ? "G" : "H", pat_name(gpl), playing ? "PLAYING" : slider24 ? "WAITING FOR PLAY" : "SEQ OFF"); cur_step >= 0 && playing ? sprintf(#l2, "PAT %s STEP %02d BAR - %.1f BPM", gpl == 0 ? "A" : gpl == 1 ? "B" : gpl == 2 ? "C" : gpl == 3 ? "D" : gpl == 4 ? "E" : gpl == 5 ? "F" : gpl == 6 ? "G" : "H", cur_step + 1, tempo); gfx_x = 432 * sc; gfx_y = 41 * sc; gfx_drawstr(#l2); function kit_menu(x, y) local(i, k) ( #km = ""; i = 0; loop(NKIT, cur_kit == i ? strcat(#km, "!"); strcat(#km, kit_name(i)); i < NKIT - 1 ? strcat(#km, "|"); i += 1; ); gfx_x = x; gfx_y = y; k = gfx_showmenu(#km); k > 0 ? load_kit(k - 1); ); ui_noled = 1; ui_button(766 * sc, 22 * sc, 92 * sc, 30 * sc, "KITS", 0, 1) ? kit_menu(766 * sc, 52 * sc); ui_noled = 0; pk_led(884 * sc, 38 * sc, 3 * sc, ins_midi_lit(), CYAN); gfx_setfont(4); ui_col(SILK); gfx_x = 892 * sc; gfx_y = 33 * sc; gfx_drawstr("MIDI"); ui_size_picker(960 * sc, 28 * sc); // ---------- the voice matrix ---------- pk_inset(24 * sc, 84 * sc, 1132 * sc, 352 * sc, SH1); function psx(s) ( 232 + s * 29 + floor(s / 4) * 6; ); function pry(r) ( 108 + r * 26; ); gfx_setfont(4); ui_cola(SILK, 0.7); gfx_x = 44 * sc; gfx_y = 90 * sc; gfx_drawstr("PAD SOUND NOTE"); gi = 0; loop(16, ui_cola(SILK, gi % 4 == 0 ? 0.85 : 0.35); sprintf(#sn, "%d", gi + 1); ui_text((psx(gi) + 13) * sc, 90 * sc, #sn); gi += 1; ); ui_cola(SILK, 0.7); gi = 0; loop(6, ui_text((756 + gi * 62) * sc, 90 * sc, gi == 0 ? "PITCH" : gi == 1 ? "LEVEL" : gi == 2 ? "PAN" : gi == 3 ? "DECAY" : gi == 4 ? "BEND" : "REV"); gi += 1; ); ui_text(1120 * sc, 90 * sc, "NOTE"); gpp = slider26 * 192; gr = 0; loop(NCH, gy = pry(gr); gc = dr_vcol(gr); gb = CHP + gr * 16; sprintf(#pn, "%d %s", gr + 1, snd_name(gb[0])); sprintf(#ps, "%d", gb[10]); dr_tab(32, gy, 172, 23, gc, #pn, #ps, lt[gr], sel == gr) ? ( aud = gr + 1; aud_vel = 0.9; sel != gr ? ( sel = gr; ed_load = 1; ); ); lt[gr] *= 0.85; // the sound arrow ginb = mouse_x >= 206 * sc && mouse_x < 222 * sc && mouse_y >= gy * sc && mouse_y < (gy + 23) * sc; ui_col(ginb ? 0x4A4C52 : 0x34353B); ui_rrect(206 * sc, gy * sc, 16 * sc, 23 * sc, 3 * sc); ui_col(SILK); gfx_triangle(210 * sc, (gy + 9) * sc, 218 * sc, (gy + 9) * sc, 214 * sc, (gy + 14) * sc); pressed && drag_id == 0 && ginb ? ( drag_id = -1; #sm = ""; gi = 0; loop(NSND, gb[0] == gi ? strcat(#sm, "!"); strcat(#sm, snd_name(gi)); gi < NSND - 1 ? strcat(#sm, "|"); gi += 1; ); gfx_x = 206 * sc; gfx_y = (gy + 23) * sc; gsel = gfx_showmenu(#sm); gsel > 0 ? ( gb[0] = gsel - 1; gr == sel ? ed_load = 1; aud = gr + 1; aud_vel = 0.9; ); ); // steps (click cycles off / hit / accent) gs = 0; loop(16, gx = psx(gs); gv = PAT[gpp + gr * 16 + gs]; dr_cell(gx, gy, 26, 23, gv, gc, cur_step == gs && cur_pat == slider26 && playing, floor(gs / 4) % 2 == 0); pressed && drag_id == 0 && mouse_x >= gx * sc && mouse_x < (gx + 26) * sc && mouse_y >= gy * sc && mouse_y < (gy + 23) * sc ? ( drag_id = -1; PAT[gpp + gr * 16 + gs] = (gv + 1) % 3; ); gs += 1; ); // the pad's knobs on its band ghot = mouse_x >= 726 * sc && mouse_x < 1148 * sc && mouse_y >= gy * sc && mouse_y < (gy + 23) * sc; dr_band(726, gy, 422, 23, gc, ghot || sel == gr); gch = 0; gky = gy + 11.5; ui_section(gr % 2 ? 1 : 3); gch += dr_mknob(756, gky, gb + 1, -24, 24, 0, 0, 0, "%+.1f st", gr * 8); gch += dr_mknob(818, gky, gb + 2, 0, 100, 80, 0, 1, "%.0f", gr * 8 + 1); gch += dr_mknob(880, gky, gb + 3, -100, 100, 0, 0, 1, "%+.0f", gr * 8 + 2); gch += dr_mknob(942, gky, gb + 5, 10, 4000, 4000, 1, 0, "%.0f ms", gr * 8 + 3); gch += dr_mknob(1004, gky, gb + 8, -100, 100, 0, 0, 1, "%+.0f", gr * 8 + 4); // reverse: a small key grv = mouse_x >= 1052 * sc && mouse_x < 1080 * sc && mouse_y >= (gy + 4) * sc && mouse_y < (gy + 19) * sc; ui_col(gb[9] ? AMBR : 0x34353B); ui_rrect(1052 * sc, (gy + 4) * sc, 28 * sc, 15 * sc, 3 * sc); gfx_setfont(4); ui_col(gb[9] ? 0x1A1A1A : SILK); ui_text(1066 * sc, (gy + 7) * sc, "REV"); pressed && drag_id == 0 && grv ? ( drag_id = -1; gb[9] = !gb[9]; gch += 1; ); gch += dr_mknob(1120, gky, gb + 10, 24, 96, 36, 0, 1, "%.0f", gr * 8 + 5); gch > 0 && gr == sel ? ed_load = 1; gr += 1; ); gfx_setfont(4); ui_cola(SILK, 0.45); ui_text(590 * sc, 424 * sc, "click a pad's name to hear and edit it - its arrow picks the sound - click a step: off > hit > accent - double-click a knob to reset it"); // ---------- VOICE EDIT (the selected pad) ---------- function sgroup(x, y, w, h, title) ( pk_inset(x * sc, y * sc, w * sc, h * sc, SH2); pk_box(x * sc, y * sc, w * sc, h * sc, ui_mix(CYAN, SILK, 0.35), title); C_INK = SILK; ); sprintf(#vt, "VOICE EDIT - PAD %d %s", sel + 1, snd_name(slider13)); sgroup(24, 452, 420, 122, #vt); ui_section(1); C_INK = SILK; ui_knob(66 * sc, 512 * sc, 17, 0, 200, 0, 0, "ATTACK", "%.1f ms"); ui_knob(130 * sc, 512 * sc, 18, 10, 4000, 4000, 1, "DECAY 1", slider18 >= 4000 ? "FULL" : "%.0f ms"); ui_knob(194 * sc, 512 * sc, 19, 0, 100, 100, 0, "D.POINT", "%.0f%%"); ui_knob(258 * sc, 512 * sc, 20, 10, 4000, 4000, 1, "DECAY 2", slider20 >= 4000 ? "FULL" : "%.0f ms"); ui_section(2); C_INK = SILK; ui_knob(322 * sc, 512 * sc, 21, -100, 100, 0, 0, "BEND", slider21 == 0 ? "OFF" : slider21 < 0 ? "DOWN %.0f" : "UP %.0f"); pk_ledbtn(368 * sc, 478 * sc, 40 * sc, 34 * sc, slider22, AMBR) ? ( slider22 = 1 - slider22; slider_automate(2 ^ 21); ); gfx_setfont(4); ui_col(SILK); ui_text(388 * sc, 520 * sc, "REVERSE"); !ed_load ? sliders_to_ed(); // ---------- PATTERN ---------- sgroup(452, 452, 436, 122, "PATTERN"); pk_ledbtn(466 * sc, 470 * sc, 40 * sc, 32 * sc, slider24, CYAN) ? ( slider24 = 1 - slider24; slider_automate(2 ^ 23); ); gfx_setfont(4); ui_col(SILK); ui_text(486 * sc, 508 * sc, "PLAY"); gi = 0; loop(8, gblink = slider26 == gi && cur_pat != gi && playing && (time_precise() * 4 % 2) < 1; pk_ledbtn((520 + gi * 45) * sc, 470 * sc, 38 * sc, 32 * sc, (slider26 == gi && !gblink), AMBR) ? ( slider26 = gi; slider_automate(2 ^ 25); ); gfx_setfont(4); ui_col(SILK); ui_text((539 + gi * 45) * sc, 508 * sc, gi == 0 ? "A" : gi == 1 ? "B" : gi == 2 ? "C" : gi == 3 ? "D" : gi == 4 ? "E" : gi == 5 ? "F" : gi == 6 ? "G" : "H"); gi += 1; ); ui_section(1); C_INK = SILK; ui_kmini = 1; ui_knob(488 * sc, 546 * sc, 25, 0, 75, 0, 0, "", "%.0f%%"); ui_kmini = 0; gfx_setfont(4); ui_col(SILK); gfx_x = 504 * sc; gfx_y = 542 * sc; gfx_drawstr("SWING"); ui_section(2); C_INK = SILK; ui_noled = 1; ui_button(552 * sc, 534 * sc, 66 * sc, 24 * sc, "COPY TO", 0, 1) ? ( gfx_x = 552 * sc; gfx_y = 558 * sc; gsel = gfx_showmenu("A|B|C|D|E|F|G|H"); gsel > 0 ? ( gi = 0; loop(192, PAT[(gsel - 1) * 192 + gi] = PAT[slider26 * 192 + gi]; gi += 1; ); ); ); ui_button(622 * sc, 534 * sc, 54 * sc, 24 * sc, "CLEAR", 0, 1) ? memset(PAT + slider26 * 192, 0, 192); gi = 0; loop(4, ui_keycap((690 + gi * 28) * sc, 534 * sc, 25 * sc, 24 * sc, gi == 0 ? "1" : gi == 1 ? "2" : gi == 2 ? "4" : "8", slider27 == gi, 1) ? ( slider27 = gi; slider_automate(2 ^ 26); ); gi += 1; ); ui_button(806 * sc, 534 * sc, 74 * sc, 24 * sc, "EXPORT", time_precise() - exp_t < 0.6, 1) ? export_midi(); ui_noled = 0; gfx_setfont(4); ui_cola(SILK, 0.6); ui_text(746 * sc, 563 * sc, "BARS"); ui_text(843 * sc, 563 * sc, gmem[4] && time_precise() - exp_t > 1.5 && exp_t > 0 ? "run script" : "MIDI"); // ---------- MASTER ---------- sgroup(896, 452, 260, 122, "MASTER"); ui_section(2); C_INK = SILK; ui_knob(932 * sc, 512 * sc, 1, -24, 12, 0, 0, "VOLUME", "%.1f dB"); ui_knob(990 * sc, 512 * sc, 3, 0, 100, 60, 0, "VELOCITY", "%.0f%%"); ui_section(1); C_INK = SILK; ui_knob(1048 * sc, 512 * sc, 4, 4000, 20000, 14000, 1, "DAC", "%.0f Hz"); ui_section(3); C_INK = SILK; knob_step = 1; ui_kmini = 1; ui_knob(1048 * sc, 558 * sc, 2, 0, 16, 0, 0, "", slider2 == 0 ? "OMNI" : "CH %.0f"); ui_kmini = 0; gfx_setfont(4); ui_col(SILK); gfx_x = 1008 * sc; gfx_y = 554 * sc; gfx_drawstr("CH"); mL.ui_ledm(1104 * sc, 466 * sc, 12 * sc, 96 * sc, pkL); mR.ui_ledm(1122 * sc, 466 * sc, 12 * sc, 96 * sc, pkR); pkL *= 0.85; pkR *= 0.85; // ---------- IN / OUT strip: SAT, OUT, STEREO ---------- // ins_strip(24, 588, 1132, 5, 11, SILK, SH2); pk_end(); ui_end();