desc:GFX G Arp //tags: midi arpeggiator arp sequencer //author: JGWizrad / Garden FX (GFX) // // Requires gfx-drift, gfx-arp, gfx-panel and gfx-ui (.jsfx-inc) in the same folder. // Place before any synth. Tempo-locked when the project plays, free-running from the first key when stopped. // // Step row: click a step button to turn it on / off, Shift+click for an accent, right-click to tie it // to the step before. Drag in the display above to set each step's level (velocity in PATTERN mode). // FEEL adds a slowly wandering, human timing / velocity variation and a chance for steps to skip. // Non-note MIDI (CCs, pitch bend, etc.) passes straight through. import gfx-drift.jsfx-inc import gfx-arp.jsfx-inc import gfx-ui.jsfx-inc import gfx-panel.jsfx-inc slider1:0<0,6,1{Up,Down,Up-Down,Down-Up,As played,Random,Chord}>-Mode slider2:3<0,5,1{1/4,1/8,1/8T,1/16,1/16T,1/32}>-Rate slider3:1<1,4,1>-Octaves slider4:60<10,200,1>-Gate (%) slider5:0<0,75,1>-Swing (%) slider6:16<1,16,1>-Steps slider7:0<0,1,1{Off,On}>-Latch slider8:100<0,100,1>-Chance (%) slider9:0<0,30,0.1>-Humanise timing (ms) slider10:0<0,40,1>-Humanise velocity slider11:0<0,2,1{As played,Fixed,Pattern}>-Velocity source slider12:100<1,127,1>-Fixed velocity slider13:20<0,60,1>-Accent slider14:1<0,1,1{Off,On}>-Arp on in_pin:none out_pin:none @init #ui_man = "gfx-g-arpeggiator"; // the ? pop-up links to this manual LEV = 10000; ACC = 10020; TIE = 10040; BK = 10060; // 16-step pattern (saved with the project) !pat_init ? ( pat_init = 1; bi = 0; loop(16, LEV[bi] = 1; ACC[bi] = 0; TIE[bi] = 0; BK[bi] = 1; bi += 1; ); ); arp_init(20000); arp_pattern(LEV, ACC, TIE); was_on = 1; !ui_dflt ? ui_thm = 6; !ui_dflt ? ui_hw = 1; // fixed red faceplate function rate_beats(r) ( r == 0 ? 1 : r == 1 ? 0.5 : r == 2 ? 1 / 3 : r == 3 ? 0.25 : r == 4 ? 1 / 6 : 0.125; ); function rate_name(r) ( r == 0 ? "1/4" : r == 1 ? "1/8" : r == 2 ? "1/8T" : r == 3 ? "1/16" : r == 4 ? "1/16T" : "1/32"; ); function mode_name(m) ( m == 0 ? "UP" : m == 1 ? "DOWN" : m == 2 ? "UP-DN" : m == 3 ? "DN-UP" : m == 4 ? "PLAYED" : m == 5 ? "RANDOM" : "CHORD"; ); function pc_name(n) ( n = n % 12; n == 0 ? "C" : n == 1 ? "C#" : n == 2 ? "D" : n == 3 ? "D#" : n == 4 ? "E" : n == 5 ? "F" : n == 6 ? "F#" : n == 7 ? "G" : n == 8 ? "G#" : n == 9 ? "A" : n == 10 ? "A#" : "B"; ); // pattern presets (the knobs and switches stay as they are, except Reset) function setpat(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15) ( LEV[0]=a0; LEV[1]=a1; LEV[2]=a2; LEV[3]=a3; LEV[4]=a4; LEV[5]=a5; LEV[6]=a6; LEV[7]=a7; LEV[8]=a8; LEV[9]=a9; LEV[10]=a10; LEV[11]=a11; LEV[12]=a12; LEV[13]=a13; LEV[14]=a14; LEV[15]=a15; ); function load_pattern(p) local(k) ( k = 0; loop(16, ACC[k] = 0; TIE[k] = 0; k += 1; ); p == 1 ? ( setpat(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1); ) : p == 2 ? ( setpat(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1); ACC[2] = ACC[6] = ACC[10] = ACC[14] = 1; ) : p == 3 ? ( setpat(1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1); ) : p == 4 ? ( setpat(1,0,1,1,0,1,1,0,1,0,1,1,0,1,0,1); ) : p == 5 ? ( k = 0; loop(16, LEV[k] = 0.35 + 0.65 * k / 15; k += 1; ); ) : p == 6 ? ( k = 0; loop(16, LEV[k] = rand(1) < 0.25 ? 0 : 0.4 + rand(0.6); ACC[k] = rand(1) < 0.15; k += 1; ); ) : p == 7 ? ( setpat(1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0); TIE[1] = TIE[5] = TIE[9] = TIE[13] = 1; ) : p == 8 ? ( setpat(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1); slider1 = 0; slider2 = 3; slider3 = 1; slider4 = 60; slider5 = 0; slider6 = 16; slider7 = 0; slider8 = 100; slider9 = 0; slider10 = 0; slider11 = 0; slider12 = 100; slider13 = 20; slider14 = 1; slider_automate(16383); ); k = 0; loop(16, LEV[k] > 0 ? BK[k] = LEV[k]; k += 1; ); ); ui_dflt = 1; // theme defaults above apply once: @init re-runs (playback start, device reset) must not undo the saved theme @block on = slider14; arp_params(slider1, rate_beats(slider2), slider3, slider4 / 100, slider5 / 100, slider6, slider7, slider8 / 100, slider9, slider10, slider11, slider12, slider13); while (midirecv(mofs, m1, m2, m3)) ( st = m1 & 0xF0; on && (st == 0x90 || st == 0x80) ? ( arp_key(m2, m3, m1 & 0x0F, st == 0x90 && m3 > 0); midi_t = time_precise(); ) : ( midisend(mofs, m1, m2, m3); st == 0x90 && m3 > 0 ? midi_t = time_precise(); ); ); // switching the arp off: stop what it was playing, release its note set !on && was_on ? ( arp_outn = 0; arp_flush(); arp_sn = 0; arp_pn = 0; bi = 0; loop(arp_outn, midisend(0, 0x80 | arp_out_ch[bi], arp_out_note[bi], 0); bi += 1; ); ); was_on = on; on ? ( arp_block(play_state & 1, beat_position, tempo, samplesblock); bi = 0; loop(arp_outn, arp_out_on[bi] ? midisend(arp_out_ofs[bi], 0x90 | arp_out_ch[bi], arp_out_note[bi], arp_out_vel[bi]) : midisend(arp_out_ofs[bi], 0x80 | arp_out_ch[bi], arp_out_note[bi], 0); bi += 1; ); ); @serialize file_var(0, ui_sz); file_var(0, pat_init); file_mem(0, LEV, 80); @gfx 780 420 ui_thm = 6; ui_hw = 1; ui_begin(780, 420); RED = 0xB3122A; WHT = 0xE9E6E0; INKD = 0x2A2A2E; AMBL = 0xFFB030; REDL = 0xFF3A2A; // ---------- chassis ---------- ui_col(0x0B0C0D); gfx_rect(0, 0, gfx_w, gfx_h); pk_chassis(8 * sc, 8 * sc, 764 * sc, 404 * sc, RED); pk_screw(20 * sc, 20 * sc); pk_screw(760 * sc, 20 * sc); pk_screw(20 * sc, 400 * sc); pk_screw(760 * sc, 400 * sc); // brand strip ui_col(WHT); gfx_circle(66 * sc, 52 * sc, 17 * sc, 1, 1); gfx_setfont(2); ui_col(RED); ui_text(66 * sc, 45 * sc, "GFX"); gfx_setfont(4); ui_col(WHT); ui_text(66 * sc, 76 * sc, "ARPEGGIATOR"); pk_ledbtn(36 * sc, 100 * sc, 60 * sc, 46 * sc, slider14, REDL) ? ( slider14 = 1 - slider14; slider_automate(8192); ); gfx_setfont(4); ui_col(WHT); ui_text(66 * sc, 150 * sc, "ARP ON"); pk_ledbtn(36 * sc, 172 * sc, 60 * sc, 46 * sc, slider7, REDL) ? ( slider7 = 1 - slider7; slider_automate(64); ); gfx_setfont(4); ui_col(WHT); ui_text(66 * sc, 222 * sc, "LATCH"); pk_led(52 * sc, 252 * sc, 2.8 * sc, time_precise() - midi_t < 0.12, 0x3ADF6A); gfx_setfont(4); ui_col(WHT); gfx_x = 60 * sc; gfx_y = 246 * sc; gfx_drawstr("MIDI"); pk_ledbtn(36 * sc, 290 * sc, 60 * sc, 46 * sc, 0, AMBL) ? ( gfx_x = 36 * sc; gfx_y = 336 * sc; load_pattern(gfx_showmenu("Straight|Offbeat accents|Gallop|Trance gate|Rise|Random|Tied pairs|Reset all to defaults")); ); gfx_setfont(4); ui_col(WHT); ui_text(66 * sc, 340 * sc, "PATTERNS"); ui_size_picker(38 * sc, 372 * sc); // ---------- white control panel ---------- pk_panel(140 * sc, 30 * sc, 330 * sc, 192 * sc, WHT); pk_line(335 * sc, 40 * sc, 335 * sc, 212 * sc, INKD, 0.25); pk_line(150 * sc, 126 * sc, 460 * sc, 126 * sc, INKD, 0.25); gfx_setfont(4); ui_col(RED); ui_text(235 * sc, 36 * sc, "TIME"); ui_text(405 * sc, 36 * sc, "RANGE"); ui_text(235 * sc, 130 * sc, "FEEL"); ui_text(405 * sc, 130 * sc, "NOTES"); C_INK = INKD; ui_section(2); knob_step = 1; ui_knob(175 * sc, 82 * sc, 2, 0, 5, 3, 0, "RATE", rate_name(slider2)); ui_knob(235 * sc, 82 * sc, 5, 0, 75, 0, 0, "SWING", "%.0f%%"); ui_knob(295 * sc, 82 * sc, 4, 10, 200, 60, 0, "GATE", "%.0f%%"); knob_step = 1; ui_knob(375 * sc, 82 * sc, 3, 1, 4, 1, 0, "OCTAVES", "%.0f"); knob_step = 1; ui_knob(435 * sc, 82 * sc, 6, 1, 16, 16, 0, "STEPS", "%.0f"); ui_section(1); C_INK = INKD; ui_knob(175 * sc, 172 * sc, 9, 0, 30, 0, 0, "TIMING", "%.1f ms"); ui_knob(235 * sc, 172 * sc, 10, 0, 40, 0, 0, "VELOCITY", "%.0f"); ui_knob(295 * sc, 172 * sc, 8, 0, 100, 100, 0, "CHANCE", "%.0f%%"); ui_section(2); C_INK = INKD; ui_knob(375 * sc, 172 * sc, 13, 0, 60, 20, 0, "ACCENT", "+%.0f"); ui_knob(435 * sc, 172 * sc, 12, 1, 127, 100, 0, "LEVEL", "%.0f"); // ---------- display + modes ---------- pk_lcd(490 * sc, 30 * sc, 262 * sc, 58 * sc, 1); pk_lcdfont(7, 14 * sc); ui_col(pk_lcdc); sprintf(#l1, "%-6s %-5s %dOCT", mode_name(slider1), rate_name(slider2), slider3); gfx_x = 500 * sc; gfx_y = 38 * sc; gfx_drawstr(#l1); pk_lcdfont(8, 11 * sc); !slider14 ? strcpy(#l2, "ARP OFF - THRU") : arp_sn == 0 ? strcpy(#l2, slider7 ? "LATCH PLAY A CHORD" : "PLAY A CHORD") : ( sprintf(#l2, "%02d/%02d ", arp_step + 1, slider6); gi = 0; loop(min(arp_sn, 5), sprintf(#nn, "%s%d ", pc_name(arp_S[gi]), floor(arp_S[gi] / 12) - 1); strcat(#l2, #nn); gi += 1; ); arp_sn > 5 ? strcat(#l2, "+"); ); gfx_x = 500 * sc; gfx_y = 64 * sc; gfx_drawstr(#l2); gi = 0; loop(8, gx = (490 + (gi % 4) * 68) * sc; gy = (gi < 4 ? 100 : 160) * sc; gi < 7 ? ( pk_ledbtn(gx, gy, 58 * sc, 44 * sc, slider1 == gi, REDL) ? ( slider1 = gi; slider_automate(1); ); gfx_setfont(4); ui_col(WHT); ui_text(gx + 29 * sc, gy + 46 * sc, mode_name(gi)); ) : ( pk_ledbtn(gx, gy, 58 * sc, 44 * sc, slider11 > 0, AMBL) ? ( slider11 = slider11 >= 2 ? 0 : slider11 + 1; slider_automate(1024); ); gfx_setfont(4); ui_col(WHT); ui_text(gx + 29 * sc, gy + 46 * sc, slider11 == 0 ? "VEL: PLAYED" : slider11 == 1 ? "VEL: FIXED" : "VEL: PATTERN"); ); gi += 1; ); // ---------- step display (drag to set levels) ---------- pk_panel(140 * sc, 234 * sc, 620 * sc, 92 * sc, 0x16161A); gsx = 146 * sc; gsw = 38.6 * sc; gby = 318 * sc; gbh = 72 * sc; gin = mouse_x >= 140 * sc && mouse_x < 760 * sc && mouse_y >= 234 * sc && mouse_y < 326 * sc; gstep = floor((mouse_x - gsx) / gsw); gstep = min(max(gstep, 0), 15); // shift-click a bar: accent; right-click: tie pressed && gin && drag_id == 0 && (mouse_cap & 8) ? ( ACC[gstep] = !ACC[gstep]; drag_id = -1; ); (mouse_cap & 2) && !g_prevr && gin ? TIE[gstep] = !TIE[gstep]; mdown && gin && (drag_id == 0 || drag_id == -7) ? ( drag_id = -7; LEV[gstep] = min(max((gby - mouse_y) / gbh, 0), 1); LEV[gstep] < 0.04 ? LEV[gstep] = 0 : BK[gstep] = LEV[gstep]; ); gi = 0; loop(16, gx = gsx + gi * gsw; gdim = gi >= slider6; gi == arp_step && slider14 && arp_sn > 0 ? ( ui_cola(0xFFFFFF, 0.07); gfx_rect(gx - 2 * sc, 238 * sc, 36 * sc, 84 * sc); ); ui_cola(0xFFFFFF, 0.06); gfx_rect(gx, gby - gbh, 32 * sc, gbh); LEV[gi] > 0 ? ( ui_cola(WHT, gdim ? 0.25 : 0.85); gfx_rect(gx, gby - gbh * LEV[gi], 32 * sc, gbh * LEV[gi]); ACC[gi] ? ( ui_col(gdim ? 0x5A1A20 : REDL); gfx_rect(gx, gby - gbh * LEV[gi], 32 * sc, 4 * sc); ); ); TIE[gi] ? ( ui_col(gdim ? 0x6A4A20 : AMBL); gfx_rect(gx - 7 * sc, gby - gbh * 0.5, 10 * sc, 3 * sc); gfx_setfont(4); ui_text(gx + 16 * sc, gby - gbh * 0.5 - 14 * sc, "TIE"); ); gi += 1; ); // ---------- step buttons ---------- gi = 0; loop(16, gx = gsx + gi * gsw; gcur = gi == arp_step && slider14 && arp_sn > 0; pk_ledbtn(gx, 336 * sc, 32 * sc, 40 * sc, gcur || (LEV[gi] > 0 && gi < slider6), gcur ? REDL : AMBL) ? ( mouse_cap & 8 ? ACC[gi] = !ACC[gi] : LEV[gi] > 0 ? ( BK[gi] = LEV[gi]; LEV[gi] = 0; ) : ( LEV[gi] = BK[gi] > 0 ? BK[gi] : 1; ); ); pk_rclick ? TIE[gi] = !TIE[gi]; gfx_setfont(4); ui_col(gi < slider6 ? WHT : 0xD07080); sprintf(#sn, ACC[gi] ? "%d>" : "%d", gi + 1); ui_text(gx + 16 * sc, 380 * sc, #sn); gi += 1; ); g_prevr = mouse_cap & 2; pk_end(); ui_end();