desc:GFX G Clockwork //tags: instrument drum machine rhythm box analogue preset rhythms //author: JGWizrad / Garden FX (GFX) // // Requires gfx-drift, gfx-output, gfx-ui, gfx-panel and gfx-instrument (.jsfx-inc) in the same folder. // // Analogue rhythm box in the spirit of the late-70s preset-rhythm machines. // - 15 voices, all synthesised (no samples): kick, snare, rimshot, closed / open hat, cymbal, // bongos, conga, clave, maracas, guiro, tambourine, cowbell, metal beat. // Each has TUNE, DECAY and LEVEL (the VOICES key opens them); click its pad to hear it. // - PRESETS picks a rhythm pattern; KITS re-voices the whole kit (tune, decay and the balance). // - The theme swatch and the HW key at the top change the skin. // - Play it from MIDI (General MIDI drum notes), and / or // - RHYTHM: 16 preset rhythms that follow Reaper's tempo and play while the transport runs // (PLAY on). VAR B adds the variation parts; FILL drops in a fill every 2 / 4 / 8 bars; // ACCENT sets how much the accented hits stand out; SWING delays the off-beat 16ths. // - DRIFT: the GFX instrument drift (tuning, tone and decay wander slowly; each hit is tuned slightly // differently). HITS varies each hit (tune, decay, level); TIMING loosens the rhythm. // MIDI map: 35/36 kick, 37 rim, 38/40 snare, 42/44 closed hat, 46 open hat, 49/51/52/55/57/59 cymbal, // 54 tambourine, 56 cowbell, 60/61 bongos, 62-64 conga, 70 maracas, 73/74 guiro, 75 clave, 80/81 metal beat. 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<0,31,1>-Rhythm slider2:0<0,1,1{Off,On}>-Play rhythm (with transport) slider3:0<0,1,1{A,B}>-Variation slider4:0<0,3,1{Off,Every 2 bars,Every 4 bars,Every 8 bars}>-Fill slider5:0<0,100,1>-Swing (%) slider6:50<0,100,1>-Accent (%) slider7:0<-12,12,0.1>-Kick tune (st) slider8:100<25,400,1>-Kick decay (%) slider9:90<0,100,1>-Kick level (%) slider10:0<-12,12,0.1>-Snare tune (st) slider11:100<25,400,1>-Snare decay (%) slider12:75<0,100,1>-Snare level (%) slider13:0<-12,12,0.1>-Rimshot tune (st) slider14:100<25,400,1>-Rimshot decay (%) slider15:60<0,100,1>-Rimshot level (%) slider16:0<-12,12,0.1>-Closed hat tune (st) slider17:100<25,400,1>-Closed hat decay (%) slider18:60<0,100,1>-Closed hat level (%) slider19:0<-12,12,0.1>-Open hat tune (st) slider20:100<25,400,1>-Open hat decay (%) slider21:55<0,100,1>-Open hat level (%) slider22:0<-12,12,0.1>-Cymbal tune (st) slider23:100<25,400,1>-Cymbal decay (%) slider24:50<0,100,1>-Cymbal level (%) slider25:0<-12,12,0.1>-Bongo high tune (st) slider26:100<25,400,1>-Bongo high decay (%) slider27:65<0,100,1>-Bongo high level (%) slider28:0<-12,12,0.1>-Bongo low tune (st) slider29:100<25,400,1>-Bongo low decay (%) slider30:65<0,100,1>-Bongo low level (%) slider31:0<-12,12,0.1>-Conga tune (st) slider32:100<25,400,1>-Conga decay (%) slider33:65<0,100,1>-Conga level (%) slider34:0<-12,12,0.1>-Clave tune (st) slider35:100<25,400,1>-Clave decay (%) slider36:55<0,100,1>-Clave level (%) slider37:0<-12,12,0.1>-Maracas tune (st) slider38:100<25,400,1>-Maracas decay (%) slider39:55<0,100,1>-Maracas level (%) slider40:0<-12,12,0.1>-Guiro tune (st) slider41:100<25,400,1>-Guiro decay (%) slider42:50<0,100,1>-Guiro level (%) slider43:0<-12,12,0.1>-Tambourine tune (st) slider44:100<25,400,1>-Tambourine decay (%) slider45:55<0,100,1>-Tambourine level (%) slider46:0<-12,12,0.1>-Cowbell tune (st) slider47:100<25,400,1>-Cowbell decay (%) slider48:55<0,100,1>-Cowbell level (%) slider49:0<-12,12,0.1>-Metal beat tune (st) slider50:100<25,400,1>-Metal beat decay (%) slider51:50<0,100,1>-Metal beat level (%) slider52:30<0,100,1>-Hit variation (%) slider53:0<0,20,0.1>-Timing variation (ms) slider54:0<0,16,1>-MIDI channel (0 = omni) slider55:60<0,100,1>-Stereo spread (%) slider56:0<-24,12,0.1>-Output (dB) slider57:0<0,100,1>-Band sat (%) slider58:30<0,100,1>-Bias (%) slider59:120<40,400,1>-Sat low band (Hz) slider60:6000<2000,16000,10>-Sat high band (Hz) slider61:-0.3<-24,0,0.1>-Ceiling / limit threshold (dB) slider62:1<0,2,1{Off,Clip,Limit}>-Output stage slider63:25<0,100,1>-Drift (%) slider64:20<20,500,1>-Mono below (Hz, 20 = off) slider65:0<0,100,1>-Stereo width (%) slider66:0<0,1,1{Off,On}>-Phaser slider67:0.3<0.05,8,0.01>-Phaser rate (Hz) slider68:70<0,100,1>-Phaser depth (%) slider69:50<0,95,1>-Phaser feedback (%) slider70:100<0,100,1>-Phaser mix (%) slider71:90<0,180,1>-Phaser stereo (deg) in_pin:none out_pin:left output out_pin:right output @init #ui_man = "gfx-g-clockwork"; // the ? pop-up links to this manual NVO = 15; // ---------- memory map ---------- vt = 0; vd = 20; vl = 40; vp = 60; // per-voice tune ratio, decay mult, level, pan lt = 80; // per-voice trigger light pend = 100; pvel = 120; // per-voice pending (delayed) hit nmap = 200; // MIDI note -> voice (128) evq = 400; // MIDI event queue (4 x 512) pn = 2500; psb = 2550; // pattern step count, step length (beats) PAT = 3000; // patterns: ((p * 3 + layer) * 15 + v) * 16 + step memset(0, 0, 3000); memset(PAT, 0, 32 * 3 * 15 * 16); i = 0; loop(128, nmap[i] = -1; i += 1; ); nmap[35] = 0; nmap[36] = 0; nmap[38] = 1; nmap[40] = 1; nmap[37] = 2; nmap[42] = 3; nmap[44] = 3; nmap[46] = 4; nmap[49] = 5; nmap[51] = 5; nmap[52] = 5; nmap[55] = 5; nmap[57] = 5; nmap[59] = 5; nmap[60] = 6; nmap[61] = 7; nmap[62] = 8; nmap[63] = 8; nmap[64] = 8; nmap[75] = 9; nmap[70] = 10; nmap[73] = 11; nmap[74] = 11; nmap[54] = 12; nmap[56] = 13; nmap[80] = 14; nmap[81] = 14; vp[0] = 0; vp[1] = 0; vp[2] = -0.2; vp[3] = 0.3; vp[4] = 0.3; vp[5] = -0.3; vp[6] = 0.45; vp[7] = -0.45; vp[8] = -0.3; vp[9] = 0.25; vp[10] = 0.5; vp[11] = -0.5; vp[12] = 0.4; vp[13] = -0.2; vp[14] = 0.2; // ---------- rhythm patterns ---------- function pmeta(p, n, sb, name) ( pn[p] = n; psb[p] = sb; strcpy(100 + p, name); ); function pdef(p, layer, v, str) local(i, c) ( i = 0; loop(strlen(str), c = str_getchar(str, i); PAT[((p * 3 + layer) * 15 + v) * 16 + i] = c == 'X' ? 2 : c == 'x' ? 1 : 0; i += 1; ); ); pmeta(0, 16, 0.25, "Rock 1"); pdef(0, 0, 0, "X.......X.x....."); pdef(0, 0, 1, "....X.......X..."); pdef(0, 0, 3, "x.x.x.x.x.x.x.x."); pdef(0, 1, 4, "..............x."); pdef(0, 1, 5, "X..............."); pdef(0, 2, 0, "X.......x......."); pdef(0, 2, 1, "........x.xx.xXX"); pmeta(1, 16, 0.25, "Rock 2"); pdef(1, 0, 0, "X.....x.X.x....."); pdef(1, 0, 1, "....X.......X..x"); pdef(1, 0, 3, "XxxxXxxxXxxxXxxx"); pdef(1, 1, 12, "....x.......x..."); pdef(1, 1, 4, "......x.......x."); pdef(1, 2, 0, "X.......x......."); pdef(1, 2, 1, "........x.xx.xXX"); pmeta(2, 16, 0.25, "Disco"); pdef(2, 0, 0, "X...X...X...X..."); pdef(2, 0, 1, "....X.......X..."); pdef(2, 0, 3, "x...x...x...x..."); pdef(2, 0, 4, "..x...x...x...x."); pdef(2, 1, 12, "xxxxxxxxxxxxxxxx"); pdef(2, 1, 14, "..x...x...x...x."); pdef(2, 2, 0, "X.......x......."); pdef(2, 2, 1, "........x.xx.xXX"); pmeta(3, 16, 0.25, "Funk"); pdef(3, 0, 0, "X..x..x...X..x.."); pdef(3, 0, 1, "....X..x.x..X..."); pdef(3, 0, 3, "xxXxxxXxxxXxxxXx"); pdef(3, 1, 4, "..............x."); pdef(3, 1, 13, "X.....x.....x..."); pdef(3, 2, 0, "X.......x......."); pdef(3, 2, 1, "........x.xx.xXX"); pmeta(4, 16, 0.25, "Ballad"); pdef(4, 0, 0, "X.......X.x....."); pdef(4, 0, 2, "....X.......X..."); pdef(4, 0, 3, "x.x.x.x.x.x.x.x."); pdef(4, 1, 10, "xxxxxxxxxxxxxxxx"); pdef(4, 2, 0, "X.......x......."); pdef(4, 2, 1, "........x.xx.xXX"); pmeta(5, 16, 0.25, "Pop"); pdef(5, 0, 0, "X..x....X.x....."); pdef(5, 0, 1, "....X.......X..."); pdef(5, 0, 3, "XxXxXxXxXxXxXxXx"); pdef(5, 1, 12, "....x.......x..."); pdef(5, 1, 5, "X..............."); pdef(5, 2, 0, "X.......x......."); pdef(5, 2, 1, "........x.xx.xXX"); pmeta(6, 12, 0.333333333333, "Shuffle"); pdef(6, 0, 0, "X.....X..x.."); pdef(6, 0, 1, "...X.....X.."); pdef(6, 0, 3, "x.xx.xx.xx.x"); pdef(6, 1, 4, "..........x."); pdef(6, 1, 5, "X..........."); pdef(6, 2, 0, "X.....x....."); pdef(6, 2, 1, "......x.xxXX"); pmeta(7, 12, 0.333333333333, "Slow Rock"); pdef(7, 0, 0, "X.....X.x..."); pdef(7, 0, 1, "...X.....X.."); pdef(7, 0, 3, "XxxXxxXxxXxx"); pdef(7, 1, 5, "X..........."); pdef(7, 1, 12, "...x.....x.."); pdef(7, 2, 0, "X.....x....."); pdef(7, 2, 1, "......x.xxXX"); pmeta(8, 12, 0.333333333333, "Swing"); pdef(8, 0, 5, "X..x.xX..x.x"); pdef(8, 0, 3, "...X.....X.."); pdef(8, 0, 0, "x.....x....."); pdef(8, 1, 2, "..x.....x..."); pdef(8, 2, 0, "X.....x....."); pdef(8, 2, 1, "......x.xxXX"); pmeta(9, 12, 0.25, "Waltz"); pdef(9, 0, 0, "X..........."); pdef(9, 0, 2, "....x...x..."); pdef(9, 0, 3, "x.x.x.x.x.x."); pdef(9, 1, 5, "X..........."); pdef(9, 1, 10, "xxxxxxxxxxxx"); pdef(9, 2, 0, "X.....x....."); pdef(9, 2, 1, "......x.xxXX"); pmeta(10, 16, 0.25, "Bossa Nova"); pdef(10, 0, 0, "X..xX..xX..xX..x"); pdef(10, 0, 2, "x..x..x...x..x.."); pdef(10, 0, 10, "XxxxXxxxXxxxXxxx"); pdef(10, 1, 3, "..x...x...x...x."); pdef(10, 1, 8, "......x.......x."); pdef(10, 2, 0, "X.......x......."); pdef(10, 2, 1, "........x.xx.xXX"); pmeta(11, 16, 0.25, "Samba"); pdef(11, 0, 0, "X..xX..xX..xX..x"); pdef(11, 0, 12, "xxXxxxXxxxXxxxXx"); pdef(11, 0, 6, "..x...x.x.x...x."); pdef(11, 1, 8, "x..x..x.x..x..x."); pdef(11, 1, 13, "X.x..x.xX.x..x.x"); pdef(11, 2, 0, "X.......x......."); pdef(11, 2, 1, "........x.xx.xXX"); pmeta(12, 16, 0.25, "Rhumba"); pdef(12, 0, 9, "x..x..x...x.x..."); pdef(12, 0, 0, "X......xX......x"); pdef(12, 0, 7, "....x.......x..."); pdef(12, 0, 8, "......x.x.....x."); pdef(12, 0, 10, "x.x.x.x.x.x.x.x."); pdef(12, 1, 6, "..x.x.....x.x..."); pdef(12, 1, 11, "X.......X......."); pdef(12, 2, 0, "X.......x......."); pdef(12, 2, 1, "........x.xx.xXX"); pmeta(13, 16, 0.25, "Cha-Cha"); pdef(13, 0, 0, "X.......X......."); pdef(13, 0, 13, "X...X...X...X..."); pdef(13, 0, 11, "X...x.x.X...x.x."); pdef(13, 0, 8, "......x.x.x.x..."); pdef(13, 1, 9, "x..x..x...x.x..."); pdef(13, 1, 6, "..x...x...x...x."); pdef(13, 2, 0, "X.......x......."); pdef(13, 2, 1, "........x.xx.xXX"); pmeta(14, 16, 0.25, "Tango"); pdef(14, 0, 0, "X...X...X...X..."); pdef(14, 0, 2, "...x.......x...."); pdef(14, 0, 3, "x.x.x.x.x.x.x.x."); pdef(14, 1, 8, "......x.....x.x."); pdef(14, 1, 12, "....x.......x..."); pdef(14, 2, 0, "X.......x......."); pdef(14, 2, 1, "........x.xx.xXX"); pmeta(15, 16, 0.25, "Beguine"); pdef(15, 0, 0, "X.......X.x....."); pdef(15, 0, 7, "x.....x.x.....x."); pdef(15, 0, 6, "....x.......x..."); pdef(15, 0, 10, "x.x.x.x.x.x.x.x."); pdef(15, 1, 5, "X..............."); pdef(15, 1, 9, "...x......x....."); pdef(15, 2, 0, "X.......x......."); pdef(15, 2, 1, "........x.xx.xXX"); // ---------- percussion-only rhythms (16-31): no kick, snare, hats or cymbal - hand percussion to play along with ---------- pmeta(16, 16, 0.25, "Tumbao"); pdef(16, 0, 8, "x.x.X..xx.x.x.XX"); pdef(16, 0, 9, "..X.X...X..X..X."); pdef(16, 0, 10, "XxxxXxxxXxxxXxxx"); pdef(16, 1, 6, "x.x...x.x.x...x."); pdef(16, 1, 7, "....X.......X..."); pdef(16, 2, 10, "........XxxxXxxx"); pdef(16, 2, 8, "........x.x.xXX."); pdef(16, 2, 6, "........xx.xx..X"); pmeta(17, 16, 0.25, "Salsa"); pdef(17, 0, 9, "X..X..X...X.X..."); pdef(17, 0, 13, "X.x.X.xxX.x.X.xx"); pdef(17, 0, 8, "..x.X..x..x.X.XX"); pdef(17, 0, 11, "X.xxX.xxX.xxX.xx"); pdef(17, 1, 6, "x.xxx.xxx.xxx.xx"); pdef(17, 1, 7, "....X.......X..."); pdef(17, 2, 9, "X..X..X........."); pdef(17, 2, 8, "........x.xxX.XX"); pdef(17, 2, 6, "........X..x.x.."); pmeta(18, 16, 0.25, "Bossa Perc"); pdef(18, 0, 2, "x..x..x...x..x.."); pdef(18, 0, 10, "XxxxXxxxXxxxXxxx"); pdef(18, 0, 8, "X..x....X..x...."); pdef(18, 1, 7, "......x.......x."); pdef(18, 1, 6, "..x...x...x...x."); pdef(18, 2, 10, "........XxxxXxxx"); pdef(18, 2, 8, "........X..xx.xX"); pdef(18, 2, 2, "..........x..x.."); pmeta(19, 16, 0.25, "Batucada"); pdef(19, 0, 12, "XxxXXxxXXxxXXxxX"); pdef(19, 0, 13, "X...x.X...x.x..."); pdef(19, 0, 14, "..x.x...x...x.x."); pdef(19, 0, 8, "....X.......X..."); pdef(19, 0, 6, "X.xx.x.xX.xx.x.x"); pdef(19, 1, 10, "xxxxxxxxxxxxxxxx"); pdef(19, 1, 7, "X..xX..xX..xX..x"); pdef(19, 2, 12, "........XxxXXxxX"); pdef(19, 2, 6, "........xxxxXxXX"); pdef(19, 2, 8, "............X.XX"); pmeta(20, 16, 0.25, "Rumba Perc"); pdef(20, 0, 9, "X..X...X..X.X..."); pdef(20, 0, 10, "x.x.x.x.x.x.x.x."); pdef(20, 0, 8, "..x.X.....x.X.xx"); pdef(20, 0, 7, "......x.......x."); pdef(20, 1, 11, "X.x.X.x.X.x.X.x."); pdef(20, 1, 6, "x.xx..x.x.xx..x."); pdef(20, 2, 10, "........x.x.x.x."); pdef(20, 2, 8, "........X.x.XxXX"); pdef(20, 2, 7, "..........x...x."); pmeta(21, 16, 0.25, "Mambo"); pdef(21, 0, 13, "X.x.X.xxX.x.xX.x"); pdef(21, 0, 9, "..X.X...X..X..X."); pdef(21, 0, 8, "..x.X..x..x.X.XX"); pdef(21, 0, 11, "X.xxX.xxX.xxX.xx"); pdef(21, 1, 14, "X...X...X...X..."); pdef(21, 1, 6, "x.x.x.x.x.x.x.x."); pdef(21, 2, 13, "........X.X.X.X."); pdef(21, 2, 8, "........xxXxxXX."); pdef(21, 2, 6, "........X.xxX..X"); pmeta(22, 12, 0.333333333333, "Afro 6/8"); pdef(22, 0, 13, "X.x.xX.x.x.x"); pdef(22, 0, 10, "XxxXxxXxxXxx"); pdef(22, 0, 8, "..x..X..x.XX"); pdef(22, 0, 7, "X.....x....."); pdef(22, 1, 6, "..x..x..x..x"); pdef(22, 1, 12, "...X.....X.."); pdef(22, 2, 13, "X.x.xX......"); pdef(22, 2, 10, "......XxxXxx"); pdef(22, 2, 8, "......x.xXXX"); pdef(22, 2, 6, "........x.x."); pmeta(23, 16, 0.25, "Calypso"); pdef(23, 0, 12, "x.X.x.X.x.X.x.X."); pdef(23, 0, 13, "X..X..X.X..X..X."); pdef(23, 0, 8, "...x..x....x..x."); pdef(23, 0, 7, "X.......X......."); pdef(23, 1, 6, "..x.x..x..x.x..x"); pdef(23, 1, 10, "XxxxXxxxXxxxXxxx"); pdef(23, 2, 12, "........x.X.x.X."); pdef(23, 2, 6, "........x.xx.xx."); pdef(23, 2, 7, "........X.....XX"); pmeta(24, 16, 0.25, "Reggae Perc"); pdef(24, 0, 2, "........X......."); pdef(24, 0, 10, "x.x.x.x.x.x.x.x."); pdef(24, 0, 7, "..........x..x.."); pdef(24, 0, 6, "..x...x...x...x."); pdef(24, 1, 12, "....X.......X..."); pdef(24, 1, 9, "...x......x....."); pdef(24, 2, 10, "........x.x.x.x."); pdef(24, 2, 2, "........X......."); pdef(24, 2, 7, "...........xx.XX"); pmeta(25, 16, 0.25, "Merengue"); pdef(25, 0, 11, "X.xxX.xxX.xxX.xx"); pdef(25, 0, 8, "X..x..x.X..x.xxX"); pdef(25, 0, 13, "X...X...X...X..."); pdef(25, 1, 6, "..x...x...x...x."); pdef(25, 1, 10, "xxxxxxxxxxxxxxxx"); pdef(25, 2, 11, "........X.xxX.xx"); pdef(25, 2, 8, "........XxxXxxXX"); pdef(25, 2, 7, "...........x..x."); pmeta(26, 16, 0.25, "Baiao"); pdef(26, 0, 14, "x.xXx.xXx.xXx.xX"); pdef(26, 0, 8, "X..x....X..x...."); pdef(26, 0, 7, "....x.......x..."); pdef(26, 1, 10, "xxxxxxxxxxxxxxxx"); pdef(26, 1, 6, "......x.......x."); pdef(26, 2, 14, "........x.xXx.xX"); pdef(26, 2, 8, "........X..x.xXX"); pdef(26, 2, 6, "..........x.x..."); pmeta(27, 16, 0.25, "Shaker Pop"); pdef(27, 0, 10, "xxXxxxXxxxXxxxXx"); pdef(27, 0, 12, "....X.......X..."); pdef(27, 0, 8, "X......xX.x....x"); pdef(27, 1, 6, "..x...x...x.x.x."); pdef(27, 1, 9, "......x.......x."); pdef(27, 2, 10, "........xxXxxxXx"); pdef(27, 2, 12, "............X..."); pdef(27, 2, 8, "........X.x.xxXX"); pmeta(28, 16, 0.25, "Tamb Soul"); pdef(28, 0, 12, "x.x.X.x.x.x.X.x."); pdef(28, 0, 9, "......x.......x."); pdef(28, 0, 8, "X......xX.x....."); pdef(28, 1, 10, "xxxxxxxxxxxxxxxx"); pdef(28, 1, 7, "...x.......x...."); pdef(28, 2, 12, "........xxX.xxXX"); pdef(28, 2, 8, "........X.x...xx"); pmeta(29, 16, 0.25, "Martillo"); pdef(29, 0, 6, "X.xxx.xxX.xxx.xx"); pdef(29, 0, 7, "......X.......X."); pdef(29, 0, 13, "X.x.X.x.X.x.X.x."); pdef(29, 1, 8, "..x.X..x..x.X.XX"); pdef(29, 1, 9, "X..X..X...X.X..."); pdef(29, 2, 6, "........XxxxXxXX"); pdef(29, 2, 7, "..........x..x.."); pdef(29, 2, 13, "........X...X..."); pmeta(30, 16, 0.25, "Guaguanco"); pdef(30, 0, 9, "X..X...X..X.X..."); pdef(30, 0, 8, "..xx.X.x..x.X..x"); pdef(30, 0, 2, "X.xxX.x.X.xxX.x."); pdef(30, 1, 6, ".....x.x...x..x."); pdef(30, 1, 10, "x.x.x.x.x.x.x.x."); pdef(30, 2, 2, "........X.xxX.x."); pdef(30, 2, 8, "........x.xXx.XX"); pdef(30, 2, 6, ".........x.x.xx."); pmeta(31, 16, 0.25, "Cumbia"); pdef(31, 0, 11, "X.xxX.xxX.xxX.xx"); pdef(31, 0, 10, "x.x.x.x.x.x.x.x."); pdef(31, 0, 8, "..X...X...X...X."); pdef(31, 0, 7, "X...x.xxX...x.xx"); pdef(31, 1, 6, "...x.......x...."); pdef(31, 1, 12, "....x.......x..."); pdef(31, 2, 11, "........X.xxX.xx"); pdef(31, 2, 8, "..........X...X."); pdef(31, 2, 7, "........X.xxXxXX"); // ---------- building blocks ---------- function tcoef(t) ( exp(-1 / (max(t, 0.0003) * srate)) ); function f_set(fc, q) instance(g, k, a1, a2, a3) ( g = tan($pi * min(fc, 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; ); // decaying sine with a pitch drop (bridged-T style "ping") function tone_trig(vel, f, drop, dt) instance(ph, env, pe, f0, ed, pd, dr) ( ph = 0; env = vel; pe = 1; f0 = f; dr = drop; ed = tcoef(dt); pd = tcoef(0.012); ); function tone_run() instance(ph, env, pe, f0, ed, pd, dr) local(y) ( env > 0.00005 ? ( ph += f0 * (1 + dr * pe) / srate; ph >= 1 ? ph -= 1; pe *= pd; y = sin(2 * $pi * ph) * env; env *= ed; y; ) : 0; ); // simple envelope function env_trig(vel, dt) instance(env, ed) ( env = vel; ed = tcoef(dt); ); function env_run() instance(env, ed) local(y) ( y = env; env *= ed; y ); function sq(ph) ( ph - floor(ph) < 0.5 ? 1 : -1 ); // tambourine: six jingle resonances (pairs a few Hz apart, so they shimmer), struck by a quick cluster of // collisions; TAM holds each resonator's coefficients and state (7 fields x 6) TAM = 30000; memset(TAM, 0, 64); function tam_trig(l, t, d) local(k, f, a, tau, r) ( cwm_k = 0; loop(6, f = (k == 0 ? 5150 : k == 1 ? 5235 : k == 2 ? 6880 : k == 3 ? 7015 : k == 4 ? 8640 : 11250) * t; a = k == 0 ? 1 : k == 1 ? 0.8 : k == 2 ? 0.9 : k == 3 ? 0.65 : k == 4 ? 0.7 : 0.4; tau = (0.16 - 0.015 * k) * d; r = f < srate * 0.45 ? exp(-1 / (tau * srate)) : 0; TAM[k * 7] = 2 * r * cos(2 * $pi * min(f, srate * 0.45) / srate); TAM[k * 7 + 1] = r * r; TAM[k * 7 + 2] = a * (1 - r); TAM[k * 7 + 3] = 0; TAM[k * 7 + 4] = 0; cwm_k += 1; ); ta_l = l; ta_t = 0; ta_on = 1; ta_env = 1; ta_ec = tcoef(0.5 * d); ); // the shake: collisions at 0, 8, 19 and 33 ms, each a 3 ms grain of noise function tam_run(nz) local(g, ts, k, y, x, w) ( ts = ta_t / srate; g = ts < 0.003 ? 1 : (ts >= 0.008 && ts < 0.011) ? 0.7 : (ts >= 0.019 && ts < 0.022) ? 0.5 : (ts >= 0.033 && ts < 0.036) ? 0.3 : 0; x = nz * g * ta_l; y = 0; k = 0; loop(6, w = TAM[k * 7] * TAM[k * 7 + 3] - TAM[k * 7 + 1] * TAM[k * 7 + 4] + TAM[k * 7 + 2] * x; TAM[k * 7 + 4] = TAM[k * 7 + 3]; TAM[k * 7 + 3] = w; y += w; k += 1; ); ta_t += 1; ta_env *= ta_ec; taF.f_run(x); // a little bright sizzle on each collision ta_t > srate * 2 || (ta_t > srate * 0.05 && ta_env < 0.0005) ? ta_on = 0; (y * 61 + taF.hp * 2.4) * ta_env; ); // phaser: six first-order allpass stages swept by a sine LFO, with feedback around the chain; // the right channel's LFO runs STEREO degrees ahead function ph_coef(f) local(t) ( t = tan($pi * min(f, srate * 0.42) / srate); (t - 1) / (t + 1) ); function ph_run(x, a) instance(z1, z2, z3, z4, z5, z6, lst) local(y, u) ( u = x + ph_fb * lst; y = a * u + z1; z1 = u - a * y; u = y; y = a * u + z2; z2 = u - a * y; u = y; y = a * u + z3; z3 = u - a * y; u = y; y = a * u + z4; z4 = u - a * y; u = y; y = a * u + z5; z5 = u - a * y; u = y; y = a * u + z6; z6 = u - a * y; lst = y; x + ph_mx * (y - x) * 0.5; ); // the swept frequency for an LFO phase (0..1) function ph_freq(lp) ( 160 * 2 ^ (ph_dep * 6.2 * (0.5 + 0.5 * sin(2 * $pi * lp))) ); // ---------- trigger one voice ---------- function trig(v, vel) local(t, d, l, hv, nc, mtn) ( hv = hitvar; t = vt[v] * (1 + hv * 0.03 * (rand(2) - 1)) * ins_pitch * ins_note_detune(); d = vd[v] * (1 + hv * 0.15 * (rand(2) - 1)) * (1 + 0.2 * ins_fx); nc = 2 ^ ins_cut; mtn = 2 ^ ins_tone; // drift: noise brightness, metal tone l = vel * vl[v] * (1 + hv * 0.15 * (rand(2) - 1)); lt[v] = 1; v == 0 ? ( kT.tone_trig(l * 1.2, 56 * t, 0.7, 0.2 * d); ) : v == 1 ? ( snT1.tone_trig(l * 0.55, 180 * t, 0.1, 0.05 * d); snT2.tone_trig(l * 0.35, 330 * t, 0.05, 0.04 * d); snN.env_trig(l * 0.7, 0.12 * d); snF.f_set(4200 * t * nc, 0.6); ) : v == 2 ? ( rmT1.tone_trig(l * 0.7, 1700 * t, 0.05, 0.01 * d); rmT2.tone_trig(l * 0.5, 460 * t, 0.1, 0.008 * d); ) : v == 3 ? ( hcE.env_trig(l * 1.4, 0.035 * d); hcF.f_set(9000 * t * nc, 0.8); hoE.env = 0; ) : // closed hat chokes the open one v == 4 ? ( hoE.env_trig(l * 1.4, 0.28 * d); hoF.f_set(8500 * t * nc, 0.8); ) : v == 5 ? ( cyE.env_trig(l, 0.9 * d); cyF.f_set(7000 * t * mtn, 0.7); cy_t = t; ) : v == 6 ? ( bhT.tone_trig(l, 420 * t, 0.15, 0.07 * d); ) : v == 7 ? ( blT.tone_trig(l, 300 * t, 0.15, 0.09 * d); ) : v == 8 ? ( cgT.tone_trig(l, 200 * t, 0.1, 0.16 * d); ) : v == 9 ? ( clT.tone_trig(l * 0.8, 2500 * t, 0, 0.025 * d); ) : v == 10 ? ( maE.env_trig(l, 0.03 * d); maF.f_set(6500 * t * nc, 0.7); ma_a = 0; ) : v == 11 ? ( guE.env_trig(l, 0.006); guF.f_set(2800 * t, 3); gu_len = 0.1 * d * srate; gu_n = gu_len; gu_t = t; gu_ph = 0; ) : v == 12 ? ( tam_trig(l, t * nc, d); taF.f_set(9000 * t * nc, 0.7); ) : v == 13 ? ( cbE1.env_trig(l * 0.6, 0.015); cbE2.env_trig(l * 0.4, 0.25 * d); cbF.f_set(700 * t * mtn, 2.5); cb_t = t; ) : v == 14 ? ( mbE.env_trig(l, 0.07 * d); mbF.f_set(2500 * t * mtn, 0.7); mb_t = t; ); ); // a hit from the rhythm or MIDI, optionally loosened in time function hit(v, vel) ( timevar > 0 ? ( pend[v] = 1 + floor(rand(timevar * 0.001 * srate)); pvel[v] = vel; ) : trig(v, vel); ); function update() local(v, b) ( rhy = slider1; seq_on = slider2; varb = slider3; fill_n = slider4 == 1 ? 2 : slider4 == 2 ? 4 : slider4 == 3 ? 8 : 0; swing = slider5 / 100; acc = slider6 / 100; v = 0; loop(NVO, b = 7 + v * 3; vt[v] = 2 ^ (slider(b) / 12); vd[v] = slider(b + 1) / 100; vl[v] = sqr(slider(b + 2) / 100); v += 1; ); hitvar = slider52 / 100; timevar = slider53; chan = slider54; width = slider55 / 100; out_t = 10 ^ (slider56 / 20); drift = slider63 / 100; ins_output_setup(57); ins_stereo_setup(64); ph_on = slider66; ph_rate = slider67; ph_dep = slider68 / 100; ph_fb = slider69 / 100 * 0.8; ph_mix_t = ph_on ? slider70 / 100 : 0; ph_off = slider71 / 360; ); // ---------- presets: kits ---------- function kit(tn, dc, ktn, kdc) local(v) ( v = 0; loop(NVO, slider(7 + v * 3) = tn; slider(8 + v * 3) = dc; v += 1; ); slider7 = ktn; slider8 = kdc; ); function automate_voices() local(i) ( i = 7; loop(45, slider_automate(2 ^ (i - 1)); i += 1; ); ); function lvl(v, l) ( slider(9 + v * 3) = l; ); function lv_def() local(v) ( v = 0; loop(NVO, slider(9 + v * 3) = lvdef[v]; v += 1; ); ); // voices: 0 kick 1 snare 2 rim 3 hat 4 open 5 cym 6 bongo h 7 bongo l 8 conga 9 clave 10 maracas 11 guiro 12 tamb 13 cowbell 14 metal function load_kit(p) local(v) ( p >= 1 && p <= 10 ? lv_def(); p == 1 ? kit(0, 100, 0, 100) : // Classic p == 2 ? kit(0, 55, 0, 70) : // Tight & Dry p == 3 ? kit(-3, 140, -4, 200) : // Deep & Boomy p == 4 ? kit(3, 90, 2, 90) : // Bright p == 5 ? kit(0, 220, 0, 160) : // Long Tails p == 6 ? ( kit(1, 80, -2, 130); lvl(0, 95); lvl(3, 72); lvl(4, 66); lvl(12, 62); ) : // Disco p == 7 ? ( kit(0, 110, 0, 90); lvl(0, 70); lvl(1, 55); lvl(6, 78); lvl(7, 78); lvl(8, 80); // Latin Percussion lvl(9, 70); lvl(10, 66); lvl(11, 62); lvl(13, 66); ) : p == 8 ? ( kit(-2, 75, -3, 90); lvl(3, 50); lvl(4, 45); lvl(5, 38); ) : // Lo-Fi Box p == 9 ? ( kit(-1, 60, -5, 260); lvl(0, 100); lvl(1, 80); slider11 = 70; ) : // Electro p == 10 ? ( kit(0, 130, 0, 80); slider10 = -2; slider11 = 200; lvl(0, 70); lvl(1, 55); lvl(3, 45); // Jazz Brushes lvl(4, 40); lvl(5, 45); ); p >= 1 && p <= 10 ? automate_voices(); ); function load_preset(p) local(v) ( p == 6 ? ( // Reset all to defaults slider1 = 0; slider2 = 0; slider3 = 0; slider4 = 0; slider5 = 0; slider6 = 50; kit(0, 100, 0, 100); v = 0; loop(NVO, slider(9 + v * 3) = lvdef[v]; v += 1; ); slider52 = 30; slider53 = 0; slider54 = 0; slider55 = 60; slider56 = 0; slider57 = 0; slider58 = 30; slider59 = 120; slider60 = 6000; slider61 = -0.3; slider62 = 1; slider63 = 25; slider64 = 20; slider65 = 0; slider66 = 0; slider67 = 0.3; slider68 = 70; slider69 = 50; slider70 = 100; slider71 = 90; v = 1; loop(63, slider_automate(2 ^ (v - 1)); v += 1; ); slider_automate(slider64); slider_automate(slider65); slider_automate(slider66); slider_automate(slider67); slider_automate(slider68); slider_automate(slider69); slider_automate(slider70); slider_automate(slider71); ); ); lvdef = 2600; v = 0; loop(NVO, lvdef[v] = 0; v += 1; ); // default levels function setlv(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) ( lvdef[0] = a0; lvdef[1] = a1; lvdef[2] = a2; lvdef[3] = a3; lvdef[4] = a4; lvdef[5] = a5; lvdef[6] = a6; lvdef[7] = a7; lvdef[8] = a8; lvdef[9] = a9; lvdef[10] = a10; lvdef[11] = a11; lvdef[12] = a12; lvdef[13] = a13; lvdef[14] = a14; ); setlv(90, 75, 60, 60, 55, 50, 65, 65, 65, 55, 55, 50, 55, 55, 50); // ---------- state ---------- ost_init(40000); ostL.ost_ch_init(); ostR.ost_ch_init(); sm = 1 - exp(-1 / (0.03 * srate)); pkL = 0; pkR = 0; lastkey = -1; cur_step = -1; cur_bar = 0; cur_fill = 0; cy_ph1 = 0; cy_ph2 = 0; cy_ph3 = 0; cy_ph4 = 0; cy_ph5 = 0; cy_ph6 = 0; ins_drift_init(); ph_lp = 0; ph_cnt = 0; ph_mx = 0; ph_aL = ph_aR = ph_coef(400); !ui_dflt ? ui_thm = 6; !ui_dflt ? ui_hw = 1; // fixed charcoal faceplate, red accents !ui_dflt ? ui_kstyle = 0; // GFX standard knobs show_vo = 0; // VOICES (tune / decay / level) hidden until asked for !ui_dflt ? ui_thm = 6; !ui_dflt ? ui_hw = 1; // default skin: charcoal and red, hardware RED = 0xB3122A; WHT = 0xF2EEE8; REDL = 0xFF3A2A; AMBL = 0xFFB030; CHR = 0x2B2C31; RACC = 0xE5414C; // charcoal panel, red accents SH1 = 0x313238; SH2 = 0x2B2C31; SH3 = 0x25262A; // rhythm, voices, utility function mgroup(x, y, w, h, sec, title, shade) ( ui_hw ? ( pk_inset(x * sc, y * sc, w * sc, h * sc, shade); pk_box(x * sc, y * sc, w * sc, h * sc, RACC, title); ui_section(sec == 0 ? 2 : sec); C_INK = WHT; ) : ( // flat: a plain panel with its title on the top edge (the layout keeps its title-on-top spacing) ui_col(C_HDR); ui_rrect(x * sc, y * sc, w * sc, h * sc, 4 * sc); pk_box(x * sc, y * sc, w * sc, h * sc, C_ACC, title); ui_section(sec == 0 ? 2 : sec); C_INK = C_TITLE; ); ); // a button: a lit hardware key, or a flat key in the flat look function cbtn(x, y, w, h, on, ledc) ( ui_hw ? pk_ledbtn(x, y, w, h, on, ledc) : ui_keycap(x, y, w, h, "", on, 1); ); update(); 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 evn = 0; while (midirecv(mofs, m1, m2, m3)) ( stt = m1 & 0xF0; ins_chan_ok(m1, 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; update(); playing = (play_state & 1) && seq_on; bp0 = beat_position; bps = tempo / 60 / srate; playing == 0 ? ( lastkey = -1; cur_step = -1; ); ins_drift_block(drift, samplesblock); // GFX instrument drift aud_v > 0 ? ( trig(aud_v - 1, 1); aud_v = 0; ); // a pad clicked on screen @serialize file_var(0, ui_sz); file_var(0, ui_thm); file_var(0, ui_hw); // added with the keyboard faceplate: only read when the saved state has them file_avail(0) != 0 ? ( file_var(0, show_vo); file_var(0, show_ph); file_var(0, show_su); file_var(0, cur_kit_v); ); @sample // MIDI hits 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 && nmap[e2] >= 0 ? ( trig(nmap[e2], e3 / 127); ins_midi_hit(); ); evi += 1; ); // rhythm, locked to the project's beat position playing ? ( rn = pn[rhy]; rsb = psb[rhy]; rlb = rn * rsb; beat = bp0 + spos * bps; beat >= 0 ? ( bar = floor(beat / rlb); s_ = (beat - bar * rlb) / rsb; sb_ = floor(s_); fr_ = s_ - sb_; sw_ = rn == 16 ? swing * 0.5 : 0; // swing: the off-beat 16ths come later st_ = (sb_ & 1) && fr_ < sw_ ? sb_ - 1 : sb_; key = bar * 64 + st_; key != lastkey ? ( lastkey = key; cur_step = st_; cur_bar = bar; cur_fill = fill_n > 0 && (bar % fill_n) == fill_n - 1 && st_ >= rn / 2; av = 0; loop(NVO, cur_fill ? ( val = PAT[((rhy * 3 + 2) * 15 + av) * 16 + st_]; ) : ( val = PAT[((rhy * 3) * 15 + av) * 16 + st_]; varb ? val = max(val, PAT[((rhy * 3 + 1) * 15 + av) * 16 + st_]); ); val > 0 ? hit(av, val == 2 ? 1 : 1 - acc * 0.45); av += 1; ); ); ); ); spos += 1; // delayed (loosened) hits av = 0; loop(NVO, pend[av] > 0 ? ( pend[av] -= 1; pend[av] == 0 ? trig(av, pvel[av]); ); av += 1; ); // ---------- voices ---------- nz = rand(2) - 1; y0 = kT.tone_run(); y1 = snT1.tone_run() + snT2.tone_run() + (snN.env > 0.00005 ? ( snF.f_run(nz); snF.hp * snN.env_run() ) : 0); y2 = rmT1.tone_run() + rmT2.tone_run(); hcE.env > 0.00005 ? ( hcF.f_run(nz); y3 = (hcF.bp + 0.6 * hcF.hp) * hcE.env_run(); ) : y3 = 0; hoE.env > 0.00005 ? ( hoF.f_run(nz); y4 = (hoF.bp + 0.6 * hoF.hp) * hoE.env_run(); ) : y4 = 0; cyE.env > 0.00005 ? ( cy_ph1 += 205.3 * cy_t / srate; cy_ph2 += 304.4 * cy_t / srate; cy_ph3 += 369.6 * cy_t / srate; cy_ph4 += 522.7 * cy_t / srate; cy_ph5 += 540 * cy_t / srate; cy_ph6 += 800 * cy_t / srate; mt = (sq(cy_ph1) + sq(cy_ph2) + sq(cy_ph3) + sq(cy_ph4) + sq(cy_ph5) + sq(cy_ph6)) / 6; cyF.f_run(mt + 0.4 * nz); y5 = cyF.hp * cyE.env_run(); ) : y5 = 0; y6 = bhT.tone_run(); y7 = blT.tone_run(); y8 = cgT.tone_run(); y9 = clT.tone_run(); maE.env > 0.00005 ? ( ma_a = min(ma_a + 1 / (0.002 * srate), 1); maF.f_run(nz); y10 = maF.hp * maE.env_run() * ma_a; ) : y10 = 0; gu_len > 0 || guE.env > 0.00005 ? ( gu_len > 0 ? gu_len -= 1; gu_ph += 50 * gu_t / srate; gu_ph >= 1 ? gu_ph -= 1; // a short scrape (about 100 ms) that fades as the stick runs out, then a quick 6 ms stop guF.f_run(nz); y11 = guF.bp * (gu_ph < 0.5 ? 1 : 0.08) * (gu_len > 0 ? guE.env * (0.45 + 0.55 * gu_len / gu_n) : guE.env_run()) * 1.25; ) : y11 = 0; y12 = ta_on ? tam_run(nz) : 0; (cbE1.env + cbE2.env) > 0.00005 ? ( cb_ph1 += 562 * cb_t / srate; cb_ph2 += 845 * cb_t / srate; y13 = cbF.f_run(sq(cb_ph1) + sq(cb_ph2)) * (cbE1.env_run() + cbE2.env_run()) * 0.5; ) : y13 = 0; mbE.env > 0.00005 ? ( mb_ph1 += 1180 * mb_t / srate; mb_ph2 += 1720 * mb_t / srate; mbF.f_run(sq(mb_ph1) * sq(mb_ph2)); y14 = mbF.hp * mbE.env_run() * 0.6; ) : y14 = 0; // mix with pan (SPREAD scales the pan positions) oL = 0; oR = 0; av = 0; loop(NVO, yv = av == 0 ? y0 : av == 1 ? y1 : av == 2 ? y2 : av == 3 ? y3 : av == 4 ? y4 : av == 5 ? y5 : av == 6 ? y6 : av == 7 ? y7 : av == 8 ? y8 : av == 9 ? y9 : av == 10 ? y10 : av == 11 ? y11 : av == 12 ? y12 : av == 13 ? y13 : y14; pn_ = vp[av] * width; oL += yv * (1 - max(pn_, 0)); oR += yv * (1 + min(pn_, 0)); av += 1; ); // phaser (fades in and out with the ON key, so switching it never clicks) ph_mx += (ph_mix_t - ph_mx) * sm; ph_mx > 0.0005 ? ( ph_cnt <= 0 ? ( ph_cnt = 32; ph_lp += ph_rate * 32 / srate; ph_lp >= 1 ? ph_lp -= 1; ph_aL = ph_coef(ph_freq(ph_lp)); ph_aR = ph_coef(ph_freq(ph_lp + ph_off)); ); ph_cnt -= 1; oL = phL.ph_run(oL, ph_aL); oR = phR.ph_run(oR, ph_aR); ); out_s += (out_t - out_s) * sm; oL = ostL.ost_band(oL * 0.5) * out_s; oR = ostR.ost_band(oR * 0.5) * out_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 1180 588 // --------------------------------------------------------------------------------------------------------- // GFX drum machine panel (the G-Pulse family): header with the rhythm keys and LCD, the 15 voices in two // columns (voice tab - click to hear it -, TUNE / DECAY / LEVEL on a band in its colour, a hit light), then // RHYTHM / KITS / MASTER and PHASER / DRIFT. // --------------------------------------------------------------------------------------------------------- fh = 582; ui_thm = 5; ui_hw = 0; ui_begin(1180, fh + 6); // the panel's own label fonts (bigger than the house small print): 10 = labels, 11 = headers, 12 = scales gfx_setfont(10, "Arial", 11.5 * sc, 'b'); gfx_setfont(11, "Arial", 12 * sc, 'b'); gfx_setfont(12, "Arial", 10 * sc, 'b'); // champagne keyboard-case skin BODY = 0xCDBFA6; BODYL = 0xDCD1BC; BODYD = 0xB3A48A; INKB = 0x3A2C20; BRN = 0x5B3A22; CRM = 0xF1E6D0; C_BG = BODY; C_ACC = BRN; C_S0 = 0x8A5A34; C_S2 = 0x8A5A34; C_KC = 0x2A2018; C_KNOB = 0x6B4A30; C_KEDGE = 0x4A3220; C_DIM = BODYD; C_LABEL = INKB; C_VALUE = INKB; C_INK = INKB; C_PTR = CRM; C_KA = 0x8A5A34; C_KAH = 0xB0703C; C_BTN = 0xC9C6C0; C_BTNT = INKB; C_HDR = BODYL; C_TITLE = INKB; dr_tabc = 0xDDD3BF; dr_tabs = 0xEFE7D6; dr_ink = INKB; dr_c0 = 0xC2B49A; dr_c1 = 0xB8A98F; dr_hl = 0x000000; WHT = INKB; RACC = BRN; SH1 = BODYD; SH3 = BODYL; ui_col(0x8E8C88); gfx_rect(0, 0, gfx_w, gfx_h); ui_col(BODY); ui_rrect(8 * sc, 8 * sc, 1164 * sc, fh * sc, 8 * sc); ui_col(BODYL); gfx_rect(14 * sc, 8 * sc, 1152 * sc, 2 * sc); ui_col(INKB); gfx_circle(40 * sc, 38 * sc, 14 * sc, 1, 1); gfx_setfont(4); ui_col(CRM); ui_text(40 * sc, 33 * sc, "GFX"); gfx_setfont(6, "Arial", 24 * sc, 'b'); ui_col(INKB); gfx_x = 62 * sc; gfx_y = 24 * sc; gfx_drawstr("CLOCKWORK"); gfx_setfont(10); ui_cola(INKB, 0.75); gfx_x = 232 * sc; gfx_y = 33 * sc; gfx_drawstr("ANALOGUE RHYTHM BOX"); ui_col(BRN); gfx_rect(14 * sc, 70 * sc, 1152 * sc, 17 * sc); gfx_setfont(10); ui_col(CRM); gfx_x = 30 * sc; gfx_y = 72 * sc; gfx_drawstr("rhythm - 32 preset rhythms - 15 analogue voices - phaser - drift"); // keyboard-style keys in place of the LED keys function ptk(x, y, w, h, on, ledc) local(inb, hit) ( inb = mouse_x >= x && mouse_x < x + w && mouse_y >= y && mouse_y < y + h; hit = pressed && drag_id == 0 && inb; hit ? drag_id = -1; ui_cola(0x000000, 0.35); gfx_rect(x + 1 * sc, y + 2 * sc, w, h); ui_col(on ? 0xE5A637 : 0xCFCBC4); gfx_rect(x, y + on * 1.5 * sc, w, h); ui_cola(0xFFFFFF, on ? 0.15 : 0.4); gfx_rect(x, y + on * 1.5 * sc, w, 2 * sc); ui_cola(0x000000, 0.2); gfx_rect(x, y + h - 4 * sc + on * 1.5 * sc, w, 4 * sc); hit; ); // a key with its label on the cap (unscaled x, y, w, h) function cw_key(x, y, w, h, on, label) local(hit, tw, th) ( hit = ptk(x * sc, y * sc, w * sc, h * sc, on, REDL); gfx_setfont(10); ui_col(INKB); gfx_measurestr(label, tw, th); gfx_x = (x + w * 0.5) * sc - tw * 0.5; gfx_y = (y + (h - 4) * 0.5 + on * 1.5) * sc - th * 0.5; gfx_drawstr(label); hit; ); // the rhythm list, in two groups: full-kit rhythms and percussion-only rhythms function rhythm_menu(x, y) local(i, sel) ( #rm = ">KIT RHYTHMS|"; i = 0; loop(32, i == 16 ? strcat(#rm, ">PERCUSSION ONLY|"); i == 15 || i == 31 ? strcat(#rm, "<"); slider1 == i ? strcat(#rm, "!"); strcat(#rm, 100 + i); i < 31 ? strcat(#rm, "|"); i += 1; ); gfx_x = x; gfx_y = y; sel = gfx_showmenu(#rm); sel > 0 ? ( slider1 = sel - 1; slider_automate(slider1); ); ); // header, the house preset layout: [RHYTHMS][◄][►][LCD] (RHYTHMS or a click on the LCD opens the list), MIDI, size cw_key(392, 18, 92, 40, 0, "RHYTHMS") ? rhythm_menu(392 * sc, 60 * sc); cw_key(490, 18, 36, 40, 0, "◄") ? ( slider1 = (slider1 + 31) % 32; slider_automate(1); ); cw_key(530, 18, 36, 40, 0, "►") ? ( slider1 = (slider1 + 1) % 32; slider_automate(1); ); ui_col(0x1E1D1C); ui_rrect(574 * sc, 12 * sc, 372 * sc, 54 * sc, 4 * sc); ui_col(0xB8BC9C); gfx_rect(580 * sc, 17 * sc, 360 * sc, 44 * sc); pk_lcdc = 0x262A1E; rh = slider1; rn_d = pn[rh]; pk_lcdfont(7, 16 * sc); ui_col(pk_lcdc); sprintf(#rt, "%02d %s", rh + 1, 100 + rh); gfx_x = 590 * sc; gfx_y = 19 * sc; gfx_drawstr(#rt); pk_lcdfont(8, 12 * sc); sprintf(#rt, "%s %s %s", rh >= 16 ? "PERCUSSION" : "KIT RHYTHM", slider3 ? "VAR B" : "VAR A", psb[rh] > 0.3 ? "TRIPLET" : rn_d == 12 ? "3/4" : "4/4"); gfx_x = 590 * sc; gfx_y = 42 * sc; gfx_drawstr(#rt); slider2 ? ( (play_state & 1) ? sprintf(#rt, "BAR %d%s", cur_bar + 1, cur_fill ? " FILL" : "") : strcpy(#rt, "WAIT") ) : strcpy(#rt, "MIDI ONLY"); gfx_measurestr(#rt, gw_, gh_); gfx_x = 932 * sc - gw_; gfx_y = 42 * sc; gfx_drawstr(#rt); pressed && drag_id == 0 && mouse_x >= 578 * sc && mouse_x < 942 * sc && mouse_y >= 14 * sc && mouse_y < 62 * sc ? ( drag_id = -1; rhythm_menu(578 * sc, 64 * sc); ); pk_led(980 * sc, 38 * sc, 4 * sc, ins_midi_lit(), 0x3ADF6A); gfx_setfont(10); ui_col(WHT); gfx_x = 990 * sc; gfx_y = 32 * sc; gfx_drawstr("MIDI"); ui_size_picker(1092 * sc, 28 * sc); // ---------- the voices: two columns, each voice a tab (click to hear it), TUNE / DECAY / LEVEL and a hit light ---------- ui_col(BODYL); ui_rrect(24 * sc, 93 * sc, 1132 * sc, 257 * sc, 6 * sc); gi = 0; loop(2, gx0 = gi ? 596 : 32; gfx_setfont(11); ui_cola(WHT, 0.75); gfx_x = (gx0 + 12) * sc; gfx_y = 98 * sc; gfx_drawstr("VOICE"); gfx_measurestr("NOTE", gw_, gh_); gfx_x = (gx0 + 162) * sc - gw_; gfx_drawstr("NOTE"); ui_text((gx0 + 240) * sc, 98 * sc, "TUNE"); ui_text((gx0 + 330) * sc, 98 * sc, "DECAY"); ui_text((gx0 + 420) * sc, 98 * sc, "LEVEL"); ui_text((gx0 + 500) * sc, 98 * sc, "HIT"); gi += 1; ); gfx_setfont(4, "Arial", 11.5 * sc, 'b'); // the note numbers on the voice tabs, bigger than the house small print v = 0; loop(NVO, gx0 = v < 8 ? 32 : 596; gy = 116 + (v % 8) * 28; gc = dr_vcol(v); dr_tab(gx0, gy, 170, 24, gc, v == 0 ? "KICK" : v == 1 ? "SNARE" : v == 2 ? "RIMSHOT" : v == 3 ? "CLOSED HAT" : v == 4 ? "OPEN HAT" : v == 5 ? "CYMBAL" : v == 6 ? "BONGO HIGH" : v == 7 ? "BONGO LOW" : v == 8 ? "CONGA" : v == 9 ? "CLAVE" : v == 10 ? "MARACAS" : v == 11 ? "GUIRO" : v == 12 ? "TAMBOURINE" : v == 13 ? "COWBELL" : "METAL BEAT", v == 0 ? "36" : v == 1 ? "38" : v == 2 ? "37" : v == 3 ? "42" : v == 4 ? "46" : v == 5 ? "49" : v == 6 ? "60" : v == 7 ? "61" : v == 8 ? "62" : v == 9 ? "75" : v == 10 ? "70" : v == 11 ? "73" : v == 12 ? "54" : v == 13 ? "56" : "80", lt[v], 0) ? aud_v = v + 1; lt_d = lt[v]; lt[v] *= 0.82; dr_band(gx0 + 178, gy, 370, 24, gc, mouse_x >= (gx0 + 178) * sc && mouse_x < (gx0 + 548) * sc && mouse_y >= gy * sc && mouse_y < (gy + 24) * sc); ui_kmini = 1; ui_section(v % 2 ? 1 : 3); C_INK = WHT; o_sc = sc; sc *= 0.85; ui_knob((gx0 + 240) * o_sc, (gy + 12) * o_sc, 7 + v * 3, -12, 12, 0, 0, "", "%+.1f st"); ui_knob((gx0 + 330) * o_sc, (gy + 12) * o_sc, 8 + v * 3, 25, 400, 100, 1, "", "%.0f%%"); ui_knob((gx0 + 420) * o_sc, (gy + 12) * o_sc, 9 + v * 3, 0, 100, lvdef[v], 0, "", "%.0f%%"); sc = o_sc; ui_kmini = 0; pk_led((gx0 + 500) * sc, (gy + 12) * sc, 4 * sc, lt_d > 0.15, gc); v += 1; ); gfx_setfont(4, "Trebuchet MS", ui_fs4, 'b'); // back to the house small font // the free slot under METAL BEAT carries the hint ui_cola(dr_tabc, 0.6); gfx_rect(596 * sc, (116 + 7 * 28) * sc, 548 * sc, 24 * sc); gfx_setfont(10); ui_cola(WHT, 0.62); gfx_measurestr("x", gw_, gh_); ui_text(870 * sc, (116 + 7 * 28 + 12) * sc - gh_ * 0.5, "click a voice's name to hear it - double-click a knob to reset it"); function sgroup(x, y, w, h, title) ( ui_col(BODYL); ui_rrect(x * sc, y * sc, w * sc, h * sc, 5 * sc); ui_col(BRN); gfx_rect((x + 10) * sc, (y + 6) * sc, (w - 20) * sc, 15 * sc); gfx_setfont(10); ui_col(CRM); gfx_x = (x + 16) * sc; gfx_y = (y + 7) * sc; gfx_drawstr(title); C_INK = INKB; ); // ---------- RHYTHM ---------- ui_flatk = 0.62; // the knobs below RHYTHM, MASTER, PHASER and DRIFT are drawn a size smaller sgroup(24, 358, 402, 108, "RHYTHM"); ptk(38 * sc, 388 * sc, 44 * sc, 34 * sc, slider2 == 1, REDL) ? ( slider2 = 1 - slider2; slider_automate(2); ); ptk(92 * sc, 388 * sc, 44 * sc, 34 * sc, slider3 == 1, AMBL) ? ( slider3 = 1 - slider3; slider_automate(4); ); ptk(146 * sc, 388 * sc, 44 * sc, 34 * sc, slider1 >= 16, AMBL) ? ( slider1 = slider1 >= 16 ? slider1 - 16 : slider1 + 16; slider_automate(1); ); gfx_setfont(10); ui_col(WHT); ui_text(60 * sc, 428 * sc, "PLAY"); ui_text(114 * sc, 428 * sc, "VAR B"); ui_text(168 * sc, 428 * sc, "PERC"); ui_cola(WHT, 0.62); ui_text(114 * sc, 446 * sc, "plays with the transport"); knob_step = 1; ui_knob(246 * sc, 428 * sc, 4, 0, 3, 0, 0, "FILL", slider4 == 0 ? "OFF" : slider4 == 1 ? "EVERY 2" : slider4 == 2 ? "EVERY 4" : "EVERY 8"); ui_knob(310 * sc, 428 * sc, 6, 0, 100, 50, 0, "ACCENT", "%.0f%%"); ui_knob(374 * sc, 428 * sc, 5, 0, 100, 0, 0, "SWING", "%.0f%%"); // ---------- KITS: ten kit keys, the name on each cap, RESET ALL beside them ---------- sgroup(434, 358, 420, 108, "KITS"); gi = 0; loop(10, gkx = 446 + (gi % 5) * 68; gky = gi < 5 ? 388 : 424; cw_key(gkx, gky, 64, 30, cur_kit_v == gi, gi == 0 ? "CLASSIC" : gi == 1 ? "TIGHT" : gi == 2 ? "DEEP" : gi == 3 ? "BRIGHT" : gi == 4 ? "LONG" : gi == 5 ? "DISCO" : gi == 6 ? "LATIN" : gi == 7 ? "LO-FI" : gi == 8 ? "ELECTRO" : "BRUSHES") ? ( load_kit(gi + 1); cur_kit_v = gi; ); gi += 1; ); cw_key(790, 388, 54, 66, 0, "") ? ( load_preset(6); cur_kit_v = 0; ); gfx_setfont(10); ui_col(INKB); ui_text(817 * sc, 405 * sc, "RESET"); ui_text(817 * sc, 421 * sc, "ALL"); // ---------- MASTER: SPREAD, OUTPUT, CHANNEL and the output meter ---------- sgroup(862, 358, 294, 108, "MASTER"); ui_section(2); C_INK = WHT; ui_knob(898 * sc, 428 * sc, 55, 0, 100, 60, 0, "SPREAD", "%.0f%%"); ui_knob(958 * sc, 428 * sc, 56, -24, 12, 0, 0, "OUTPUT", "%.1f dB"); ui_section(3); C_INK = WHT; knob_step = 1; ui_knob(1020 * sc, 428 * sc, 54, 0, 16, 0, 0, "CHANNEL", slider54 == 0 ? "OMNI" : "CH %.0f"); ui_section(2); C_INK = WHT; // L / R peak meter (pkL / pkR from the audio), -48 .. 0 dB in 16 segments, with a peak hold that falls back after a second ui_col(0x2A231C); ui_rrect(1058 * sc, 384 * sc, 88 * sc, 76 * sc, 3 * sc); cw_now = time_precise(); gi = 0; loop(2, gm = gi ? pkR : pkL; gm = min(max((20 * log10(max(gm, 0.00001)) + 48) / 48, 0), 1); gi ? ( gm >= cwm_hr || cw_now - cwm_tr > 1.2 ? ( cwm_hr = gm; cwm_tr = cw_now; ); cwm_h = cwm_hr; ) : ( gm >= cwm_hl || cw_now - cwm_tl > 1.2 ? ( cwm_hl = gm; cwm_tl = cw_now; ); cwm_h = cwm_hl; ); cwm_k = 0; loop(16, cwm_sy = 444 - cwm_k * 3.5; ui_col(cwm_k >= 14 ? 0xE5453A : cwm_k >= 11 ? 0xE5C437 : 0xE5A637); gfx_a = gm * 16 > cwm_k + 0.5 ? 1 : floor(cwm_h * 16 - 0.5) == cwm_k && cwm_h > 0.02 ? 0.9 : 0.14; gfx_rect((1066 + gi * 18) * sc, cwm_sy * sc, 14 * sc, 2.5 * sc); cwm_k += 1; ); gfx_setfont(12); ui_col(0xC9BCA6); ui_text((1073 + gi * 18) * sc, 448 * sc, gi ? "R" : "L"); gi += 1; ); // dB scale beside the bars gfx_setfont(12); ui_col(0xC9BCA6); gi = 0; loop(4, cwm_db = gi == 0 ? 0 : gi == 1 ? -12 : gi == 2 ? -24 : -48; cwm_sy = 444 - ((cwm_db + 48) / 48 * 16 - 1) * 3.5; sprintf(#cwm, "%d", cwm_db); gfx_measurestr(#cwm, gw_, gh_); gfx_x = 1140 * sc - gw_; gfx_y = (cwm_sy + 1.25) * sc - gh_ * 0.5; gfx_drawstr(#cwm); gi += 1; ); pkL *= 0.85; pkR *= 0.85; // ---------- PHASER ---------- sgroup(24, 478, 640, 104, "PHASER"); ptk(38 * sc, 508 * sc, 40 * sc, 34 * sc, slider66 == 1, REDL) ? ( slider66 = 1 - slider66; slider_automate(slider66); ); gfx_setfont(10); ui_col(WHT); ui_text(58 * sc, 550 * sc, "ON"); ui_section(1); C_INK = WHT; ui_knob(120 * sc, 546 * sc, 67, 0.05, 8, 0.3, 1, "RATE", "%.2f Hz"); ui_knob(176 * sc, 546 * sc, 68, 0, 100, 70, 0, "DEPTH", "%.0f%%"); ui_knob(238 * sc, 546 * sc, 69, 0, 95, 50, 0, "FEEDBACK", "%.0f%%"); ui_knob(298 * sc, 546 * sc, 70, 0, 100, 100, 0, "MIX", "%.0f%%"); ui_knob(354 * sc, 546 * sc, 71, 0, 180, 90, 0, "STEREO", "%.0f deg"); // the screen: the phaser's response (left solid, right dotted) at the LFO's present position, drawn from the current // settings, and below it the sweep range with the left and right sweep positions. Dimmed while the phaser is off; // the LFO is the audio's own while the phaser runs, otherwise a copy at RATE. ui_col(0x1E1D1C); ui_rrect(392 * sc, 502 * sc, 262 * sc, 74 * sc, 3 * sc); ui_col(0xB8BC9C); gfx_rect(396 * sc, 506 * sc, 254 * sc, 66 * sc); pk_lcdc = 0x262A1E; cwp_dt = cw_now - cwp_t0; cwp_t0 = cw_now; cwp_dt = min(max(cwp_dt, 0), 0.1); ph_mx > 0.0005 ? cwp_lp = ph_lp : ( cwp_lp += slider67 * cwp_dt; cwp_lp -= floor(cwp_lp); ); cwp_on = slider66 ? 1 : 0.4; function cwp_x(f) ( (398 + 250 * log(f / 30) / log(18000 / 30)) * sc ); // frequency grid gfx_setfont(12); gi = 0; loop(3, cwp_f = gi == 0 ? 100 : gi == 1 ? 1000 : 10000; ui_cola(pk_lcdc, 0.18); gfx_line(cwp_x(cwp_f), 518 * sc, cwp_x(cwp_f), 556 * sc); ui_cola(pk_lcdc, 0.55); ui_text(cwp_x(cwp_f), 508 * sc, gi == 0 ? "100" : gi == 1 ? "1k" : "10k"); gi += 1; ); ui_cola(pk_lcdc, 0.18); gfx_line(398 * sc, 524 * sc, 648 * sc, 524 * sc); // 0 dB !slider66 ? ( ui_cola(pk_lcdc, 0.7); gfx_x = 404 * sc; gfx_y = 508 * sc; gfx_drawstr("OFF"); ); ph_g = slider70 / 100; ph_fbk = slider69 / 100 * 0.8; cwp_ch = 0; loop(slider71 > 0 ? 2 : 1, ph_a = ph_coef(ph_freq(cwp_lp + cwp_ch * ph_off)); gi = 0; loop(105, ph_f = 30 * (18000 / 30) ^ (gi / 104); ph_w = 2 * $pi * ph_f / srate; ph_nr = ph_a + cos(ph_w); ph_ni = -sin(ph_w); ph_dr = 1 + ph_a * cos(ph_w); ph_di = -ph_a * sin(ph_w); ph_sr = (ph_nr * ph_dr + ph_ni * ph_di) / (ph_dr * ph_dr + ph_di * ph_di); ph_si = (ph_ni * ph_dr - ph_nr * ph_di) / (ph_dr * ph_dr + ph_di * ph_di); ph_cr = 1; ph_ci = 0; loop(6, ph_t = ph_cr * ph_sr - ph_ci * ph_si; ph_ci = ph_cr * ph_si + ph_ci * ph_sr; ph_cr = ph_t; ); ph_dr = 1 - ph_fbk * ph_cr; ph_di = -ph_fbk * ph_ci; ph_t = (ph_cr * ph_dr + ph_ci * ph_di) / (ph_dr * ph_dr + ph_di * ph_di); ph_ci = (ph_ci * ph_dr - ph_cr * ph_di) / (ph_dr * ph_dr + ph_di * ph_di); ph_cr = ph_t; ph_m = sqrt(sqr(1 + ph_g * (ph_cr - 1) * 0.5) + sqr(ph_g * ph_ci * 0.5)); ph_db = 20 * log10(max(ph_m, 0.001)); ph_x = (398 + gi * 250 / 104) * sc; ph_y = (524 + 32 * (0 - max(min(ph_db, 3), -24)) / 24) * sc; ui_cola(pk_lcdc, cwp_on * (cwp_ch ? 0.5 : 0.95)); gi > 0 && (cwp_ch == 0 || (gi & 1)) ? ( gfx_line(ph_px, ph_py, ph_x, ph_y); cwp_ch == 0 ? gfx_line(ph_px, ph_py + 1 * sc, ph_x, ph_y + 1 * sc); ); ph_px = ph_x; ph_py = ph_y; gi += 1; ); cwp_ch += 1; ); // sweep range and positions ui_cola(pk_lcdc, 0.25 * cwp_on + 0.1); gfx_rect(cwp_x(ph_freq(0.75)), 562 * sc, max(cwp_x(ph_freq(0.25)) - cwp_x(ph_freq(0.75)), 2 * sc), 6 * sc); ui_cola(pk_lcdc, cwp_on); gfx_rect(cwp_x(ph_freq(cwp_lp)) - 1.5 * sc, 559 * sc, 3 * sc, 12 * sc); slider71 > 0 ? ( ui_cola(pk_lcdc, cwp_on * 0.55); gfx_rect(cwp_x(ph_freq(cwp_lp + ph_off)) - 1.5 * sc, 559 * sc, 3 * sc, 12 * sc); ); // ---------- DRIFT ---------- sgroup(672, 478, 484, 104, "DRIFT + FEEL"); ui_section(1); C_INK = WHT; ui_knob(726 * sc, 546 * sc, 63, 0, 100, 25, 0, "DRIFT", "%.0f%%"); ui_knob(806 * sc, 546 * sc, 52, 0, 100, 30, 0, "HITS", "%.0f%%"); ui_knob(886 * sc, 546 * sc, 53, 0, 20, 0, 0, "TIMING", "%.1f ms"); gfx_setfont(10); ui_cola(WHT, 0.72); gfx_x = 942 * sc; gfx_y = 510 * sc; gfx_drawstr("DRIFT: the analogue wander"); gfx_x = 942 * sc; gfx_y = 530 * sc; gfx_drawstr("HITS: each hit a little different"); gfx_x = 942 * sc; gfx_y = 550 * sc; gfx_drawstr("TIMING: loosens the groove"); ui_flatk = 0.8; // ---------- IN / OUT strip: SAT, OUT, STEREO ---------- ins_strip(24, 590, 1132, 57, 64, RACC, SH3); pk_end(); ui_end();