desc:Daichi [VA poly synth] (GFX Lab)
//tags: instrument synth poly virtual analogue lead bass pad fm sync unison arpeggiator
//author: JGWizrad / Garden FX (GFX)
//
// Requires gfx-lab-drift, gfx-lab-output, gfx-lab-ui, gfx-lab-panel, gfx-lab-instrument, gfx-lab-voice, gfx-lab-arp, gfx-lab-dyn, gfx-lab-faceplate and gfx-lab-clip (.jsfx-inc) in the same folder.
//
// A virtual-analogue polysynth in the spirit of the much-loved early-2000s freeware VA, itself modelled on the
// classic red Swedish virtual-analogue lead synth: two oscillators, a sync / FM / ring section, a mod envelope,
// a multimode filter, two LFOs, unison, an arpeggiator and a compact effects strip.
//   OSC 1     sine, triangle, saw, square (pulse, PULSE WIDTH) or noise; SUB one octave down (sine or square);
//             FM: OSC 2 modulates OSC 1 (phase modulation on sine / triangle, through-zero-clamped FM on the others)
//   OSC 2     the same waves; PITCH (semitones), FINE, TRACK (off = fixed pitch, ignores the keyboard),
//             SYNC (hard sync to OSC 1, band-limited); RING (OSC 1 x OSC 2); MIX balances OSC 1 <-> OSC 2
//   MOD ENV   attack / decay envelope (AMOUNT +/-) to OSC 2 pitch, FM amount or pulse width
//   FILTER    12 dB LP, 24 dB LP (zero-delay ladder, self-oscillates at full RESONANCE, soft-limited), 12 dB HP,
//             12 dB BP (zero-delay state-variable); CUTOFF, RESONANCE, SATURATION, KEY track, ENV amount,
//             VELOCITY; its own ADSR
//   AMP       ADSR, GAIN, VELOCITY
//   LFO 1-3   (the Lab's new idea: deeper LFOs) eight shapes - sine, triangle, saw up, saw down, square, S&H,
//             smooth random and STEP (a 16-step pattern drawn on the panel; RATE is then the step rate);
//             RATE free (Hz), SYNC (locked to the project's beat) or FLOW (tempo speed, free-running phase);
//             two destinations, each with its own +/- DEPTH: pitch, OSC 2 pitch, filter, amp, pan, FM, pulse
//             width, resonance, OSC mix, ring, sub, unison detune, chorus depth, delay mix, and another LFO's
//             rate or depth; GLOBAL (one LFO for the whole synth) or VOICE (every note its own LFO, so a chord
//             shimmers instead of pulsing as one); KEY restarts it on every note (VOICE, off: each note starts
//             at a random point); FADE brings it in gently after the note starts (delayed vibrato).
//             Every shape is rounded over 2 ms (the saws, square, S&H and STEP never click), the depths glide 30 ms.
//   VOICE     POLY / MONO / LEGATO, VOICES (1-16), UNISON (1-8 voices per note, DETUNE, SPREAD - the unison
//             voices come out of the VOICES pool), PORTAMENTO, BEND range, mod WHEEL destination
//   ARP       the GFX arpeggiator: MODE, RANGE (1-4 octaves), RATE (1/4 - 1/32, triplets), GATE; tempo-locked
//   EFFECTS   DIST (drive or bit crush), PHASER, CHORUS / FLANGER, stereo tempo-synced DELAY (ping-pong)
//   MASTER    DRIFT, VOLUME, SAT (the output soft clip is always on)
// DRIFT (GFX instrument standard, 0-100 %): the tuning wanders, OSC 2 wanders against OSC 1, the filter
//   cutoff wanders, the chorus / phaser speed breathes, and every note lands slightly differently in tune.
//   The LFOs drift too (JG): each free / FLOW LFO's speed wanders (+/-4 % at 50 %), and in VOICE mode each note's
//   LFO runs at its own slightly different speed (+/-3 %) and, with KEY on, starts a touch off the beat.
//   SYNC LFOs stay locked to the grid.
// GUI: the LFO section shows one LFO at a time (keys 1 / 2 / 3, a green lamp on each LFO that is working): GLOBAL /
//   VOICE, KEY, FREE / SYNC / FLOW, the eight shapes, RATE, FADE, a window with the shape and where the LFO is now
//   (STEP: the 16 steps, click and drag to draw them), DEPTH A / B with a destination menu each.
// MIDI: pitch bend (BEND range), CC1 mod wheel (WHEEL destination), CC64 sustain, CC120 / CC123 all notes off.
//   All MIDI is passed through to the next plug-in.

import gfx-lab-drift.jsfx-inc
import gfx-lab-output.jsfx-inc
import gfx-lab-ui.jsfx-inc
import gfx-lab-panel.jsfx-inc
import gfx-lab-instrument.jsfx-inc
import gfx-lab-voice.jsfx-inc
import gfx-lab-arp.jsfx-inc
import gfx-lab-dyn.jsfx-inc
import gfx-lab-faceplate.jsfx-inc
import gfx-lab-clip.jsfx-inc

// ---------- OSC 1 ----------
slider1:2<0,4,1{Sine,Triangle,Saw,Square,Noise}>-Osc 1 wave
slider2:0<0,1,1{Sine,Square}>-Sub wave
slider3:0<0,100,1>-Sub level (%)
slider4:0<0,100,1>-FM (osc 2 > osc 1, %)
// ---------- OSC 2 ----------
slider5:2<0,4,1{Sine,Triangle,Saw,Square,Noise}>-Osc 2 wave
slider6:0<-24,24,1>-Osc 2 pitch (st)
slider7:8<-50,50,0.1>-Osc 2 fine (cents)
slider8:1<0,1,1{Off,On}>-Osc 2 track
slider9:0<0,1,1{Off,On}>-Osc 2 sync
slider10:0<0,100,1>-Ring (%)
slider11:50<0,100,1>-Osc mix (1 <> 2, %)
slider12:50<5,95,1>-Pulse width (%)
// ---------- MOD ENV ----------
slider13:1<0.5,5000,0.1>-Mod env attack (ms)
slider14:300<5,5000,1>-Mod env decay (ms)
slider15:0<-100,100,1>-Mod env amount (%)
slider16:0<0,2,1{Osc 2 pitch,FM,Pulse width}>-Mod env destination
// ---------- FILTER ----------
slider17:1<0,3,1{LP 12,LP 24,HP 12,BP 12}>-Filter type
slider18:6000<20,20000,1>-Cutoff (Hz)
slider19:10<0,100,1>-Resonance (%)
slider20:0<0,100,1>-Filter saturation (%)
slider21:50<0,100,1>-Key track (%)
slider22:0<-100,100,1>-Filter env amount (%)
slider23:0<0,100,1>-Filter velocity (%)
slider24:1<0.5,5000,0.1>-Filter attack (ms)
slider25:400<5,8000,1>-Filter decay (ms)
slider26:50<0,100,1>-Filter sustain (%)
slider27:300<5,8000,1>-Filter release (ms)
// ---------- AMP ----------
slider28:1<0.5,5000,0.1>-Amp attack (ms)
slider29:300<5,8000,1>-Amp decay (ms)
slider30:100<0,100,1>-Amp sustain (%)
slider31:200<5,8000,1>-Amp release (ms)
slider32:0<-24,12,0.1>-Amp gain (dB)
slider33:50<0,100,1>-Amp velocity (%)
// ---------- LFO 1 ----------
slider34:0<0,7,1{Sine,Triangle,Saw up,Square,S&H,Saw down,Smooth random,Step}>-LFO 1 shape
slider35:5<0.02,30,0.01>-LFO 1 rate (Hz)
slider36:0<0,2,1{Free,Sync,Flow}>-LFO 1 timing
slider37:5<0,11,1{4/1,2/1,1/1,1/2,1/2T,1/4,1/4T,1/8,1/8T,1/16,1/16T,1/32}>-LFO 1 synced rate
slider38:0<-100,100,1>-LFO 1 depth A (%)
slider39:2<0,19,1{Pitch,Osc 2 pitch,Filter,Amp,Pan,FM,Pulse width,Resonance,Osc mix,Ring,Sub,Unison detune,Chorus depth,Delay mix,LFO 1 rate,LFO 2 rate,LFO 3 rate,LFO 1 depth,LFO 2 depth,LFO 3 depth}>-LFO 1 destination A
slider40:0<0,1,1{Off,On}>-LFO 1 key sync
// ---------- LFO 2 ----------
slider41:1<0,7,1{Sine,Triangle,Saw up,Square,S&H,Saw down,Smooth random,Step}>-LFO 2 shape
slider42:0.5<0.02,30,0.01>-LFO 2 rate (Hz)
slider43:0<0,2,1{Free,Sync,Flow}>-LFO 2 timing
slider44:5<0,11,1{4/1,2/1,1/1,1/2,1/2T,1/4,1/4T,1/8,1/8T,1/16,1/16T,1/32}>-LFO 2 synced rate
slider45:0<-100,100,1>-LFO 2 depth A (%)
slider46:0<0,19,1{Pitch,Osc 2 pitch,Filter,Amp,Pan,FM,Pulse width,Resonance,Osc mix,Ring,Sub,Unison detune,Chorus depth,Delay mix,LFO 1 rate,LFO 2 rate,LFO 3 rate,LFO 1 depth,LFO 2 depth,LFO 3 depth}>-LFO 2 destination A
slider47:0<0,1,1{Off,On}>-LFO 2 key sync
// ---------- VOICE ----------
slider48:0<0,2,1{Poly,Mono,Legato}>-Voice mode
slider49:8<1,16,1>-Voices
slider50:1<1,8,1>-Unison voices
slider51:15<0,50,0.1>-Unison detune (cents)
slider52:60<0,100,1>-Unison spread (%)
slider53:0<0,2000,1>-Portamento (ms)
slider54:2<0,24,1>-Pitch bend range (st)
slider55:1<0,4,1{Off,Vibrato,LFO 1 amount,Cutoff,FM}>-Mod wheel destination
// ---------- ARP ----------
slider56:0<0,1,1{Off,On}>-Arp
slider57:0<0,6,1{Up,Down,Up-Down,Down-Up,As played,Random,Chord}>-Arp mode
slider58:1<1,4,1>-Arp range (octaves)
slider59:4<0,6,1{1/4,1/4T,1/8,1/8T,1/16,1/16T,1/32}>-Arp rate
slider60:60<10,200,1>-Arp gate (%)
// ---------- EFFECTS ----------
slider61:0<0,1,1{Off,On}>-Distortion
slider62:0<0,1,1{Drive,Crush}>-Distortion type
slider63:30<0,100,1>-Distortion amount (%)
slider64:0<0,1,1{Off,On}>-Phaser
slider65:0.4<0.05,10,0.01>-Phaser rate (Hz)
slider66:60<0,100,1>-Phaser depth (%)
slider67:0<0,1,1{Off,On}>-Chorus
slider68:0<0,1,1{Chorus,Flanger}>-Chorus type
slider69:0.6<0.05,10,0.01>-Chorus rate (Hz)
slider70:50<0,100,1>-Chorus depth (%)
slider71:0<0,1,1{Off,On}>-Delay
slider72:4<0,7,1{1/2,1/4D,1/4,1/4T,1/8D,1/8,1/8T,1/16}>-Delay time
slider73:35<0,90,1>-Delay feedback (%)
slider74:25<0,100,1>-Delay mix (%)
slider75:1<0,1,1{Off,On}>-Delay ping-pong

// ---------- MASTER ----------
slider76:25<0,100,1>-Drift (%)
slider77:0<-24,6,0.1>-Volume (dB)
slider78:0<0,16,1>-MIDI channel (0 = omni)
slider79:0<0,100,1>-Band sat (%)
slider80:30<0,100,1>-Bias (%)
slider81:120<40,400,1>-Sat low band (Hz)
slider82:6000<2000,16000,10>-Sat high band (Hz)
slider83:-0.3<-24,0,0.1>-Ceiling (dB)
slider84:1<0,1,1{Off,Clip}>-(unused, was output stage)
// ---------- LFO 1 / 2: second destination, mode, fade ----------
slider85:0<-100,100,1>-LFO 1 depth B (%)
slider86:6<0,19,1{Pitch,Osc 2 pitch,Filter,Amp,Pan,FM,Pulse width,Resonance,Osc mix,Ring,Sub,Unison detune,Chorus depth,Delay mix,LFO 1 rate,LFO 2 rate,LFO 3 rate,LFO 1 depth,LFO 2 depth,LFO 3 depth}>-LFO 1 destination B
slider87:0<0,1,1{Global,Voice}>-LFO 1 mode
slider88:0<0,5000,1>-LFO 1 fade (ms)
slider89:0<-100,100,1>-LFO 2 depth B (%)
slider90:2<0,19,1{Pitch,Osc 2 pitch,Filter,Amp,Pan,FM,Pulse width,Resonance,Osc mix,Ring,Sub,Unison detune,Chorus depth,Delay mix,LFO 1 rate,LFO 2 rate,LFO 3 rate,LFO 1 depth,LFO 2 depth,LFO 3 depth}>-LFO 2 destination B
slider91:0<0,1,1{Global,Voice}>-LFO 2 mode
slider92:0<0,5000,1>-LFO 2 fade (ms)
// ---------- LFO 3 ----------
slider93:0<0,7,1{Sine,Triangle,Saw up,Square,S&H,Saw down,Smooth random,Step}>-LFO 3 shape
slider94:0.25<0.02,30,0.01>-LFO 3 rate (Hz)
slider95:0<0,2,1{Free,Sync,Flow}>-LFO 3 timing
slider96:5<0,11,1{4/1,2/1,1/1,1/2,1/2T,1/4,1/4T,1/8,1/8T,1/16,1/16T,1/32}>-LFO 3 synced rate
slider97:0<-100,100,1>-LFO 3 depth A (%)
slider98:2<0,19,1{Pitch,Osc 2 pitch,Filter,Amp,Pan,FM,Pulse width,Resonance,Osc mix,Ring,Sub,Unison detune,Chorus depth,Delay mix,LFO 1 rate,LFO 2 rate,LFO 3 rate,LFO 1 depth,LFO 2 depth,LFO 3 depth}>-LFO 3 destination A
slider99:0<0,1,1{Off,On}>-LFO 3 key sync
slider100:0<-100,100,1>-LFO 3 depth B (%)
slider101:4<0,19,1{Pitch,Osc 2 pitch,Filter,Amp,Pan,FM,Pulse width,Resonance,Osc mix,Ring,Sub,Unison detune,Chorus depth,Delay mix,LFO 1 rate,LFO 2 rate,LFO 3 rate,LFO 1 depth,LFO 2 depth,LFO 3 depth}>-LFO 3 destination B
slider102:0<0,1,1{Global,Voice}>-LFO 3 mode
slider103:0<0,5000,1>-LFO 3 fade (ms)
slider104:-11.6<-30,6,0.1>-Preset level (dB)
slider105:prs_sel=0<0,40,1{No preset,Init,Fat Saw Bass,Acid Line,FM Bass,Sub Bass,Sync Lead,Square Lead,Unison Lead,Soft Lead,PWM Strings,Warm Pad,Sweep Pad,Glass Pad,FM E-Piano,Bell Tines,Clav Pluck,Organ,Poly Brass,Brass Stab,Arp Pulse,Arp Glass,Chip Square,Chip Arp,Noise Sweep,Wind Riser,Laser Zap,Reso Pluck,Hoover,Bit Bass,Phase Keys,Fifth Lead,Dark Drone,Shimmer Pad,Late Vibrato,Step Bass,Gate Pad,Drunk Strings,Tremolo Keys,Random Ripple,Wobble Bass}>-Preset

in_pin:none
out_pin:left output
out_pin:right output

@init
#ui_man = "gfx-lab-g-daichi";                                  // the ? pop-up links to this manual
ext_noinit = 1;

// ---------- memory ----------
ost_init(20000); ostL.ost_ch_init(); ostR.ost_ch_init();   // 20000 - 24095
arp_init(30000);                                           // 30000 - 34095
DV  = 40000;                                               // 16 voices x 64 slots
DHN = 41100;                                               // mono held-note stack (128)
DQO = 41300; DQT = 41600; DQN = 41900; DQV = 42200;        // note event queue (300 each)
DLEV = 42600; DACC = 42620; DTIE = 42640;                  // arp pattern: every step on
DEFM = 43600;                                              // the Init values of sliders 1-75 and 85-103 (DEFM + 84 ..)
DLF = 43400;                                               // LFO settings, 16 per LFO (see dh_lfo_set)
DLG = 43450;                                               // the global LFOs' running state, 12 per LFO
DSTEP = 43500;                                             // the STEP patterns, 16 per LFO (-1 .. 1)
DTMP = 42800;                                              // preset parser scratch (400)
DCHL = 44000; DCHR = 52192; DCHM = 8191;                   // chorus / flanger lines (8192 each)
DDLL = 61000; DDLR = 61000 + 524288; DDLM = 524287;        // delay lines (2^19 each)
memset(DV, 0, 1024);
dh_i = 0; loop(16, DLEV[dh_i] = 1; DACC[dh_i] = 0; DTIE[dh_i] = 0; dh_i += 1; );
arp_pattern(DLEV, DACC, DTIE);
dh_i = 0; loop(16, DV[dh_i * 64] = -1; dh_i += 1; );
memset(DCHL, 0, 8192 * 2);
memset(DDLL, 0, 524288 * 2);

// voice slots (p = DV + 64 * voice):
//   0 note (-1 free)  1 key held  2 age  3 held by pedal  4 pitch now (st)  5 pitch target  6 velocity 0..1
//   7 note detune (st)  8 unison offset (st)  9 unison pan  10 osc 1 phase  11 osc 2 phase  12 sub phase
//   13 amp stage (0 idle, 1 att, 2 dec / sus, 3 rel)  14 amp level  15 filter env stage  16 its level
//   17 mod env stage (0 off, 1 att, 2 dec)  18 its level  19-22 filter states  23 osc 2 (one-sample delayed)
//   24 osc 1 inc  25 osc 2 inc  26-28 filter coefficients  29 pulse width  30 FM  31 / 32 pan gains L / R
//   33 osc 2 last output  34 voice gain  35 VOICE-LFO amp gain  36 / 37 filter damping / ladder feedback (with
//   the LFOs' resonance)  38 needs its control values now  39 - 42 OSC 1 / OSC 2 / ring / sub levels (with the LFOs)
//   44 + 5 l .. (l = LFO 0-2, VOICE mode): phase, held value, previous held value, rounded output, fade level
//   59 + l  this note's LFO speed (drift)

L12 = log(2) / 12; LN2 = log(2);

// ---------- presets: the Init values, and each preset as "slider:value" changes from Init ----------
#dh_def = "2 0 0 0 2 0 8 1 0 0 50 50 1 300 0 0 1 6000 10 0 50 0 0 1 400 50 300 1 300 100 200 0 50 0 5 0 5 0 2 0 1 0.5 0 5 0 0 0 0 8 1 15 60 0 2 1 0 0 1 4 60 0 0 30 0 0.4 60 0 0 0.6 50 0 4 35 25 1";

#dh_def2 = "0 6 0 0 0 2 0 0 0 0.25 0 5 0 2 0 0 4 0 0 -11.6";     // sliders 85-104

// the default STEP pattern (a rising four-step figure) and what a preset can ask for instead
function dh_stepdef(l) local(i) ( i = 0; loop(16, DSTEP[l * 16 + i] = ((i & 3) - 1.5) / 1.5; i += 1; ); );

// parse the numbers in s (separated by spaces or colons) into dst; returns the count
function dh_nums(s, dst) local(i, n, c, len, acc, neg, fr, scl, inn) (
  n = 0; i = 0; inn = 0; len = strlen(s);
  loop(len + 1,
    c = i < len ? str_getchar(s, i) : 32;
    c >= 48 && c <= 57 ? (
      !inn ? ( inn = 1; acc = 0; fr = 0; scl = 1; neg = 0; );
      fr ? ( scl *= 0.1; acc += (c - 48) * scl; ) : acc = acc * 10 + c - 48;
    ) : c == 46 ? (
      !inn ? ( inn = 1; acc = 0; scl = 1; neg = 0; ); fr = 1;
    ) : c == 45 ? (
      inn = 1; acc = 0; fr = 0; scl = 1; neg = 1;
    ) : (
      inn ? ( dst[n] = neg ? -acc : acc; n += 1; inn = 0; );
    );
    i += 1;
  );
  n;
);
dh_nums(#dh_def, DEFM); dh_nums(#dh_def2, DEFM + 84);
!ui_dflt ? ( dh_stepdef(0); dh_stepdef(1); dh_stepdef(2); );   // (a saved project brings its own patterns)

#dh_pm = "Init|Fat Saw Bass|Acid Line|FM Bass|Sub Bass|Sync Lead|Square Lead|Unison Lead|Soft Lead|PWM Strings|Warm Pad|Sweep Pad|Glass Pad|FM E-Piano|Bell Tines|Clav Pluck|Organ|Poly Brass|Brass Stab|Arp Pulse|Arp Glass|Chip Square|Chip Arp|Noise Sweep|Wind Riser|Laser Zap|Reso Pluck|Hoover|Bit Bass|Phase Keys|Fifth Lead|Dark Drone|Shimmer Pad|Late Vibrato|Step Bass|Gate Pad|Drunk Strings|Tremolo Keys|Random Ripple|Wobble Bass";
function dh_pstr(p) (
  p == 2  ? "1:2 5:2 6:-12 7:-9 11:45 3:40 17:1 18:420 19:22 22:55 24:1 25:380 26:25 27:160 28:0.5 29:600 30:85 31:120 49:2 48:2 53:30 50:2 51:9 52:30 20:30 32:1 104:-7.3" :
  p == 3  ? "1:2 5:3 11:0 17:1 18:300 19:78 20:45 22:70 21:30 24:0.5 25:220 26:0 27:120 28:0.5 29:300 30:90 31:80 48:2 49:1 53:60 23:30 32:-7 104:-9.4" :
  p == 4  ? "1:0 5:0 6:12 7:0 11:0 4:20 15:60 16:1 13:0.5 14:250 17:0 18:1800 19:0 22:30 25:300 26:20 3:30 2:0 28:0.5 29:700 30:60 31:150 48:1 49:1 32:0 104:-7.8" :
  p == 5  ? "1:0 5:1 6:12 7:0 11:12 3:45 2:0 17:0 18:500 19:5 22:15 25:200 26:30 28:2 29:500 30:90 31:180 48:2 49:1 53:20 32:0 104:-9.8" :
  p == 6  ? "1:2 5:2 9:1 6:7 11:100 15:45 16:0 13:0.5 14:900 17:1 18:5200 19:15 22:20 25:600 26:60 28:2 30:100 31:300 48:2 49:1 53:40 34:0 35:5.5 38:6 39:0 32:2 104:-9.9" :
  p == 7  ? "1:3 5:3 6:12 7:4 11:30 12:42 17:0 18:3600 19:20 22:25 25:500 26:55 28:2 31:250 48:2 49:1 53:50 34:1 35:5.2 38:7 39:0 41:0 42:0.3 45:20 46:6 32:-3 104:-9.2" :
  p == 8  ? "1:2 5:2 6:0 7:12 11:50 17:1 18:7000 19:12 22:15 25:600 26:70 28:3 31:350 48:2 49:16 50:7 51:22 52:90 53:35 67:1 68:0 69:0.4 70:40 71:1 72:5 73:30 74:22 32:2 104:-4.9" :
  p == 9  ? "1:1 5:0 6:12 7:0 11:25 17:0 18:2600 19:5 22:25 24:20 25:800 26:40 28:25 29:500 30:90 31:400 48:2 49:1 53:80 34:0 35:5 38:6 39:0 71:1 72:4 73:40 74:25 32:1 104:-9.7" :
  p == 10 ? "1:3 5:3 6:0 7:10 11:50 12:35 17:0 18:3500 19:8 22:10 24:300 25:900 26:80 27:900 28:350 29:600 30:90 31:900 34:1 35:0.7 38:55 39:6 41:1 42:0.45 45:40 46:6 67:1 69:0.5 70:60 49:8 32:-5 104:-10.7" :
  p == 11 ? "1:2 5:2 6:0 7:12 11:50 17:1 18:1400 19:15 22:20 24:900 25:2500 26:60 27:2500 28:800 29:1500 30:85 31:2500 34:0 35:0.3 38:15 39:2 49:8 67:1 69:0.35 70:55 71:1 72:2 73:45 74:20 3:20 32:1 104:-10.9" :
  p == 12 ? "1:2 5:3 6:7 7:6 11:45 12:30 17:1 18:300 19:45 22:65 24:2500 25:5000 26:30 27:3000 28:1200 29:2000 30:90 31:3000 34:1 35:0.12 38:30 39:2 41:0 42:0.25 45:20 46:6 49:8 64:1 65:0.15 66:70 71:1 72:1 73:50 74:25 32:1 104:-8.2" :
  p == 13 ? "1:0 5:1 6:19 7:5 11:40 10:30 17:2 18:220 19:20 22:0 28:600 29:2000 30:80 31:2500 34:0 35:4.5 38:8 39:1 41:0 42:0.2 45:15 46:2 49:8 67:1 69:0.3 70:50 71:1 72:1 73:50 74:30 32:3 104:-9.1" :
  p == 14 ? "1:0 5:0 6:0 7:3 11:0 4:28 15:35 16:1 13:2 14:900 17:0 18:9000 19:0 28:3 29:2200 30:25 31:700 33:70 49:12 64:1 65:0.6 66:40 67:1 69:0.8 70:35 32:-1 104:-3.8" :
  p == 15 ? "1:0 5:0 6:28 7:14 11:0 4:35 15:40 16:1 13:0.5 14:1500 17:0 18:12000 19:0 28:0.5 29:3500 30:0 31:3000 33:60 49:12 71:1 72:2 73:35 74:22 32:-4.5 104:-6.2" :
  p == 16 ? "1:3 5:2 6:12 7:0 11:35 12:22 17:3 18:900 19:35 22:60 24:0.5 25:180 26:0 27:120 28:0.5 29:400 30:0 31:150 33:60 49:8 64:1 65:0.8 66:35 32:8.5 104:-3.4" :
  p == 17 ? "1:0 5:0 6:12 7:0 11:45 3:60 2:0 17:0 18:9000 19:0 21:0 28:3 29:300 30:100 31:60 34:0 35:6.5 38:4 39:0 49:10 67:1 68:0 69:5.8 70:30 33:0 32:-4.5 104:-11.1" :
  p == 18 ? "1:2 5:2 6:0 7:9 11:50 17:1 18:600 19:10 22:55 24:60 25:600 26:45 27:400 23:40 28:40 29:500 30:85 31:350 34:0 35:5 38:4 39:0 49:8 67:1 69:0.5 70:30 32:1.5 104:-10.8" :
  p == 19 ? "1:2 5:3 6:0 7:7 11:40 12:40 17:1 18:500 19:18 22:70 24:0.5 25:280 26:15 27:200 23:50 28:0.5 29:350 30:30 31:220 49:8 50:2 51:10 52:70 32:2.5 104:-4.3" :
  p == 20 ? "1:3 5:2 6:12 7:0 11:30 12:30 17:1 18:700 19:45 22:55 24:0.5 25:200 26:0 27:150 28:0.5 29:250 30:0 31:200 56:1 57:0 58:2 59:4 60:55 71:1 72:4 73:40 74:25 41:1 42:0.1 45:35 46:2 32:-1.5 104:-18.5" :
  p == 21 ? "1:0 5:1 6:19 7:0 11:35 4:20 17:0 18:6000 19:10 28:0.5 29:500 30:0 31:400 56:1 57:2 58:3 59:4 60:70 71:1 72:3 73:50 74:30 67:1 70:40 32:-1 104:-19.5" :
  p == 22 ? "1:3 5:3 6:12 7:0 11:20 12:25 17:0 18:20000 19:0 21:0 28:0.5 30:100 31:40 48:1 49:1 34:3 35:12 38:0 53:0 41:3 42:7 45:0 32:-2.5 55:2 39:0 104:-9.7" :
  p == 23 ? "1:3 5:3 6:12 7:0 11:0 12:50 17:0 18:20000 19:0 21:0 28:0.5 29:150 30:0 31:60 56:1 57:2 58:3 59:6 60:50 32:0.5 104:-24.3" :
  p == 24 ? "1:4 5:4 11:0 17:3 18:200 19:55 21:0 22:100 24:3000 25:4000 26:0 27:1500 28:200 29:5000 30:0 31:1500 34:0 35:0.25 38:30 39:4 64:1 65:0.2 66:80 71:1 72:2 73:55 74:30 32:3.5 104:-6.4" :
  p == 25 ? "1:4 5:4 11:0 17:0 18:400 19:60 21:0 22:90 24:5000 25:8000 26:100 27:3000 28:4000 29:8000 30:100 31:3000 41:0 42:0.08 45:40 46:2 34:4 35:8 38:12 39:2 64:1 65:0.1 66:70 32:-4.5 104:-10.2" :
  p == 26 ? "1:2 5:0 6:24 11:30 15:-100 16:0 13:0.5 14:180 4:30 17:1 18:3000 19:60 22:60 24:0.5 25:200 26:0 27:150 28:0.5 29:350 30:0 31:200 71:1 72:6 73:45 74:30 32:3.5 104:-5.0" :
  p == 27 ? "1:2 5:3 6:12 7:0 11:30 17:0 18:400 19:75 22:65 24:0.5 25:280 26:0 27:250 23:40 28:0.5 29:500 30:0 31:400 49:8 71:1 72:4 73:35 74:25 32:-2.5 104:-5.4" :
  p == 28 ? "1:3 5:2 6:-12 7:0 11:50 12:20 17:1 18:1500 19:20 20:40 22:25 25:500 26:60 49:8 50:4 51:30 52:80 53:120 48:2 41:0 42:0.5 45:30 46:6 34:0 35:4.5 38:4 39:0 67:1 68:0 69:0.7 70:60 32:-1.5 104:-4.6" :
  p == 29 ? "1:3 5:3 6:12 7:0 11:25 12:50 17:1 18:1200 19:25 22:45 24:0.5 25:300 26:20 27:120 28:0.5 29:400 30:70 31:100 48:2 49:1 61:1 62:1 63:55 3:30 2:1 32:1 104:-8.8" :
  p == 30 ? "1:2 5:1 6:12 7:0 11:30 17:0 18:3000 19:20 22:30 25:900 26:30 28:2 29:1200 30:40 31:600 49:8 64:1 65:0.35 66:75 71:1 72:4 73:30 74:20 32:2.5 104:-4.9" :
  p == 31 ? "1:2 5:2 6:7 7:4 11:45 17:1 18:3800 19:18 22:20 25:500 26:60 28:2 31:300 48:2 49:1 53:60 34:0 35:5.6 38:6 39:0 71:1 72:5 73:30 74:20 32:2 104:-7.5" :
  p == 32 ? "1:2 5:2 6:-12 7:7 11:50 3:50 2:0 17:1 18:180 19:40 20:35 21:0 22:15 24:2000 25:5000 26:60 27:3000 28:3000 29:5000 30:100 31:4000 34:0 35:0.07 38:25 39:2 41:1 42:0.15 45:10 46:1 49:4 64:1 65:0.07 66:60 71:1 72:0 73:45 74:20 32:-0.5 104:-12.6" :
  p == 33 ? "1:2 5:2 6:0 7:10 11:50 17:1 18:1800 19:15 22:20 24:900 25:2500 26:60 27:2500 28:800 29:1500 30:85 31:2500 49:8 34:1 35:0.6 38:45 39:2 85:50 86:4 87:1 41:0 42:0.15 45:60 46:12 67:1 69:0.4 70:50 71:1 72:2 73:40 74:20 104:-8.9" :
  p == 34 ? "1:3 5:3 6:12 7:0 11:25 12:40 17:0 18:2600 19:5 22:25 24:20 25:800 26:40 28:25 29:500 30:90 31:400 48:2 49:1 53:80 34:0 35:5.4 38:30 39:0 88:700 41:1 42:0.3 45:25 46:6 71:1 72:4 73:40 74:25 104:-12.5" :
  p == 35 ? "1:2 5:3 6:-12 7:5 11:40 17:1 18:300 19:55 20:30 22:30 24:0.5 25:250 26:30 27:120 28:0.5 29:400 30:80 31:100 48:2 49:1 53:20 34:7 36:1 37:9 38:65 39:2 85:30 86:7 104:-7.0" :
  p == 36 ? "1:2 5:2 7:12 11:50 17:1 18:3000 19:10 22:10 24:300 25:900 26:80 28:200 29:600 30:90 31:500 49:8 41:7 43:1 44:9 45:100 46:3 67:1 70:40 71:1 72:5 73:35 74:20 104:-10.0" :
  p == 37 ? "1:3 5:3 6:0 7:10 11:50 12:35 17:0 18:3500 19:8 22:10 24:300 25:900 26:80 27:900 28:350 29:600 30:90 31:900 34:0 35:0.7 38:55 39:6 41:6 42:0.4 45:12 46:0 91:1 89:30 90:2 93:0 94:0.08 97:70 98:14 67:1 69:0.5 70:60 49:8 104:-15.3" :
  p == 38 ? "1:0 5:0 6:0 7:3 11:0 4:28 15:35 16:1 13:2 14:900 17:0 18:9000 19:0 28:3 29:2200 30:25 31:700 33:70 49:12 34:0 35:4.5 38:50 39:3 87:1 85:70 86:4 88:300 104:-4.2" :
  p == 39 ? "1:2 5:2 7:7 11:50 17:1 18:900 19:45 22:30 25:600 26:50 28:2 31:400 49:8 34:4 36:2 37:7 38:60 39:2 85:40 86:9 41:6 42:0.3 45:40 46:4 71:1 72:4 73:45 74:25 104:-9.2" :
  p == 40 ? "1:2 5:3 6:-12 7:0 11:50 17:1 18:200 19:40 20:40 22:0 28:0.5 30:100 31:120 48:2 49:1 53:30 34:0 36:2 37:7 38:80 39:2 41:2 43:1 44:2 45:80 46:14 3:40 104:-8.7" :
  "";
);
// a preset's STEP patterns: "LFO number, then 16 steps -100 .. 100" for each LFO that has one
function dh_psteps(p) (
  p == 35 ? "1 100 -60 20 -100 80 -100 40 -20 100 -80 0 -100 60 -40 90 -100" :
  p == 36 ? "2 100 100 -100 100 -100 100 100 -100 100 -100 100 100 -100 100 100 -100" :
  "";
);
function load_preset(p) local(n, i, l, k) (
  dh_panic = 1;                                     // (first: the audio thread holds the old sound until it has faded)
  i = 0; loop(75, slider(i + 1) = DEFM[i]; i += 1; );
  i = 84; loop(20, slider(i + 1) = DEFM[i]; i += 1; );
  n = dh_nums(dh_pstr(p), DTMP);
  i = 0; loop(floor(n / 2), DTMP[i * 2] >= 1 && (DTMP[i * 2] <= 75 || (DTMP[i * 2] >= 85 && DTMP[i * 2] <= 104)) ? slider(DTMP[i * 2]) = DTMP[i * 2 + 1]; i += 1; );
  dh_stepdef(0); dh_stepdef(1); dh_stepdef(2);
  n = dh_nums(dh_psteps(p), DTMP);
  i = 0; loop(floor(n / 17), l = DTMP[i * 17] - 1;
    l >= 0 && l < 3 ? ( k = 0; loop(16, DSTEP[l * 16 + k] = min(max(DTMP[i * 17 + 1 + k] / 100, -1), 1); k += 1; ); ); i += 1; );
  i = 1; loop(75, ui_auto(i); i += 1; );
  i = 85; loop(20, ui_auto(i); i += 1; );
  pr_cur = p; dh_panic = 1;
);

// ---------- the LFOs ----------
// DLF + 16 l (l = LFO 0-2): 0 shape  1 speed (Hz, per cycle)  2 timing (0 free, 1 sync, 2 flow)  3 beats per cycle
//   4 depth A (-1 .. 1)  5 destination A  6 KEY  7 depth B  8 destination B  9 VOICE  10 fade step per tick (1 = none)
//   11 phase step per tick  12 rounding coefficient (2 ms)  13 depth A with the wheel  14 in use  15 the mode last tick
// DLG + 12 l (the global LFO): 0 phase  1 held value  2 previous held value  3 output  4 fade level  5 speed
//   multiplier and 6 depth multiplier (from the other LFOs)  7 drift speed factor  8 / 9 the next 5 / 6 (gathering)
//   10 / 11 depth A / B, glided 30 ms (a depth knob never steps; a GLOBAL / VOICE change dips to 0 and comes back)
// A STEP pattern's 16 steps make one cycle, so RATE is the step rate.
function dh_lfov(l, sh, ph, hv, pv) (
  sh == 0 ? sin(2 * $pi * ph) : sh == 1 ? 1 - 4 * abs(ph - 0.5) : sh == 2 ? 2 * ph - 1 : sh == 3 ? (ph < 0.5 ? 1 : -1) :
  sh == 4 ? hv : sh == 5 ? 1 - 2 * ph : sh == 6 ? pv + (hv - pv) * (0.5 - 0.5 * cos($pi * ph)) :
  DSTEP[l * 16 + min(floor(ph * 16), 15)];
);
function dh_fadec(x) ( x * x * (3 - 2 * x); );       // the fade's curve (eases in and out)
// adds one LFO's contribution (v -1 .. 1, depth a -1 .. 1) to the modulation sums
function dh_lfo_to(v, d, a) (
  d == 0 ? dh_pmod += v * 12 * a * abs(a) :
  d == 1 ? dh_l2p += v * 24 * a * abs(a) :
  d == 2 ? dh_loct += v * 4 * a :
  d == 3 ? dh_trem *= 1 - abs(a) * (0.5 - 0.5 * v * sign(a)) :
  d == 4 ? dh_lpan += v * a :
  d == 5 ? dh_lfm += v * a * 0.75 :
  d == 6 ? dh_lpw += v * a * 0.45 :
  d == 7 ? dh_lres += v * a * 0.5 :
  d == 8 ? dh_lmix += v * a * 0.5 :
  d == 9 ? dh_lring += v * a * 0.5 :
  d == 10 ? dh_lsub += v * a * 0.8 :
  d == 11 ? dh_ludet += v * a :
  d == 12 ? dh_lcho += v * a * 0.5 :
  d == 13 ? dh_ldly += v * a * 0.5 :
  d < 17 ? DLG[(d - 14) * 12 + 8] *= 2 ^ (v * a * 2) :           // another LFO's speed: up to 2 octaves either way
           DLG[(d - 17) * 12 + 9] *= 1 - abs(a) * (0.5 - 0.5 * v * sign(a));
);
// the global LFOs, one control tick on
function dh_lfo_tick() local(l, b, g, ph) (
  l = 0;
  loop(3,
    b = DLF + l * 16; g = DLG + l * 12;
    ph = g[0] + b[11] * (b[2] == 1 ? 1 : g[5] * g[7]);
    ph >= 1 ? ( ph -= floor(ph); g[2] = g[1]; g[1] = rand(2) - 1; );
    g[0] = ph;
    b[14] || g[10] != 0 || g[11] != 0 ? (           // (an LFO with no depth only keeps time)
      g[3] += (dh_lfov(l, b[0], ph, g[1], g[2]) - g[3]) * b[12];
      g[4] = min(g[4] + b[10], 1);
    );
    l += 1;
  );
);
// their contributions: GLOBAL LFOs to everything, VOICE LFOs only to the whole-synth destinations (chorus, delay,
// the other LFOs); the per-note ones are added voice by voice (dh_vlfo)
function dh_lfo_all() local(l, b, g, v) (
  DLG[8] = DLG[9] = DLG[20] = DLG[21] = DLG[32] = DLG[33] = 1;
  dh_anyv = 0;
  l = 0;
  loop(3,
    b = DLF + l * 16; g = DLG + l * 12;
    b[13] = b[4]; l == 0 && dh_mwd == 2 ? b[13] += dh_mw * ((b[4] < 0 ? -1 : 1) - b[4]);   // WHEEL: LFO 1 depth A
    b[9] != b[15] ? ( g[10] = g[11] = 0; b[15] = b[9]; );
    g[10] += (b[13] - g[10]) * dh_dk; abs(g[10]) < 0.00001 && b[13] == 0 ? g[10] = 0;
    g[11] += (b[7] - g[11]) * dh_dk; abs(g[11]) < 0.00001 && b[7] == 0 ? g[11] = 0;
    b[9] && (g[10] != 0 || g[11] != 0) ? dh_anyv = 1;
    g[10] != 0 || g[11] != 0 ? (
      v = g[3] * dh_fadec(g[4]) * g[6];
      b[9] ? (
        b[5] >= 12 && g[10] != 0 ? dh_lfo_to(v, b[5], g[10]); b[8] >= 12 && g[11] != 0 ? dh_lfo_to(v, b[8], g[11]);
      ) : (
        g[10] != 0 ? dh_lfo_to(v, b[5], g[10]); g[11] != 0 ? dh_lfo_to(v, b[8], g[11]);
      );
    );
    l += 1;
  );
  DLG[5] = DLG[8]; DLG[6] = DLG[9]; DLG[17] = DLG[20]; DLG[18] = DLG[21]; DLG[29] = DLG[32]; DLG[30] = DLG[33];
);
// a voice's own LFOs (VOICE mode), one control tick on, added to the sums
function dh_vlfo(p) local(l, b, q, ph, v) (
  l = 0;
  loop(3,
    b = DLF + l * 16;
    b[9] && (DLG[l * 12 + 10] != 0 || DLG[l * 12 + 11] != 0) ? (
      q = p + 44 + l * 5;
      ph = q[0] + b[11] * (b[2] == 1 ? 1 : DLG[l * 12 + 5] * p[59 + l]);
      ph >= 1 ? ( ph -= floor(ph); q[2] = q[1]; q[1] = rand(2) - 1; );
      q[0] = ph;
      q[3] += (dh_lfov(l, b[0], ph, q[1], q[2]) - q[3]) * b[12];
      q[4] = min(q[4] + b[10], 1);
      v = q[3] * dh_fadec(q[4]) * DLG[l * 12 + 6];
      b[5] < 12 ? dh_lfo_to(v, b[5], DLG[l * 12 + 10]);
      b[8] < 12 ? dh_lfo_to(v, b[8], DLG[l * 12 + 11]);
    );
    l += 1;
  );
);
// a new note's own LFOs: KEY on starts them at the top of the cycle (DRIFT nudges each a touch), off at a random
// point (SYNC: in step with the global one); DRIFT gives each note's LFO its own slightly different speed
function dh_vlfo_start(p) local(l, b, q) (
  l = 0;
  loop(3,
    b = DLF + l * 16; q = p + 44 + l * 5;
    q[0] = b[6] ? rand(1) * 0.02 * dh_drift : b[2] == 1 ? DLG[l * 12] : rand(1);
    q[1] = rand(2) - 1; q[2] = rand(2) - 1;
    q[3] = dh_lfov(l, b[0], q[0], q[1], q[2]);
    q[4] = b[10] >= 1 ? 1 : 0;
    p[59 + l] = b[2] == 1 ? 1 : 1 + (rand(2) - 1) * 0.06 * dh_drift;
    l += 1;
  );
);

// ---------- voices ----------
function dh_vrel(p) ( p[1] = 0; p[3] = 0; p[13] > 0 ? p[13] = 3; p[15] > 0 ? p[15] = 3; );
function dh_vstart(p, n, v, retrig, udet, upan, from) (
  p[13] == 0 ? (
    // a fresh voice: free-running phases (random, so unison and chords never line up), clean filter
    p[10] = rand(1); p[11] = rand(1); p[12] = rand(1); p[23] = 0; p[33] = 0;
    p[19] = p[20] = p[21] = p[22] = 0; p[14] = 0; p[16] = 0; p[18] = 0;
    p[4] = from;                                     // PORTAMENTO glides in from the last note played
  );
  dh_pc >= 1 ? p[4] = n;                             // no portamento: straight to the note
  p[0] = n; p[1] = 1; p[3] = 0; p[5] = n; p[6] = v / 127;
  p[8] = udet; p[9] = upan;
  retrig || p[13] == 0 || p[13] == 3 ? (
    p[7] = log(ins_note_detune()) / log(2) * 12;
    p[13] = 1; p[15] = 1; p[17] = 1;
    dh_anyv ? dh_vlfo_start(p);
  );
  p[34] = dh_gain * (1 - dh_avel + dh_avel * p[6]) * dh_uscl;
  p[38] = 1;
);
function dh_alloc() local(i, p, best, ba) (
  best = -1;
  i = 0; loop(dh_nv, p = DV + i * 64; p[13] == 0 && (best < 0 || p[2] < ba) ? ( best = i; ba = p[2]; ); i += 1; );
  best < 0 ? ( i = 0; loop(dh_nv, p = DV + i * 64; !p[1] && (best < 0 || p[2] < ba) ? ( best = i; ba = p[2]; ); i += 1; ); );
  best < 0 ? ( i = 0; loop(dh_nv, p = DV + i * 64; best < 0 || p[2] < ba ? ( best = i; ba = p[2]; ); i += 1; ); );
  DV + best * 64;
);
function dh_upos(k) ( dh_un > 1 ? k / (dh_un - 1) * 2 - 1 : 0; );
function dh_poly_on(n, v) local(i, p, k, from) (
  i = 0; loop(16, p = DV + i * 64; p[0] == n && p[1] ? dh_vrel(p); i += 1; );   // the same key again: a fresh note
  from = dh_last >= 0 && dh_porta > 0 ? dh_last : n;
  k = 0;
  loop(dh_un,
    p = dh_alloc();
    dh_age += 1; p[2] = dh_age;
    dh_vstart(p, n, v, 1, dh_upos(k) * dh_udet, dh_upos(k) * dh_uspr, from);
    k += 1;
  );
);
function dh_poly_off(n) local(i, p) (
  i = 0; loop(16, p = DV + i * 64; p[0] == n && p[1] ? ( dh_pedal ? p[3] = 1 : dh_vrel(p); ); i += 1; );
);
function dh_mono_go(n, v, retrig) local(k, p) (
  k = 0;
  loop(dh_un,
    p = DV + k * 64;
    dh_age += 1; p[2] = dh_age;
    dh_vstart(p, n, v, retrig, dh_upos(k) * dh_udet, dh_upos(k) * dh_uspr, p[13] > 0 ? p[4] : (dh_last >= 0 && dh_porta > 0 ? dh_last : n));
    k += 1;
  );
);
function dh_mono_on(n, v) local(i, f, held) (
  held = dh_hc > 0;
  f = -1; i = 0; loop(dh_hc, DHN[i] == n ? f = i; i += 1; );
  f >= 0 ? ( i = f; loop(dh_hc - f - 1, DHN[i] = DHN[i + 1]; i += 1; ); dh_hc -= 1; );
  dh_hc < 127 ? ( DHN[dh_hc] = n; dh_hc += 1; );
  dh_mono_go(n, v, !(held && dh_mode == 2));
);
function dh_mono_off(n) local(i, f, k, p) (
  f = -1; i = 0; loop(dh_hc, DHN[i] == n ? f = i; i += 1; );
  f >= 0 ? (
    i = f; loop(dh_hc - f - 1, DHN[i] = DHN[i + 1]; i += 1; ); dh_hc -= 1;
    dh_hc > 0 ? (
      // back to the note still held, without a new attack
      k = 0; loop(dh_un, p = DV + k * 64; p[0] = DHN[dh_hc - 1]; p[5] = p[0]; dh_pc >= 1 ? p[4] = p[0]; k += 1; );
    ) : (
      k = 0; loop(dh_un, p = DV + k * 64; dh_pedal ? p[3] = 1 : dh_vrel(p); k += 1; );
    );
  );
);
function dh_note_on(n, v) local(i, held) (
  ins_midi_hit();
  held = 0; i = 0; loop(16, DV[i * 64 + 1] ? held = 1; i += 1; );
  i = 0;
  loop(3,
    (dh_mode == 0 || dh_hc == 0 || dh_mode == 1) && DLF[i * 16 + 6] ? DLG[i * 12] = 0;   // KEY: restart
    !held && DLF[i * 16 + 10] < 1 ? DLG[i * 12 + 4] = 0;                                  // FADE: from the first key
    i += 1;
  );
  dh_mode ? dh_mono_on(n, v) : dh_poly_on(n, v);
  dh_last = n;
);
function dh_note_off(n) ( dh_mode ? dh_mono_off(n) : dh_poly_off(n); );
function dh_all_off() local(i, p) (
  i = 0; loop(16, p = DV + i * 64; p[1] ? dh_vrel(p); p[0] = -1; i += 1; );
  dh_hc = 0;
);
function dh_pedal_up() local(i, p) ( i = 0; loop(16, p = DV + i * 64; p[3] ? dh_vrel(p); i += 1; ); );
function dh_qpush(ofs, t, n, v) local(i) (
  dh_qc < 300 ? (
    i = dh_qc; while (i > 0 && DQO[i - 1] > ofs) ( DQO[i] = DQO[i - 1]; DQT[i] = DQT[i - 1]; DQN[i] = DQN[i - 1]; DQV[i] = DQV[i - 1]; i -= 1; );
    DQO[i] = ofs; DQT[i] = t; DQN[i] = n; DQV[i] = v; dh_qc += 1;
  );
);

// ---------- names, rates ----------
function dh_lfo_beats(r) ( r == 0 ? 16 : r == 1 ? 8 : r == 2 ? 4 : r == 3 ? 2 : r == 4 ? 4 / 3 : r == 5 ? 1 : r == 6 ? 2 / 3 :
                          r == 7 ? 0.5 : r == 8 ? 1 / 3 : r == 9 ? 0.25 : r == 10 ? 1 / 6 : 0.125; );
function dh_arp_beats(r) ( r == 0 ? 1 : r == 1 ? 2 / 3 : r == 2 ? 0.5 : r == 3 ? 1 / 3 : r == 4 ? 0.25 : r == 5 ? 1 / 6 : 0.125; );
function dh_dly_beats(r) ( r == 0 ? 2 : r == 1 ? 1.5 : r == 2 ? 1 : r == 3 ? 2 / 3 : r == 4 ? 0.75 : r == 5 ? 0.5 : r == 6 ? 1 / 3 : 0.25; );

// one LFO's settings (sliders a .. a + 6: shape, rate, timing, synced rate, depth A, destination A, KEY;
// c .. c + 3: depth B, destination B, mode, fade)
function dh_lfo_set(l, a, c) local(b, r) (
  b = DLF + l * 16;
  b[0] = floor(slider(a) + 0.5); b[2] = floor(slider(a + 2) + 0.5);
  b[3] = dh_lfo_beats(floor(slider(a + 3) + 0.5)) * (b[0] == 7 ? 16 : 1);
  r = b[2] ? dh_tmp / 60 / b[3] : slider(a + 1) / (b[0] == 7 ? 16 : 1);
  b[1] = r; b[11] = r * 4 / srate;
  b[4] = slider(a + 4) / 100; b[5] = floor(slider(a + 5) + 0.5); b[6] = slider(a + 6) >= 0.5;
  b[7] = slider(c) / 100; b[8] = floor(slider(c + 1) + 0.5); b[9] = slider(c + 2) >= 0.5;
  b[10] = slider(c + 3) > 0 ? 4 / (slider(c + 3) * 0.001 * srate) : 1;
  b[12] = 1 - exp(-4 / (0.002 * srate));
  dh_dk = 1 - exp(-4 / (0.03 * srate));
  b[14] = b[4] != 0 || b[7] != 0 || (l == 0 && dh_mwd == 2);
  b[9] && b[14] ? dh_anyv = 1;                     // (dh_lfo_all keeps it up to date while depths glide)
);

// ---------- parameters ----------
function dh_c4(c) ( 1 - (1 - c) ^ 4; );
function update() local(r, g) (
  dh_w1 = slider1; dh_subw = slider2; dh_sub = slider3 / 100 * 0.8;
  dh_fm = sqr(slider4 / 100) * 1.5;
  dh_w2 = slider5; dh_o2st = slider6 + slider7 / 100; dh_trk = slider8; dh_sync = slider9;
  dh_ring = slider10 / 100; r = slider11 / 100; dh_mixr = r;
  dh_m1 = (1 - r) * (1 - dh_ring); dh_m2 = r * (1 - dh_ring);
  dh_pw = slider12 / 100;
  dh_ma4 = dh_c4(vo_coef(slider13)); dh_md4 = dh_c4(vo_coef(slider14)); dh_mamt = slider15 / 100; dh_mdst = slider16;
  dh_ft = slider17; dh_cut = slider18; dh_res = slider19 / 100;
  fl_lc = log(dh_cut);                               // the cutoff in log Hz: the target of the per-sample glide
  dh_k = 2 - 1.985 * fl_rs;                          // state-variable damping (12 dB types), from the glided RESO
  dh_lk = fl_rs * 4.15;                              // ladder feedback (24 dB): self-oscillates near the top
  dh_sat = slider20 / 100; dh_sg = 1 + dh_sat * 5; dh_sn = 1 / (1 + dh_sat * 1.6);
  dh_key = slider21 / 100; dh_fenv = slider22 / 100; dh_fvel = slider23 / 100;
  dh_fa4 = dh_c4(vo_coef(slider24)); dh_fd4 = dh_c4(vo_coef(slider25)); dh_fs = slider26 / 100; dh_fr4 = dh_c4(vo_coef(slider27));
  dh_aa = vo_coef(slider28); dh_ad = vo_coef(slider29); dh_as = slider30 / 100; dh_ar = vo_coef(slider31);
  dh_gain = 10 ^ (slider32 / 20) * 0.9; dh_avel = slider33 / 100;
  dh_tmp = tempo > 0 ? tempo : 120;
  dh_mode = slider48; dh_nv = max(1, min(16, floor(slider49 + 0.5)));
  dh_un = min(max(1, floor(slider50 + 0.5)), dh_nv); dh_uscl = 1 / sqrt(dh_un);
  dh_udet = slider51 / 100; dh_uspr = slider52 / 100;
  dh_porta = slider53; dh_pc = slider53 > 0 ? dh_c4(vo_coef(slider53)) : 1;
  dh_brng = slider54; dh_mwd = slider55;
  dh_anyv = 0; dh_lfo_set(0, 34, 85); dh_lfo_set(1, 41, 89); dh_lfo_set(2, 93, 100);
  dh_dist = slider61; dh_dtyp = slider62; dh_damt = slider63 / 100;
  dh_dg = 1 + 30 * dh_damt * dh_damt; dh_dl = 1 / (1 + 0.9 * sqrt(dh_damt * 3)); dh_dt0 = vo_tanh(0.12 * dh_damt);
  dh_bits = 2 ^ (12 - 9 * dh_damt); dh_hr = 1 / (1 + 15 * dh_damt * dh_damt);
  dh_phs = slider64; dh_phr = slider65; dh_phd = slider66 / 100;
  dh_cho = slider67; dh_chf = slider68; dh_chr = slider69; dh_chd = slider70 / 100;
  dh_dly = slider71; dh_dlb = dh_dly_beats(slider72); dh_dfb = slider73 / 100; dh_dmx = slider74 / 100; dh_dpp = slider75;
  dh_dlt = min(dh_dlb * 60 / dh_tmp * srate, DDLM - 4);
  dh_drift = slider76 / 100; dh_volt = 10 ^ ((slider77 + slider104) / 20); dh_chan = slider78;   // VOLUME + the preset level
  dh_fmax = srate * 0.45; dh_pis = $pi / srate; dh_440s = 440 / srate;
  ins_output_setup(79);   // output stage: CLIP, always on
);

// DIST (drive): vo_tanh anti-aliased, first-order ADAA on its antiderivative (as gfx-clip.jsfx-inc) with half the
// step of the plain curve added back (no droop, no delay), behind the house drive-tracking pre-filter (gfx-clip:
// 40 kHz to 6 kHz and the lows down to -1.5 dB as the amount rises, squared). rd: start afresh (DIST was off).
function dh_daa(z) instance(z1, ad1, cv1, rd) local(q, ad, cv, d, y) (
  q = z * z;
  q < 9 ? ( ad = q * 0.05555555555555556 + 1.3333333333333333 * log(1 + q * 0.3333333333333333); cv = z * (27 + q) / (27 + 9 * q); )
        : ( ad = abs(z) - 0.6516075; cv = sign(z); );
  rd ? ( z1 = z; ad1 = ad; cv1 = cv; rd = 0; );
  d = z - z1;
  y = abs(d) > 0.00001 ? (ad - ad1) / d : vo_tanh(0.5 * (z + z1));
  y += 0.5 * (cv - cv1);
  z1 = z; ad1 = ad; cv1 = cv;
  y;
);
dh_dzl.rd = dh_dzr.rd = 1;
dh_pfl.clp_pf_cfg(40000, 6000, -1.5, 150); dh_pfr.clp_pf_cfg(40000, 6000, -1.5, 150);
dh_pfl.clp_pf_reset(); dh_pfr.clp_pf_reset();

ins_drift_init();
fl_ok = 0;
dh_bend = 0; dh_mw = 0; dh_pedal = 0; dh_hc = 0; dh_age = 0; dh_last = -1; dh_qc = 0;
dh_wasarp = 0; dh_lastmode = -1; dh_ct = 0;
dh_i = 0; loop(3, dh_zg = DLG + dh_i * 12; memset(dh_zg, 0, 12); dh_zg[1] = rand(2) - 1; dh_zg[2] = rand(2) - 1;
                  dh_zg[4] = dh_zg[5] = dh_zg[6] = dh_zg[7] = 1; dh_i += 1; );
dh_lw0.drift_init(0.8 + rand(1) * 0.5); dh_lw1.drift_init(0.8 + rand(1) * 0.5); dh_lw2.drift_init(0.8 + rand(1) * 0.5);
dh_gtrem = 1; dh_chdm = slider70 / 100; dh_dmxm = slider74 / 100;
// (what a voice starting before the first control tick reads)
dh_grg = slider10 / 100; dh_gm1 = (1 - slider11 / 100) * (1 - dh_grg); dh_gm2 = slider11 / 100 * (1 - dh_grg);
dh_gsub = slider3 / 100 * 0.8; dh_gk = 2 - 1.985 * slider19 / 100; dh_glk = slider19 / 100 * 4.15;
dh_dcxl = dh_dcyl = dh_dcxr = dh_dcyr = 0; dh_vol = 1; dh_wp = 0; dh_cwp = 0; dh_dts = -1;
dh_wasdly = 0; dh_wascho = 0; dh_phph = 0; dh_chph = 0; dh_vph = 0; dh_hold = 0; dh_holdr = 0; dh_hcnt = 0;
// everything that depends on the sample rate (run again from @block if the rate changes: ext_noinit keeps @init
// from running then)
function dh_srinit() ( dh_sr = srate; dh_sm = 1 - exp(-1 / (0.03 * srate)); ost_init(20000); dh_dts = -1; );
dh_srinit();
!ui_dflt ? ( ui_sz = 2; pr_cur = 1; );            // a new instance opens Large, on Init
update();
dh_lastmode = dh_mode; dh_wasarp = slider56;     // (so the first block does not queue an all-off over its own notes)
dh_pcf = 0; dh_pck = dh_pcg = 1;
// a preset loaded while voices sound: the old sound fades out (12 ms), the new preset applies at the silent
// point (where the released notes end) and the output fades back in (12 ms): no click
// notes played during the fade wait for its silent point (they start with the new sound)
DPCP = 43300; dh_pcn = 0;                                    // (32 pairs: note, velocity)
function dh_pchold(n, v) ( dh_pcn < 32 ? ( DPCP[dh_pcn * 2] = n; DPCP[dh_pcn * 2 + 1] = v; dh_pcn += 1; ); );
function dh_pcunhold(n) local(i, j) (
  i = j = 0; loop(dh_pcn, DPCP[i * 2] != n ? ( DPCP[j * 2] = DPCP[i * 2]; DPCP[j * 2 + 1] = DPCP[i * 2 + 1]; j += 1; ); i += 1; ); dh_pcn = j;
);
function dh_pcapply() local(i, p) (
  i = 0; loop(16, p = DV + i * 64; p[13] == 3 ? ( p[13] = 0; p[15] = 0; p[14] = 0; p[17] = 0; p[0] = -1; p[19] = p[20] = p[21] = p[22] = 0; ); i += 1; );   // the released notes end here
  update(); dh_lastmode = dh_mode; dh_pcf = 0;
  i = 0; loop(dh_pcn, dh_note_on(DPCP[i * 2], DPCP[i * 2 + 1]); i += 1; ); dh_pcn = 0;
  dh_dly && !dh_wasdly ? ( memset(DDLL, 0, 524288 * 2); dh_dts = -1; ); dh_wasdly = dh_dly;
  dh_cho && !dh_wascho ? memset(DCHL, 0, 8192 * 2); dh_wascho = dh_cho;
  dh_dts = dh_dlt;                                  // (the echo time moves here, while it is silent, instead of gliding)
);
dh_vol = dh_volt;
ui_dflt = 1;

// PRESET parameter (JG, 28 Oct 2026: accessibility - a screen reader reaches the presets through REAPER's parameter list).
// A change of prs_sel (a screen reader, the host, automation) loads that preset (0 = No preset: nothing loaded); the preset bar asks through
// prs_dirty. Both load here, on the audio side only. prs_last is saved, so opening a project or recalling its
// state never reloads a preset over the saved settings.
function prs_chk() local(k) (
  prs_dirty > 0 ? (
    k = prs_dirty; prs_dirty = 0; load_preset(k);
    k <= 40 ? ( prs_sel = k; slider_automate(prs_sel); prs_cur_d = k; );
    prs_last = prs_sel;
  ) : prs_ok ? (
    prs_sel = max(0, min(40, prs_sel | 0));
    prs_sel != prs_last ? ( prs_last = prs_sel; prs_sel > 0 ? ( load_preset(prs_sel); prs_cur_d = prs_sel; ); );
  ) : ( prs_last = prs_sel | 0; prs_ok = 1; );
);
prs_ok = 0; prs_dirty = 0;

@slider
prs_chk();
!dh_panic && !dh_pcf ? update();

@block
prs_chk();
srate != dh_sr ? dh_srinit();
dh_pcr = 1 / (0.012 * srate);
// the filter glide: two 15 ms stages on the cutoff, 15 ms on RESO and ENV (starts on its targets)
fl_k = 1 - exp(-1 / (0.015 * srate));
!fl_ok ? ( fl_c1 = fl_c2 = log(slider18); fl_rs = slider19 / 100; fl_ev = slider22 / 100; fl_ok = 1; );
dh_panic && !dh_pcf ? ( dh_i = 0; loop(16, DV[dh_i * 64 + 13] > 0 ? dh_pcf = 1; dh_i += 1; ); );   // sounding: fade first
!dh_pcf ? update();
arp_params(slider57, dh_arp_beats(slider59), slider58, slider60 / 100, 0, 16, 0, 1, 0, 0, 0, 100, 0);
dh_pfl.clp_pf_block(dh_damt * dh_damt, srate, samplesblock); dh_pfr.clp_pf_block(dh_damt * dh_damt, srate, samplesblock);

// MIDI in: notes go to the arp when it is on, otherwise into the queue (sample-accurate)
dh_qc = 0;
while (midirecv(mofs, m1, m2, m3)) (
  ins_chan_ok(m1, dh_chan) ? (
    dh_st = m1 & 0xF0;
    dh_st == 0x90 || dh_st == 0x80 ? (
      slider56 ? arp_key(m2, m3, 0, dh_st == 0x90 && m3 > 0) : dh_qpush(mofs, dh_st == 0x90 && m3 > 0, m2, m3);
      dh_st == 0x90 && m3 > 0 ? ins_midi_hit();
    ) :
    dh_st == 0xE0 ? dh_bend = (((m3 * 128 + m2) - 8192) / 8192) * dh_brng :
    dh_st == 0xB0 ? (
      m2 == 1 ? dh_mw = m3 / 127 :
      m2 == 64 ? ( dh_pedal = m3 >= 64; !dh_pedal ? dh_qpush(mofs, 2, 0, 0); ) :
      m2 == 123 || m2 == 120 ? dh_qpush(mofs, 3, 0, 0);
    );
  );
  midisend(mofs, m1, m2, m3);                       // pass MIDI through
);
// transport stop: release whatever is still held (as an all-notes-off would; a safety net if the stop's note-offs go missing)
gfx_pst = play_state == 1 || play_state == 5;
gfx_pst_prev && !gfx_pst ? ( dh_qpush(0, 3, 0, 0); );
gfx_pst_prev = gfx_pst;
!slider56 && dh_wasarp ? ( arp_outn = 0; arp_flush(); arp_sn = 0; arp_pn = 0; dh_qpush(0, 3, 0, 0); );
slider56 && !dh_wasarp ? dh_qpush(0, 3, 0, 0);
dh_wasarp = slider56;
dh_mode != dh_lastmode ? ( dh_qpush(0, 3, 0, 0); dh_lastmode = dh_mode; );
slider56 ? (
  arp_block(play_state & 1, beat_position, dh_tmp, samplesblock);
  dh_i = 0; loop(arp_outn, dh_qpush(arp_out_ofs[dh_i], arp_out_on[dh_i], arp_out_note[dh_i], arp_out_vel[dh_i]); dh_i += 1; );
);
dh_panic ? ( dh_all_off(); dh_panic = 0; );
// fewer VOICES than are sounding: let the extra ones go
dh_i = dh_nv; loop(16 - dh_nv, dh_p = DV + dh_i * 64; dh_p[1] ? dh_vrel(dh_p); dh_i += 1; );
dh_eqi = 0; dh_spos = 0;

// SYNC LFOs follow the project's beat while it plays (unless KEY restarts them); free / FLOW ones drift in speed
play_state & 1 ? (
  dh_i = 0;
  loop(3,
    dh_zb = DLF + dh_i * 16; dh_zg = DLG + dh_i * 12;
    dh_zb[2] == 1 && !dh_zb[6] ? (
      dh_zp = beat_position / dh_zb[3]; dh_zp -= floor(dh_zp);
      dh_zp < dh_zg[0] - 0.5 ? ( dh_zg[2] = dh_zg[1]; dh_zg[1] = rand(2) - 1; );   // (a new S&H value on the wrap)
      dh_zg[0] = dh_zp;
    );
    dh_i += 1;
  );
);
dh_zp = 0.35 * samplesblock / srate;
DLG[7] = 1 + dh_lw0.drift_tick(dh_zp) * dh_drift * 0.08;
DLG[19] = 1 + dh_lw1.drift_tick(dh_zp) * dh_drift * 0.08;
DLG[31] = 1 + dh_lw2.drift_tick(dh_zp) * dh_drift * 0.08;
// effects switched on: start from clean lines
dh_dly && !dh_wasdly ? ( memset(DDLL, 0, 524288 * 2); dh_dts = -1; ); dh_wasdly = dh_dly;
dh_cho && !dh_wascho ? memset(DCHL, 0, 8192 * 2); dh_wascho = dh_cho;
dh_dts < 0 ? dh_dts = dh_dlt;

// effect feedback states left ringing near zero: flush them (no denormals)
abs(dh_pfl) + abs(dh_a1l) + abs(dh_a2l) + abs(dh_a3l) + abs(dh_a4l) < 0.0000000001 ? dh_pfl = dh_a1l = dh_a2l = dh_a3l = dh_a4l = 0;
abs(dh_pfr) + abs(dh_a1r) + abs(dh_a2r) + abs(dh_a3r) + abs(dh_a4r) < 0.0000000001 ? dh_pfr = dh_a1r = dh_a2r = dh_a3r = dh_a4r = 0;
abs(dh_dfl) + abs(dh_dfr) + abs(dh_tl) + abs(dh_tr) < 0.0000000001 ? dh_dfl = dh_dfr = dh_tl = dh_tr = 0;
abs(dh_dcyl) + abs(dh_dcyr) < 0.0000000001 ? dh_dcyl = dh_dcyr = 0;

ins_drift_block(dh_drift, samplesblock);
dh_dps = 12 * log(ins_pitch) / LN2;                 // drift as semitones

@serialize
file_var(0, ui_sz);
file_var(0, pr_cur);
file_avail(0) != 0 ? file_mem(0, DSTEP, 48);       // the STEP patterns
file_avail(0) != 0 ? file_var(0, dh_lsel);         // the LFO the panel shows
file_avail(0) != 0 ? file_var(0, prs_last) : prs_last = prs_sel | 0; prs_ok = 1;   // PRESET parameter: no reload on recall

@sample
while (dh_eqi < dh_qc && (DQO[dh_eqi] <= dh_spos || dh_spos >= samplesblock - 1)) (   // (an event stamped at / past the block end: handled at its last sample)
  dh_et = DQT[dh_eqi];
  dh_et == 1 ? ( dh_pcf ? dh_pchold(DQN[dh_eqi], DQV[dh_eqi]) : dh_note_on(DQN[dh_eqi], DQV[dh_eqi]); ) : dh_et == 0 ? ( dh_pcunhold(DQN[dh_eqi]); dh_note_off(DQN[dh_eqi]); ) : dh_et == 2 ? dh_pedal_up() : ( dh_pcn = 0; dh_all_off(); );
  dh_eqi += 1;
);
dh_spos += 1;
dh_pcf ? ( dh_pck -= dh_pcr; dh_pck <= 0 ? ( dh_pck = 0; dh_pcapply(); ); dh_pcg = 0.5 - 0.5 * cos($pi * dh_pck); ) :
dh_pck < 1 ? ( dh_pck = min(dh_pck + dh_pcr, 1); dh_pcg = 0.5 - 0.5 * cos($pi * dh_pck); );

// CUTOFF, RESO and ENV glide per sample, so a turned knob or automation sweeps smoothly (never in steps)
fl_c1 += (fl_lc - fl_c1) * fl_k; fl_c2 += (fl_c1 - fl_c2) * fl_k; fl_rs += (dh_res - fl_rs) * fl_k; fl_ev += (dh_fenv - fl_ev) * fl_k;
// ---------- control rate (every 4 samples): LFOs and the modulation sums ----------
dh_ct += 1;
dh_tick = dh_ct >= 4;
dh_tick ? (
  dh_ct = 0;
  dh_lfo_tick();
  dh_pmod = dh_bend + dh_dps; dh_l2p = ins_aux; dh_loct = ins_cut; dh_trem = 1; dh_lpan = 0; dh_lfm = 0; dh_lpw = 0;
  dh_lres = dh_lmix = dh_lring = dh_lsub = dh_ludet = dh_lcho = dh_ldly = 0;
  dh_lfo_all();
  dh_mwd == 1 ? ( dh_vph += 5.5 * 4 / srate; dh_vph -= floor(dh_vph); dh_pmod += sin(2 * $pi * dh_vph) * dh_mw * 0.5; ) :
  dh_mwd == 3 ? dh_loct += dh_mw * 3 :
  dh_mwd == 4 ? dh_lfm += dh_mw * 0.8;
  dh_gtrem = dh_trem;
  dh_chdm = min(max(dh_chd + dh_lcho, 0), 1); dh_dmxm = min(max(dh_dmx + dh_ldly, 0), 1);
  // (the sums every voice starts from: its own LFOs add to them)
  dh_gpmod = dh_pmod; dh_gl2p = dh_l2p; dh_gloct = dh_loct; dh_glpan = dh_lpan; dh_glfm = dh_lfm; dh_glpw = dh_lpw;
  dh_glres = dh_lres; dh_glmix = dh_lmix; dh_glring = dh_lring; dh_glsub = dh_lsub; dh_gludet = dh_ludet;
  dh_vr = min(max(dh_mixr + dh_lmix, 0), 1); dh_grg = min(max(dh_ring + dh_lring, 0), 1);
  dh_gm1 = (1 - dh_vr) * (1 - dh_grg); dh_gm2 = dh_vr * (1 - dh_grg); dh_gsub = min(max(dh_sub + dh_lsub, 0), 0.8);
  dh_vr = min(max(fl_rs + dh_lres, 0), 1); dh_gk = 2 - 1.985 * dh_vr; dh_glk = dh_vr * 4.15;
);

// ---------- the voices ----------
dh_L = dh_R = 0;
dh_p = DV;
loop(16,
  dh_p[13] > 0 ? (
    (dh_tick || dh_p[38]) ? (
      dh_p[38] = 0;
      dh_p[4] += (dh_p[5] - dh_p[4]) * dh_pc;
      // filter envelope and mod envelope (control rate)
      dh_e = dh_p[16];
      dh_p[15] == 1 ? ( dh_e += (1.3 - dh_e) * dh_fa4; dh_e >= 1 ? ( dh_e = 1; dh_p[15] = 2; ); ) :
      dh_p[15] == 2 ? dh_e += (dh_fs - dh_e) * dh_fd4 :
      dh_p[15] == 3 ? dh_e -= dh_e * dh_fr4;
      dh_p[16] = dh_e;
      dh_m = dh_p[18];
      dh_p[17] == 1 ? ( dh_m += (1.3 - dh_m) * dh_ma4; dh_m >= 1 ? ( dh_m = 1; dh_p[17] = 2; ); ) :
      dh_p[17] == 2 ? dh_m -= dh_m * dh_md4;
      dh_p[18] = dh_m; dh_m *= dh_mamt;
      // this voice's own LFOs (VOICE mode)
      // OSC mix, ring, sub, resonance and amp (with the LFOs; without VOICE LFOs, as worked out for all voices)
      dh_anyv ? (
        dh_pmod = dh_gpmod; dh_l2p = dh_gl2p; dh_loct = dh_gloct; dh_lpan = dh_glpan; dh_lfm = dh_glfm; dh_lpw = dh_glpw;
        dh_lres = dh_glres; dh_lmix = dh_glmix; dh_lring = dh_glring; dh_lsub = dh_glsub; dh_ludet = dh_gludet; dh_trem = 1;
        dh_vlfo(dh_p);
        dh_p[35] = dh_trem;
        dh_vr = min(max(dh_mixr + dh_lmix, 0), 1); dh_rg = min(max(dh_ring + dh_lring, 0), 1);
        dh_p[39] = (1 - dh_vr) * (1 - dh_rg); dh_p[40] = dh_vr * (1 - dh_rg); dh_p[41] = dh_rg;
        dh_p[42] = min(max(dh_sub + dh_lsub, 0), 0.8);
        dh_vr = min(max(fl_rs + dh_lres, 0), 1); dh_p[36] = 2 - 1.985 * dh_vr; dh_p[37] = dh_vr * 4.15;
      ) : (
        dh_p[35] = 1; dh_p[39] = dh_gm1; dh_p[40] = dh_gm2; dh_p[41] = dh_grg; dh_p[42] = dh_gsub;
        dh_p[36] = dh_gk; dh_p[37] = dh_glk;
      );
      // pitch
      dh_ud = dh_ludet != 0 ? dh_p[8] * max(1 + dh_ludet, 0) : dh_p[8];
      dh_b = dh_p[4] + dh_p[7] + dh_ud + dh_pmod;
      dh_p[24] = min(exp((dh_b - 69) * L12) * dh_440s, 0.45);
      dh_b2 = (dh_trk ? dh_b : 60 + dh_p[7] + dh_ud + dh_pmod) + dh_o2st + dh_l2p + (dh_mdst == 0 ? dh_m * 48 : 0);
      dh_p[25] = min(exp((dh_b2 - 69) * L12) * dh_440s, 0.45);
      // filter
      dh_v = dh_p[6];
      dh_o = fl_ev * dh_e * (1 - dh_fvel + dh_fvel * dh_v) * 7 + dh_key * (dh_p[4] - 60) / 12 + dh_fvel * (dh_v - 0.7) * 1.5 + dh_loct;
      dh_g = tan(min(max(exp(fl_c2 + dh_o * LN2), 16), dh_fmax) * dh_pis);
      dh_ft == 1 ? (
        dh_p[26] = dh_g / (1 + dh_g); dh_p[27] = 1 - dh_p[26]; dh_p[28] = sqr(sqr(dh_p[26]));
      ) : (
        dh_p[26] = 1 / (1 + dh_g * (dh_g + dh_p[36])); dh_p[27] = dh_g * dh_p[26]; dh_p[28] = dh_g * dh_p[27];
      );
      dh_p[29] = min(max(dh_pw + dh_lpw + (dh_mdst == 2 ? dh_m * 0.45 : 0), 0.03), 0.97);
      dh_p[30] = max(dh_fm + dh_lfm + (dh_mdst == 1 ? dh_m * 1.5 : 0), 0);
      dh_pn = min(max(dh_p[9] + dh_lpan, -1), 1);
      dh_p[31] = 1 - max(dh_pn, 0); dh_p[32] = 1 + min(dh_pn, 0);
    );

    // ----- OSC 1 (FM from OSC 2's last output) -----
    dh_i1 = dh_p[24]; dh_fmv = dh_p[30] * dh_p[33];
    dh_wr = 0;
    dh_w1 < 2 || dh_w1 == 4 ? (
      dh_ph = dh_p[10] + dh_i1; dh_ph >= 1 ? ( dh_ph -= 1; dh_wr = 1; dh_t = dh_ph / dh_i1; );
      dh_rp = dh_ph + dh_fmv * 0.5; dh_rp -= floor(dh_rp);
      dh_o1 = dh_w1 == 0 ? sin(2 * $pi * dh_rp) : dh_w1 == 1 ? 1 - 4 * abs(dh_rp - 0.5) : rand(2) - 1;
      dh_d = dh_i1;
    ) : (
      dh_d = min(max(dh_i1 * (1 + dh_fmv * 2), 0), 0.45);
      dh_ph = dh_p[10] + dh_d; dh_ph >= 1 ? ( dh_ph -= 1; dh_wr = 1; dh_t = dh_ph / dh_d; );
      dh_o1 = 2 * dh_ph - 1 - vo_blep(dh_ph, dh_d);
      dh_w1 == 3 ? ( dh_q = dh_ph + 1 - dh_p[29]; dh_q -= floor(dh_q); dh_o1 -= 2 * dh_q - 1 - vo_blep(dh_q, dh_d); );
    );
    dh_p[10] = dh_ph;
    // sub, one octave down
    dh_p[42] > 0 ? (
      dh_q = dh_p[12] + dh_i1 * 0.5; dh_q -= floor(dh_q); dh_p[12] = dh_q;
      dh_subw ? ( dh_qr = dh_q + 0.5; dh_qr -= floor(dh_qr);
                  dh_su = (2 * dh_q - 1 - vo_blep(dh_q, dh_i1 * 0.5)) - (2 * dh_qr - 1 - vo_blep(dh_qr, dh_i1 * 0.5)); )
              : dh_su = sin(2 * $pi * dh_q);
    ) : dh_su = 0;

    // ----- OSC 2 (hard sync: band-limited by a step correction across the reset, one sample late) -----
    dh_i2 = dh_p[25]; dh_pw2 = dh_p[29];
    dh_sync && dh_wr ? (
      dh_q = dh_p[11] + dh_i2 * (1 - dh_t); dh_q -= floor(dh_q);           // where OSC 2 was when OSC 1 reset it
      dh_h = (dh_w2 == 0 ? 0 : dh_w2 == 1 ? -1 : dh_w2 == 2 ? -1 : dh_w2 == 3 ? 2 * dh_pw2 - 2 : 0)
           - (dh_w2 == 0 ? sin(2 * $pi * dh_q) : dh_w2 == 1 ? 1 - 4 * abs(dh_q - 0.5) : dh_w2 == 2 ? 2 * dh_q - 1 :
              dh_w2 == 3 ? (dh_q < dh_pw2 ? 2 * dh_pw2 - 2 : 2 * dh_pw2) : 0);
      dh_q = dh_i2 * dh_t;
      dh_o2n = dh_w2 == 0 ? sin(2 * $pi * dh_q) : dh_w2 == 1 ? 1 - 4 * abs(dh_q - 0.5) : dh_w2 == 2 ? 2 * dh_q - 1 :
               dh_w2 == 3 ? (dh_q < dh_pw2 ? 2 * dh_pw2 - 2 : 2 * dh_pw2) : rand(2) - 1;
      dh_o2 = dh_p[23] + dh_h * 0.5 * dh_t * dh_t;
      dh_p[23] = dh_o2n - dh_h * 0.5 * sqr(1 - dh_t);
    ) : (
      dh_q = dh_p[11] + dh_i2; dh_q -= floor(dh_q);
      dh_o2n = dh_w2 == 0 ? sin(2 * $pi * dh_q) : dh_w2 == 1 ? 1 - 4 * abs(dh_q - 0.5) :
               dh_w2 == 2 ? 2 * dh_q - 1 - vo_blep(dh_q, dh_i2) :
               dh_w2 == 3 ? ( dh_qr = dh_q + 1 - dh_pw2; dh_qr -= floor(dh_qr);
                              (2 * dh_q - 1 - vo_blep(dh_q, dh_i2)) - (2 * dh_qr - 1 - vo_blep(dh_qr, dh_i2)); ) : rand(2) - 1;
      dh_o2 = dh_p[23]; dh_p[23] = dh_o2n;
    );
    dh_p[11] = dh_q; dh_p[33] = dh_o2;

    // ----- mix, saturation, filter -----
    dh_x = (dh_o1 * dh_p[39] + dh_o2 * dh_p[40] + dh_o1 * dh_o2 * dh_p[41] + dh_su * dh_p[42]) * 0.5;
    dh_sat > 0 ? dh_x = vo_tanh(dh_x * dh_sg) * dh_sn;
    dh_ft == 1 ? (
      // 24 dB: zero-delay ladder (four TPT one-poles), the feedback solved, the input soft-limited
      dh_lg = dh_p[26]; dh_bb = dh_p[27];
      dh_lk = dh_p[37];
      dh_y = (dh_p[28] * dh_x + dh_bb * (dh_lg * (dh_lg * (dh_lg * dh_p[19] + dh_p[20]) + dh_p[21]) + dh_p[22])) / (1 + dh_lk * dh_p[28]);
      dh_u = dh_x - dh_lk * dh_y; dh_u = vo_tanh(dh_u * 0.8) * 1.25;
      dh_v = (dh_u - dh_p[19]) * dh_lg; dh_y = dh_v + dh_p[19]; dh_p[19] = dh_y + dh_v;
      dh_v = (dh_y - dh_p[20]) * dh_lg; dh_y = dh_v + dh_p[20]; dh_p[20] = dh_y + dh_v;
      dh_v = (dh_y - dh_p[21]) * dh_lg; dh_y = dh_v + dh_p[21]; dh_p[21] = dh_y + dh_v;
      dh_v = (dh_y - dh_p[22]) * dh_lg; dh_y = dh_v + dh_p[22]; dh_p[22] = dh_y + dh_v;
      dh_y *= 1 + dh_lk * 0.5;
    ) : (
      // 12 dB: zero-delay state-variable (low, high or band)
      dh_v3 = dh_x - dh_p[20]; dh_v1 = dh_p[26] * dh_p[19] + dh_p[27] * dh_v3; dh_v2 = dh_p[20] + dh_p[27] * dh_p[19] + dh_p[28] * dh_v3;
      dh_p[19] = 2 * dh_v1 - dh_p[19]; dh_p[20] = 2 * dh_v2 - dh_p[20];
      dh_k = dh_p[36];
      dh_y = dh_ft == 0 ? dh_v2 : dh_ft == 2 ? dh_x - dh_k * dh_v1 - dh_v2 : dh_v1 * sqrt(dh_k);
      abs(dh_p[19]) > 4 ? dh_p[19] = vo_tanh(dh_p[19] * 0.25) * 4;   // tames the ringing at full resonance
    );

    // ----- amp envelope -----
    dh_a = dh_p[14];
    dh_p[13] == 1 ? ( dh_a += (1.3 - dh_a) * dh_aa; dh_a >= 1 ? ( dh_a = 1; dh_p[13] = 2; ); ) :
    dh_p[13] == 2 ? dh_a += (dh_as - dh_a) * dh_ad :
    ( dh_a -= dh_a * dh_ar; dh_a < 0.00002 ? ( dh_a = 0; dh_p[13] = 0; dh_p[15] = 0; dh_p[17] = 0; dh_p[0] = -1; dh_p[19] = dh_p[20] = dh_p[21] = dh_p[22] = 0; ); );
    dh_p[14] = dh_a;
    dh_y *= dh_a * dh_p[34] * dh_p[35];
    dh_L += dh_y * dh_p[31]; dh_R += dh_y * dh_p[32];
  );
  dh_p += 64;
);
dh_L *= dh_gtrem * dh_pcg; dh_R *= dh_gtrem * dh_pcg;          // (a preset fade reaches the effects' inputs too)

// ---------- EFFECTS ----------
dh_dist ? (
  dh_dtyp == 0 ? (
    dh_L = (dh_dzl.dh_daa(dh_pfl.clp_pf_run(dh_L) * dh_dg + 0.12 * dh_damt) - dh_dt0) * dh_dl;
    dh_R = (dh_dzr.dh_daa(dh_pfr.clp_pf_run(dh_R) * dh_dg + 0.12 * dh_damt) - dh_dt0) * dh_dl;
    dh_tl += (dh_L - dh_tl) * 0.55; dh_tr += (dh_R - dh_tr) * 0.55; dh_L = dh_tl; dh_R = dh_tr;
  ) : (
    dh_hcnt += dh_hr; dh_hcnt >= 1 ? ( dh_hcnt -= 1; dh_hold = floor(dh_L * dh_bits + 0.5) / dh_bits; dh_holdr = floor(dh_R * dh_bits + 0.5) / dh_bits; );
    dh_L = dh_hold; dh_R = dh_holdr;
    dh_dzl.rd = dh_dzr.rd = 1;
  );
) : dh_dzl.rd = dh_dzr.rd = 1;
dh_phs ? (
  dh_tick ? (
    dh_phph += dh_phr * (1 + 0.15 * ins_fx) * 4 / srate; dh_phph -= floor(dh_phph);
    dh_f = 280 * 2 ^ ((0.5 + 0.5 * sin(2 * $pi * dh_phph)) * dh_phd * 3.6) * dh_pis; dh_f *= 1 + dh_f * dh_f * 0.34; dh_pal = (dh_f - 1) / (dh_f + 1);
    dh_f = 280 * 2 ^ ((0.5 + 0.5 * cos(2 * $pi * dh_phph)) * dh_phd * 3.6) * dh_pis; dh_f *= 1 + dh_f * dh_f * 0.34; dh_par = (dh_f - 1) / (dh_f + 1);
  );
  dh_x = dh_L + dh_pfl * 0.5;
  dh_y = dh_pal * dh_x + dh_a1l; dh_a1l = dh_x - dh_pal * dh_y; dh_x = dh_y;
  dh_y = dh_pal * dh_x + dh_a2l; dh_a2l = dh_x - dh_pal * dh_y; dh_x = dh_y;
  dh_y = dh_pal * dh_x + dh_a3l; dh_a3l = dh_x - dh_pal * dh_y; dh_x = dh_y;
  dh_y = dh_pal * dh_x + dh_a4l; dh_a4l = dh_x - dh_pal * dh_y; dh_pfl = dh_y;
  dh_L = (dh_L + dh_y) * 0.5;
  dh_x = dh_R + dh_pfr * 0.5;
  dh_y = dh_par * dh_x + dh_a1r; dh_a1r = dh_x - dh_par * dh_y; dh_x = dh_y;
  dh_y = dh_par * dh_x + dh_a2r; dh_a2r = dh_x - dh_par * dh_y; dh_x = dh_y;
  dh_y = dh_par * dh_x + dh_a3r; dh_a3r = dh_x - dh_par * dh_y; dh_x = dh_y;
  dh_y = dh_par * dh_x + dh_a4r; dh_a4r = dh_x - dh_par * dh_y; dh_pfr = dh_y;
  dh_R = (dh_R + dh_y) * 0.5;
);
dh_cho ? (
  dh_chph += dh_chr * (1 + 0.15 * ins_fx) / srate; dh_chph -= floor(dh_chph);
  dh_tick ? (
    dh_cml = sin(2 * $pi * dh_chph); dh_cmr = sin(2 * $pi * (dh_chph + 0.25));
    dh_chf ? ( dh_cdl = (2.6 + 2.3 * dh_chdm * dh_cml) * 0.001 * srate; dh_cdr = (2.6 + 2.3 * dh_chdm * dh_cmr) * 0.001 * srate; )
           : ( dh_cdl = (8 + 5 * dh_chdm * dh_cml) * 0.001 * srate;    dh_cdr = (8 + 5 * dh_chdm * dh_cmr) * 0.001 * srate; );
  );
  dh_rp = dh_cwp - dh_cdl; dh_ri = floor(dh_rp); dh_rf = dh_rp - dh_ri;
  dh_wl = DCHL[dh_ri & DCHM] * (1 - dh_rf) + DCHL[(dh_ri + 1) & DCHM] * dh_rf;
  dh_rp = dh_cwp - dh_cdr; dh_ri = floor(dh_rp); dh_rf = dh_rp - dh_ri;
  dh_wr2 = DCHR[dh_ri & DCHM] * (1 - dh_rf) + DCHR[(dh_ri + 1) & DCHM] * dh_rf;
  DCHL[dh_cwp] = dh_L + (dh_chf ? dh_wl * 0.6 : 0);
  DCHR[dh_cwp] = dh_R + (dh_chf ? dh_wr2 * 0.6 : 0);
  dh_cwp = (dh_cwp + 1) & DCHM;
  dh_chf ? ( dh_L = (dh_L + dh_wl) * 0.6; dh_R = (dh_R + dh_wr2) * 0.6; )
         : ( dh_L = (dh_L + dh_wr2 * 0.8) * 0.72; dh_R = (dh_R + dh_wl * 0.8) * 0.72; );
);
dh_dly ? (
  dh_dts += (dh_dlt - dh_dts) * 0.0002;
  dh_rp = dh_wp - dh_dts; dh_ri = floor(dh_rp); dh_rf = dh_rp - dh_ri;
  dh_dl1 = DDLL[dh_ri & DDLM] * (1 - dh_rf) + DDLL[(dh_ri + 1) & DDLM] * dh_rf;
  dh_dr1 = DDLR[dh_ri & DDLM] * (1 - dh_rf) + DDLR[(dh_ri + 1) & DDLM] * dh_rf;
  dh_dfl += (dh_dl1 - dh_dfl) * 0.45; dh_dfr += (dh_dr1 - dh_dfr) * 0.45;        // darker repeats
  dh_dpp ? (
    DDLL[dh_wp] = (dh_L + dh_R) * 0.5 + vo_tanh(dh_dfr * dh_dfb);
    DDLR[dh_wp] = vo_tanh(dh_dfl * dh_dfb);
  ) : (
    DDLL[dh_wp] = dh_L + vo_tanh(dh_dfl * dh_dfb);
    DDLR[dh_wp] = dh_R + vo_tanh(dh_dfr * dh_dfb);
  );
  dh_wp = (dh_wp + 1) & DDLM;
  dh_L += dh_dl1 * dh_dmxm; dh_R += dh_dr1 * dh_dmxm;
);

// DC block (ring modulation, full resonance and asymmetric drive can leave an offset)
dh_dcyl = dh_L - dh_dcxl + 0.9996 * dh_dcyl; dh_dcxl = dh_L; dh_L = dh_dcyl;
dh_dcyr = dh_R - dh_dcxr + 0.9996 * dh_dcyr; dh_dcxr = dh_R; dh_R = dh_dcyr;

// ---------- MASTER ----------
dh_vol += (dh_volt - dh_vol) * dh_sm;
dh_L = ostL.ost_band(dh_L * dh_vol * dh_pcg); dh_R = ostR.ost_band(dh_R * dh_vol * dh_pcg);
ost_out(dh_L, dh_R);
spl0 = ost_l; spl1 = ost_r;
dh_pk_disp = max(dh_pk_disp, max(abs(spl0), abs(spl1)));

@gfx 1240 560
ui_thm = 13; ui_hw = 1;
ui_begin(1240, 560);

// ---------- software grey: palette ----------
PANEL = 0xB9BCC0; HDR = 0xA9ACB1; SHA = 0x9DA1A7; SHB = 0x979BA1; SHC = 0xA2A6AC;
STRIP = 0x45494F; STINK = 0xEEEFEA; INK = 0x1C1E21; DIM = 0x3A3D42;
LIT = 0xFF8A3D; KEYC = 0xD2D5D9; PTR = 0xEEEFEA; CAPG = 0x9DA2A8;
dy_ink = INK; dy_bg = SHA; dy_nov = 1; dy_kst = 0; dy_ntick = 11; rd_sqbg = 0x2A2C30;

// ---------- panel helpers (GUI thread only) ----------
function g_box(x, y, w, h, shade, title) (
  pk_inset(x * sc, y * sc, w * sc, h * sc, shade);
  ui_col(STRIP); gfx_rect(x * sc, y * sc, w * sc, 20 * sc);
  ui_cola(0xFFFFFF, 0.10); gfx_rect(x * sc, y * sc, w * sc, 1 * sc);
  gfx_setfont(4); ui_col(STINK); gfx_x = (x + 8) * sc; gfx_y = (y + 4) * sc; gfx_drawstr(title);
);
function g_set(idx, v) ( slider(idx) = v; ui_auto(idx); );
// a knob, value shown on hover; st = 1 rounds it to whole steps
function g_kn(cx, cy, r, idx, lo, hi, def, lg, label, fmt, st) (
  sprintf(#gv, fmt, slider(idx)); dy_knob(cx, cy, r, idx, lo, hi, def, lg, PTR, label, #gv);
  st ? ( g_r = floor(slider(idx) + 0.5); g_r != slider(idx) ? slider(idx) = g_r; );
);
// wave glyphs on the keys: 0 sine 1 triangle 2 saw 3 square 4 noise 5 s&h 6 saw down 7 smooth random 8 steps
function g_glyph(cx, cy, t, col) local(i, x0, y0, x1, y1, w, hh, k) (
  ui_col(col); w = 22; hh = 6; k = 0;
  loop(2,
    t == 0 ? ( i = 0; loop(22, x0 = cx - 11 + i; x1 = x0 + 1;
               gfx_line(x0 * sc, (cy + k - sin(i / 22 * 2 * $pi) * hh) * sc, x1 * sc, (cy + k - sin((i + 1) / 22 * 2 * $pi) * hh) * sc, 1); i += 1; ); ) :
    t == 1 ? ( gfx_line((cx - 11) * sc, (cy + k) * sc, (cx - 5.5) * sc, (cy + k - hh) * sc, 1); gfx_line((cx - 5.5) * sc, (cy + k - hh) * sc, (cx + 5.5) * sc, (cy + k + hh) * sc, 1);
               gfx_line((cx + 5.5) * sc, (cy + k + hh) * sc, (cx + 11) * sc, (cy + k) * sc, 1); ) :
    t == 2 ? ( gfx_line((cx - 11) * sc, (cy + k + hh) * sc, cx * sc, (cy + k - hh) * sc, 1); gfx_line(cx * sc, (cy + k - hh) * sc, cx * sc, (cy + k + hh) * sc, 1);
               gfx_line(cx * sc, (cy + k + hh) * sc, (cx + 11) * sc, (cy + k - hh) * sc, 1); gfx_line((cx + 11) * sc, (cy + k - hh) * sc, (cx + 11) * sc, (cy + k + hh) * sc, 1); ) :
    t == 3 ? ( gfx_line((cx - 11) * sc, (cy + k + hh) * sc, (cx - 11) * sc, (cy + k - hh) * sc, 1); gfx_line((cx - 11) * sc, (cy + k - hh) * sc, cx * sc, (cy + k - hh) * sc, 1);
               gfx_line(cx * sc, (cy + k - hh) * sc, cx * sc, (cy + k + hh) * sc, 1); gfx_line(cx * sc, (cy + k + hh) * sc, (cx + 11) * sc, (cy + k + hh) * sc, 1);
               gfx_line((cx + 11) * sc, (cy + k + hh) * sc, (cx + 11) * sc, (cy + k - hh) * sc, 1); ) :
    t == 4 ? ( i = 0; x0 = cx - 11; y0 = cy + k; loop(11, x1 = cx - 11 + (i + 1) * 2; y1 = cy + k + (rd_hash(i * 3.7 + 1) * 2 - 1) * hh;
               gfx_line(x0 * sc, y0 * sc, x1 * sc, y1 * sc, 1); x0 = x1; y0 = y1; i += 1; ); ) :
    t == 6 ? ( gfx_line((cx - 11) * sc, (cy + k - hh) * sc, (cx - 11) * sc, (cy + k + hh) * sc, 1); gfx_line((cx - 11) * sc, (cy + k - hh) * sc, cx * sc, (cy + k + hh) * sc, 1);
               gfx_line(cx * sc, (cy + k + hh) * sc, cx * sc, (cy + k - hh) * sc, 1); gfx_line(cx * sc, (cy + k - hh) * sc, (cx + 11) * sc, (cy + k + hh) * sc, 1); ) :
    t == 7 ? ( i = 0; loop(22, x0 = cx - 11 + i; x1 = x0 + 1;
               gfx_line(x0 * sc, (cy + k - (sin(i / 22 * 2 * $pi) * 0.6 + sin(i / 22 * 5.3 * $pi + 1) * 0.4) * hh) * sc,
                        x1 * sc, (cy + k - (sin((i + 1) / 22 * 2 * $pi) * 0.6 + sin((i + 1) / 22 * 5.3 * $pi + 1) * 0.4) * hh) * sc, 1); i += 1; ); ) :
    t == 8 ? ( i = 0; loop(6, y1 = (i == 0 ? 0.9 : i == 1 ? -0.3 : i == 2 ? 0.4 : i == 3 ? -0.9 : i == 4 ? 0.1 : -0.5) * hh;
               gfx_rect((cx - 11 + i * 3.8) * sc, (cy + k + min(y1, 0)) * sc, 2.6 * sc, max(abs(y1), 1) * sc); i += 1; ); ) :
    ( y0 = cy + k + hh * 0.2; i = 0; loop(4, y1 = cy + k + (i == 0 ? -0.8 : i == 1 ? 0.5 : i == 2 ? -0.2 : 0.9) * hh;
               gfx_line((cx - 11 + i * 5.5) * sc, y0 * sc, (cx - 11 + i * 5.5) * sc, y1 * sc, 1);
               gfx_line((cx - 11 + i * 5.5) * sc, y1 * sc, (cx - 5.5 + i * 5.5) * sc, y1 * sc, 1); y0 = y1; i += 1; ); );
    k += 0.7;
  );
);
// the selector key: light grey, lit in the accent when chosen; returns 1 when clicked
function g_sq(x, y, w, h, on, s) local(hit, hov) (
  hov = mouse_x >= x * sc && mouse_x < (x + w) * sc && mouse_y >= y * sc && mouse_y < (y + h) * sc;
  hit = pressed && hov && drag_id == 0; hit ? drag_id = -1;
  ui_cola(0x000000, 0.28); gfx_rect((x + 1) * sc, (y + 2) * sc, w * sc, h * sc);
  ui_col(0x3C3F44); gfx_rect(x * sc, y * sc, w * sc, h * sc);
  ui_col(on ? LIT : KEYC); gfx_rect((x + 1) * sc, (y + 1) * sc, (w - 2) * sc, (h - 2) * sc);
  gfx_gradrect((x + 1) * sc, (y + 1) * sc, (w - 2) * sc, (h - 2) * sc * 0.5, 1, 1, 1, on ? 0.30 : 0.35, 0, 0, 0, 0, 0, 0, 0, -(on ? 0.30 : 0.35) / ((h - 2) * sc * 0.5));
  ui_cola(0x000000, 0.12); gfx_rect((x + 1) * sc, (y + h - 2) * sc, (w - 2) * sc, 1 * sc);
  on ? ( ui_cola(LIT, 0.22); gfx_rect((x - 2) * sc, (y - 2) * sc, (w + 4) * sc, 2 * sc); gfx_rect((x - 2) * sc, (y + h) * sc, (w + 4) * sc, 2 * sc); );
  hov ? ( ui_cola(0xFFFFFF, 0.14); gfx_rect((x + 1) * sc, (y + 1) * sc, (w - 2) * sc, (h - 2) * sc); );
  strlen(s) ? ( gfx_setfont(4); ui_col(on ? 0x161616 : 0x26282C); ui_keytext((x + w * 0.5) * sc, (y + h * 0.5) * sc, s); );
  hit;
);
// a row of keys for a stepped slider: labels "A|B|C"; glyphs >= 0: draw wave glyphs from that number on instead
function g_keys(x, y, w, h, gap, idx, n, labels, glyphs) local(i, on) (
  i = 0;
  loop(n,
    on = floor(slider(idx) + 0.5) == i;
    glyphs >= 0 ? (
      g_sq(x + i * (w + gap), y, w, h, on, "") ? g_set(idx, i);
      g_glyph(x + i * (w + gap) + w * 0.5, y + h * 0.5, glyphs + i, on ? 0x161616 : 0x2A2C30);
    ) : (
      g_sq(x + i * (w + gap), y, w, h, on, pk_pitem(labels, i + 1, #gk)) ? g_set(idx, i);
    );
    i += 1;
  );
);
// an on / off key
function g_tog(x, y, w, h, idx, label) ( g_sq(x, y, w, h, slider(idx) >= 0.5, label) ? g_set(idx, slider(idx) >= 0.5 ? 0 : 1); );
// a display field with a drop-down arrow (a click opens a list); returns 1 when clicked
function g_field(x, y, w, h, s) local(hit, hov) (
  hov = mouse_x >= x * sc && mouse_x < (x + w) * sc && mouse_y >= y * sc && mouse_y < (y + h) * sc;
  hit = pressed && hov && drag_id == 0; hit ? drag_id = -1;
  ui_col(0x3C3F44); gfx_rect(x * sc, y * sc, w * sc, h * sc);
  ui_col(hov ? 0xFFFFFF : 0xEEEFEA); gfx_rect((x + 1) * sc, (y + 1) * sc, (w - 2) * sc, (h - 2) * sc);
  ui_cola(0x000000, 0.16); gfx_rect((x + 1) * sc, (y + 1) * sc, (w - 2) * sc, 2 * sc);
  gfx_setfont(4); ui_col(0x1C1E21); ui_keytext((x + (w - 10) * 0.5) * sc, (y + h * 0.5) * sc, s);
  ui_col(0x3A3D42); gfx_triangle((x + w - 12) * sc, (y + h * 0.5 - 2) * sc, (x + w - 5) * sc, (y + h * 0.5 - 2) * sc, (x + w - 8.5) * sc, (y + h * 0.5 + 2) * sc);
  hit;
);
// a key that shows a stepped slider's setting and opens the list
function g_menu(x, y, w, h, idx, labels) local(n, i, k) (
  n = pk_pcount(labels); k = floor(slider(idx) + 0.5);
  g_field(x, y, w, h, pk_pitem(labels, k + 1, #gk)) ? (
    strcpy(#gm, ""); i = 1;
    loop(n, i == k + 1 ? strcat(#gm, "!"); strcat(#gm, pk_pitem(labels, i, #gk)); i < n ? strcat(#gm, "|"); i += 1; );
    gfx_x = x * sc; gfx_y = (y + h) * sc; i = gfx_showmenu(#gm); i > 0 ? g_set(idx, i - 1);
  );
);
function g_cap(x, y, s) ( gfx_setfont(4); ui_cola(INK, 0.85); gfx_x = x * sc; gfx_y = y * sc; gfx_drawstr(s); );
function g_capc(x, y, s) ( gfx_setfont(4); ui_cola(INK, 0.85); ui_text(x * sc, y * sc, s); );
// an envelope sketch in a small dark window
function g_env(x, y, w, h, a, d, s, r, col) local(t, ta, td, tr, tt, sx, x1, y1, x2) (
  ui_col(0x24272B); gfx_rect(x * sc, y * sc, w * sc, h * sc);
  ui_cola(0x000000, 0.5); gfx_rect(x * sc, y * sc, w * sc, 1 * sc);
  ta = log(1 + a) ; td = log(1 + d); tr = log(1 + r); tt = ta + td + tr + 2.2;
  sx = (w - 8) / tt;
  ui_col(col);
  x1 = x + 4; y1 = y + h - 3; x2 = x1 + ta * sx;
  gfx_line(x1 * sc, y1 * sc, x2 * sc, (y + 3) * sc, 1);
  x1 = x2; x2 = x1 + td * sx;
  gfx_line(x1 * sc, (y + 3) * sc, x2 * sc, (y + 3 + (h - 6) * (1 - s)) * sc, 1);
  x1 = x2; x2 = x1 + 2.2 * sx;
  gfx_line(x1 * sc, (y + 3 + (h - 6) * (1 - s)) * sc, x2 * sc, (y + 3 + (h - 6) * (1 - s)) * sc, 1);
  x1 = x2; x2 = x + w - 4;
  gfx_line(x1 * sc, (y + 3 + (h - 6) * (1 - s)) * sc, x2 * sc, (y + h - 3) * sc, 1);
);

// one osc value for the displays
function g_osc(w, ph, pw) ( ph -= floor(ph);
  w == 0 ? sin(2 * $pi * ph) : w == 1 ? 1 - 4 * abs(ph - 0.5) : w == 2 ? 2 * ph - 1 : w == 3 ? (ph < pw ? -1 : 1) : rd_hash(floor(ph * 40) + 3) * 2 - 1; );
// what the oscillators make: two cycles of OSC 1 with OSC 2, MIX, RING, SUB, SYNC, PW
function g_wave(x, y, w, h) local(i, t, r, o1, o2, v, yy, py, m) (
  ui_col(0x24272B); gfx_rect(x * sc, y * sc, w * sc, h * sc);
  ui_cola(0xFFFFFF, 0.08); gfx_rect(x * sc, (y + h * 0.5) * sc, w * sc, 1 * sc);
  r = 2 ^ ((slider6 + slider7 / 100) / 12); m = slider11 / 100;
  i = 0; py = 0;
  loop(w - 4,
    t = i / (w - 4) * 2;
    o1 = g_osc(slider1, t, slider12 / 100);
    o2 = g_osc(slider5, slider9 >= 0.5 ? (t - floor(t)) * r : t * r, slider12 / 100);
    v = (o1 * (1 - m) + o2 * m) * (1 - slider10 / 100) + o1 * o2 * slider10 / 100 + (slider2 >= 0.5 ? g_osc(3, t * 0.5, 0.5) : sin($pi * t)) * slider3 / 100 * 0.8;
    v = min(max(v / (1 + slider3 / 100 * 0.6), -1.1), 1.1);
    yy = y + h * 0.5 - v * (h * 0.5 - 5);
    ui_col(0x8FD0FF); i > 0 ? gfx_line((x + 1 + i) * sc, py * sc, (x + 2 + i) * sc, yy * sc, 1);
    py = yy; i += 1;
  );
);
// the filter's response (a sketch of the analogue prototype)
function g_fcurve(x, y, w, h) local(i, f, q, wn, m, yy, py, a, b, db, fc) (
  ui_col(0x24272B); gfx_rect(x * sc, y * sc, w * sc, h * sc);
  fc = slider18; q = 0.5 + 12 * sqr(slider19 / 100);
  i = 0; py = 0;
  loop(w - 4,
    f = 20 * 1000 ^ (i / (w - 5)); wn = f / fc;
    a = 1 - wn * wn; b = wn / q; m = 1 / sqrt(a * a + b * b);
    slider17 < 0.5 ? 0 : slider17 < 1.5 ? ( q = 0.5 + 18 * sqr(slider19 / 100); b = wn / q; m = 1 / sqrt(a * a + b * b) / sqrt(1 + wn * wn * wn * wn); q = 0.5 + 12 * sqr(slider19 / 100); ) :
    slider17 < 2.5 ? m *= wn * wn : m *= wn / q;
    db = 20 * log10(max(m, 0.00001));
    yy = y + 8 + (12 - min(max(db, -36), 24)) / 48 * (h - 10);
    ui_col(0x8FD0FF); i > 0 ? gfx_line((x + 1 + i) * sc, py * sc, (x + 2 + i) * sc, yy * sc, 1);
    py = yy; i += 1;
  );
);

// one LFO's shape for the scope (the panel's own copy: the audio thread has its own); the random shapes are sketched
function g_lfod(l, sh, ph) local(i, a, b) (
  sh == 0 ? sin(2 * $pi * ph) : sh == 1 ? 1 - 4 * abs(ph - 0.5) : sh == 2 ? 2 * ph - 1 : sh == 3 ? (ph < 0.5 ? 1 : -1) :
  sh == 5 ? 1 - 2 * ph : sh == 7 ? DSTEP[l * 16 + min(floor(ph * 16), 15)] : (
    i = floor(ph * 4); a = rd_hash(i * 2.3 + 7) * 1.8 - 0.9; b = rd_hash(((i + 1) & 3) * 2.3 + 7) * 1.8 - 0.9;
    sh == 4 ? a : a + (b - a) * (0.5 - 0.5 * cos($pi * (ph * 4 - i)));
  );
);
// the LFO window: the shape over one cycle and a dot where the LFO is now; STEP: the 16 steps, drawn with the mouse
function g_lscope(x, y, w, h, l, sh) local(i, v, yy, py, ph, g, hov, bw) (
  ui_col(0x24272B); gfx_rect(x * sc, y * sc, w * sc, h * sc);
  ui_cola(0x000000, 0.5); gfx_rect(x * sc, y * sc, w * sc, 1 * sc);
  ui_cola(0xFFFFFF, 0.08); gfx_rect(x * sc, (y + h * 0.5) * sc, w * sc, 1 * sc);
  g = DLG + l * 12; ph = g[0];
  sh == 7 ? (
    hov = mouse_x >= x * sc && mouse_x < (x + w) * sc && mouse_y >= y * sc && mouse_y < (y + h) * sc;
    pressed && hov && drag_id == 0 ? drag_id = 1990;
    drag_id == 1990 && mdown ? (
      i = min(max(floor((mouse_x / sc - x) / w * 16), 0), 15);
      DSTEP[l * 16 + i] = min(max(1 - 2 * (mouse_y / sc - y - 4) / (h - 8), -1), 1);
    );
    bw = w / 16;
    i = 0;
    loop(16,
      v = DSTEP[l * 16 + i]; yy = y + h * 0.5 - v * (h * 0.5 - 4);
      ui_col(i == floor(ph * 16) ? LIT : 0x8FD0FF);
      gfx_rect((x + i * bw + 1.5) * sc, min(yy, y + h * 0.5) * sc, (bw - 3) * sc, max(abs(yy - y - h * 0.5), 1) * sc);
      i += 1;
    );
    gfx_setfont(4); ui_cola(0xEEEFEA, 0.45); gfx_x = (x + 4) * sc; gfx_y = (y + 3) * sc; gfx_drawstr("DRAW");
  ) : (
    i = 0; py = 0;
    loop(w - 4,
      yy = y + h * 0.5 - g_lfod(l, sh, i / (w - 5)) * (h * 0.5 - 6);
      ui_col(0x8FD0FF); i > 0 ? gfx_line((x + 1 + i) * sc, py * sc, (x + 2 + i) * sc, yy * sc, 1);
      py = yy; i += 1;
    );
    v = sh == 4 ? g[1] : sh == 6 ? g[2] + (g[1] - g[2]) * (0.5 - 0.5 * cos($pi * ph)) : g_lfod(l, sh, ph);
    yy = y + h * 0.5 - v * (h * 0.5 - 6);
    ui_cola(LIT, 0.35); gfx_circle((x + 2 + ph * (w - 5)) * sc, yy * sc, 5.5 * sc, 1, 1);
    ui_col(LIT); gfx_circle((x + 2 + ph * (w - 5)) * sc, yy * sc, 3.5 * sc, 1, 1);
  );
);

// ---------- the panel ----------
ui_col(PANEL); gfx_rect(0, 0, gfx_w, gfx_h);
gfx_gradrect(0, 0, 1240 * sc, 280 * sc, 1, 1, 1, 0.10, 0, 0, 0, 0, 0, 0, 0, -0.10 / (280 * sc));
ui_col(HDR); gfx_rect(0, 0, 1240 * sc, 60 * sc);
gfx_gradrect(0, 0, 1240 * sc, 60 * sc, 1, 1, 1, 0.16, 0, 0, 0, 0, 0, 0, 0, -0.16 / (60 * sc));
ui_cola(0x000000, 0.35); gfx_rect(0, 60 * sc, 1240 * sc, 1 * sc);
ui_cola(0xFFFFFF, 0.35); gfx_rect(0, 61 * sc, 1240 * sc, 1 * sc);
ui_cola(0x000000, 0.25); gfx_rect(0, 556 * sc, 1240 * sc, 4 * sc);

// header: roundel, name, preset bar, voice display, MIDI, size
rd_badge(36, 30, 16, 0x2E3135, 0xEEEFEA);
rd_title(62, 10, "DAICHI", 27, 0x202226);
rd_sub(64, 41, "VIRTUAL ANALOGUE SYNTHESIZER", 0x2E3135, 0.85);
prs_cur_d > 0 ? ( pr_cur = prs_cur_d; prs_cur_d = 0; );     // PRESET parameter: show a preset loaded by the host
gk_ = pk_preset(356 * sc, 13 * sc, 470 * sc, 34 * sc, pr_cur, #dh_pm);
gk_ > 0 ? prs_dirty = gk_;
pk_lcd(840 * sc, 13 * sc, 170 * sc, 34 * sc, 0);
gn_ = 0; gi_ = 0; loop(16, DV[gi_ * 64 + 13] > 0 ? gn_ += 1; gi_ += 1; );
pk_lcdfont(9, 10 * sc); ui_col(pk_lcdc);
sprintf(#gl, "%s  %s", slider48 == 0 ? "POLY" : slider48 == 1 ? "MONO" : "LEGATO", slider56 >= 0.5 ? "ARP" : ""); gfx_x = 850 * sc; gfx_y = 17 * sc; gfx_drawstr(#gl);
sprintf(#gl, "VOICES %2d/%d", gn_, floor(slider49 + 0.5)); gfx_x = 850 * sc; gfx_y = 31 * sc; gfx_drawstr(#gl);
rd_led(1030, 24, 4, ins_midi_lit(), 0x3ADF6A); rd_lbl(1030, 33, "MIDI", 0x2E3135, 0.9);
sprintf(#gl, slider78 < 0.5 ? "OMNI" : "CH %.0f", slider78);
g_field(1050, 18, 64, 24, #gl) ? (
  strcpy(#gm, ""); gi_ = 0;
  loop(17, gi_ == floor(slider78 + 0.5) ? strcat(#gm, "!"); gi_ == 0 ? strcat(#gm, "Omni") : ( sprintf(#gk, "Channel %d", gi_); strcat(#gm, #gk); ); gi_ < 16 ? strcat(#gm, "|"); gi_ += 1; );
  gfx_x = 1050 * sc; gfx_y = 42 * sc; gi_ = gfx_showmenu(#gm); gi_ > 0 ? g_set(78, gi_ - 1);
);
ui_size_picker(1160 * sc, 21 * sc);

// ---------- top row ----------
// OSC 1
g_box(12, 72, 210, 228, SHA, "OSC 1");
g_keys(22, 98, 34, 24, 4, 1, 5, "", 0);
g_kn(52, 166, 17, 3, 0, 100, 0, 0, "SUB", "%.0f%%", 0);
g_kn(117, 166, 17, 4, 0, 100, 0, 0, "FM", "%.0f%%", 0);
g_kn(182, 166, 17, 12, 5, 95, 50, 0, "PW", "%.0f%%", 0);
g_cap(22, 232, "SUB WAVE");
g_keys(22, 248, 34, 24, 4, 2, 2, "", 0);
g_cap(112, 232, "WAVE");
g_wave(112, 244, 100, 40);

// OSC 2
g_box(232, 72, 250, 228, SHB, "OSC 2");
g_keys(242, 98, 34, 24, 4, 5, 5, "", 0);
g_tog(434, 98, 40, 24, 9, "SYNC");
g_kn(266, 166, 17, 6, -24, 24, 0, 0, "PITCH", "%+.0f st", 1);
g_kn(324, 166, 17, 7, -50, 50, 8, 0, "FINE", "%+.1f ct", 0);
g_kn(382, 166, 17, 10, 0, 100, 0, 0, "RING", "%.0f%%", 0);
g_kn(446, 166, 17, 11, 0, 100, 50, 0, "MIX", "%.0f%%", 0);
gfx_setfont(4); ui_cola(INK, 0.7); ui_text(424 * sc, 196 * sc, "1"); ui_text(468 * sc, 196 * sc, "2");
g_tog(242, 248, 64, 24, 8, "TRACK");
g_cap(242, 232, "KEYBOARD");
sprintf(#gl, "%+.0f st  %+.1f ct", slider6, slider7);
pk_lcd(318 * sc, 236 * sc, 152 * sc, 40 * sc, 0); pk_lcdfont(9, 13 * sc); ui_col(pk_lcdc);
ui_text(394 * sc, 250 * sc, #gl);

// MOD ENV
g_box(492, 72, 150, 228, SHC, "MOD ENV");
g_keys(500, 98, 43, 24, 3, 16, 3, "OSC 2|FM|PW", -1);
g_kn(530, 166, 17, 13, 0.5, 5000, 1, 1, "ATTACK", "%.1f ms", 0);
g_kn(604, 166, 17, 14, 5, 5000, 300, 1, "DECAY", "%.0f ms", 0);
g_kn(567, 244, 19, 15, -100, 100, 0, 0, "AMOUNT", "%+.0f%%", 0);
gfx_setfont(4); ui_cola(INK, 0.7); ui_text(530 * sc, 266 * sc, "-"); ui_text(604 * sc, 266 * sc, "+");

// FILTER
g_box(652, 72, 350, 228, SHA, "FILTER");
g_keys(662, 98, 52, 24, 4, 17, 4, "LP 12|LP 24|HP 12|BP 12", -1);
g_fcurve(888, 94, 104, 32);
sprintf(#gv, "%.0f Hz", slider18); dy_kf = 1;   // the CUTOFF cap is always yellow
dy_knob(690, 166, 22, 18, 20, 20000, 6000, 1, PTR, "CUTOFF", #gv);
g_kn(758, 166, 17, 19, 0, 100, 10, 0, "RESO", "%.0f%%", 0);
g_kn(818, 166, 17, 20, 0, 100, 0, 0, "SAT", "%.0f%%", 0);
g_kn(878, 166, 17, 21, 0, 100, 50, 0, "KEY", "%.0f%%", 0);
g_kn(938, 166, 17, 23, 0, 100, 0, 0, "VELO", "%.0f%%", 0);
g_kn(690, 244, 17, 22, -100, 100, 0, 0, "ENV", "%+.0f%%", 0);
g_kn(758, 244, 17, 24, 0.5, 5000, 1, 1, "ATT", "%.1f ms", 0);
g_kn(818, 244, 17, 25, 5, 8000, 400, 1, "DEC", "%.0f ms", 0);
g_kn(878, 244, 17, 26, 0, 100, 50, 0, "SUS", "%.0f%%", 0);
g_kn(938, 244, 17, 27, 5, 8000, 300, 1, "REL", "%.0f ms", 0);

// AMP
g_box(1012, 72, 216, 228, SHB, "AMP");
g_env(1022, 98, 196, 40, slider28, slider29, slider30 / 100, slider31, 0x8FD0FF);
g_kn(1060, 176, 19, 32, -24, 12, 0, 0, "GAIN", "%+.1f dB", 0);
g_kn(1180, 176, 17, 33, 0, 100, 50, 0, "VELO", "%.0f%%", 0);
g_kn(1036, 244, 16, 28, 0.5, 5000, 1, 1, "ATT", "%.1f ms", 0);
g_kn(1088, 244, 16, 29, 5, 8000, 300, 1, "DEC", "%.0f ms", 0);
g_kn(1140, 244, 16, 30, 0, 100, 100, 0, "SUS", "%.0f%%", 0);
g_kn(1192, 244, 16, 31, 5, 8000, 200, 1, "REL", "%.0f ms", 0);

// ---------- bottom row ----------
// LFO: one section, the 1 / 2 / 3 keys pick which LFO the controls show (a lamp on each key: that LFO is working)
g_box(12, 314, 382, 232, SHA, "LFO");
gl_ = min(max(floor(dh_lsel + 0.5), 0), 2);
ga_ = gl_ == 0 ? 34 : gl_ == 1 ? 41 : 93; gc_ = gl_ == 0 ? 85 : gl_ == 1 ? 89 : 100;
gi_ = 0;
loop(3,
  sprintf(#gl, "%d", gi_ + 1);
  g_sq(20 + gi_ * 34, 340, 31, 24, gi_ == gl_, #gl) ? dh_lsel = gi_;
  rd_led(20 + gi_ * 34 + 25, 346, 2.5, DLF[gi_ * 16 + 14] > 0, 0x3ADF6A);
  gi_ += 1;
);
g_keys(130, 340, 56, 24, 3, gc_ + 2, 2, "GLOBAL|VOICE", -1);
g_tog(254, 340, 42, 24, ga_ + 6, "KEY");
g_menu(304, 340, 82, 24, ga_ + 2, "FREE|SYNC|FLOW");
// shapes, in the order sine, triangle, saw up, saw down, square, S&H, smooth random, STEP
gi_ = 0;
loop(8,
  gk_ = gi_ == 3 ? 5 : gi_ == 4 ? 3 : gi_ == 5 ? 4 : gi_;
  g_sq(20 + gi_ * 46, 372, 43, 24, floor(slider(ga_) + 0.5) == gk_, "") ? g_set(ga_, gk_);
  g_glyph(20 + gi_ * 46 + 21.5, 384, gi_ == 3 ? 6 : gi_ == 4 ? 3 : gi_ == 5 ? 5 : gi_ == 6 ? 7 : gi_ == 7 ? 8 : gi_,
          floor(slider(ga_) + 0.5) == gk_ ? 0x161616 : 0x2A2C30);
  gi_ += 1;
);
slider(ga_ + 2) >= 0.5 ? (
  sprintf(#gv, "%s", pk_pitem("4/1|2/1|1/1|1/2|1/2T|1/4|1/4T|1/8|1/8T|1/16|1/16T|1/32", floor(slider(ga_ + 3) + 0.5) + 1, #gk));
  dy_knob(44, 430, 17, ga_ + 3, 0, 11, 5, 0, PTR, "RATE", #gv);
  slider(ga_ + 3) = floor(slider(ga_ + 3) + 0.5);
) : g_kn(44, 430, 17, ga_ + 1, 0.02, 30, gl_ == 0 ? 5 : gl_ == 1 ? 0.5 : 0.25, 1, "RATE", "%.2f Hz", 0);
g_kn(100, 430, 17, gc_ + 3, 0, 5000, 0, 0, "FADE", "%.0f ms", 0);
g_lscope(136, 404, 250, 74, gl_, floor(slider(ga_) + 0.5));
#dh_dsts = "PITCH|OSC 2 PITCH|FILTER|AMP|PAN|FM|PULSE WIDTH|RESONANCE|OSC MIX|RING|SUB|UNISON DETUNE|CHORUS DEPTH|DELAY MIX|LFO 1 RATE|LFO 2 RATE|LFO 3 RATE|LFO 1 DEPTH|LFO 2 DEPTH|LFO 3 DEPTH";
g_kn(38, 504, 16, ga_ + 4, -100, 100, 0, 0, "DEPTH A", "%+.0f%%", 0);
g_menu(60, 492, 136, 24, ga_ + 5, #dh_dsts);
g_kn(226, 504, 16, gc_, -100, 100, 0, 0, "DEPTH B", "%+.0f%%", 0);
g_menu(248, 492, 136, 24, gc_ + 1, #dh_dsts);

// VOICE / ARP
g_box(404, 314, 352, 232, SHB, "VOICE");
gfx_setfont(4); ui_col(STINK); gfx_x = 630 * sc; gfx_y = 318 * sc; gfx_drawstr("ARPEGGIATOR");
ui_cola(0x000000, 0.25); gfx_rect(620 * sc, 334 * sc, 1 * sc, 212 * sc); ui_cola(0xFFFFFF, 0.25); gfx_rect(621 * sc, 334 * sc, 1 * sc, 212 * sc);
g_keys(414, 340, 62, 24, 4, 48, 3, "POLY|MONO|LEGATO", -1);
g_kn(436, 402, 17, 49, 1, 16, 8, 0, "VOICES", "%.0f", 1);
g_kn(494, 402, 17, 50, 1, 8, 1, 0, "UNISON", "%.0f", 1);
g_kn(552, 402, 17, 51, 0, 50, 15, 0, "DETUNE", "%.1f ct", 0);
g_kn(604, 402, 15, 52, 0, 100, 60, 0, "SPREAD", "%.0f%%", 0);
g_kn(436, 482, 17, 53, 0, 2000, 0, 0, "PORTA", "%.0f ms", 0);
g_kn(494, 482, 17, 54, 0, 24, 2, 0, "BEND", "%.0f st", 1);
g_menu(526, 466, 88, 24, 55, "OFF|VIBRATO|LFO 1|CUTOFF|FM");
g_capc(570, 503, "WHEEL");
g_tog(630, 340, 116, 24, 56, "ARP");
g_cap(632, 380, "MODE");
g_menu(672, 374, 74, 24, 57, "UP|DOWN|UP-DOWN|DOWN-UP|PLAYED|RANDOM|CHORD");
g_cap(632, 414, "RATE");
g_menu(672, 408, 74, 24, 59, "1/4|1/4T|1/8|1/8T|1/16|1/16T|1/32");
dy_step = 4; dy_steps = "1|2|3|4"; sprintf(#gv, "%.0f oct", slider58); dy_knob(660, 488, 15, 58, 1, 4, 1, 0, PTR, "RANGE", #gv);
slider58 = floor(slider58 + 0.5);
g_kn(718, 488, 17, 60, 10, 200, 60, 0, "GATE", "%.0f%%", 0);

// EFFECTS
g_box(766, 314, 300, 232, SHC, "EFFECTS");
ui_cola(0x000000, 0.2); gfx_rect(916 * sc, 340 * sc, 1 * sc, 200 * sc); gfx_rect(772 * sc, 440 * sc, 288 * sc, 1 * sc);
ui_cola(0xFFFFFF, 0.25); gfx_rect(917 * sc, 340 * sc, 1 * sc, 200 * sc); gfx_rect(772 * sc, 441 * sc, 288 * sc, 1 * sc);
g_tog(774, 342, 60, 22, 61, "DIST");
g_keys(840, 342, 34, 22, 3, 62, 2, "DRV|BIT", -1);
g_kn(842, 400, 16, 63, 0, 100, 30, 0, "AMOUNT", "%.0f%%", 0);
g_tog(924, 342, 70, 22, 64, "PHASER");
g_kn(960, 400, 16, 65, 0.05, 10, 0.4, 1, "RATE", "%.2f Hz", 0);
g_kn(1024, 400, 16, 66, 0, 100, 60, 0, "DEPTH", "%.0f%%", 0);
g_tog(774, 448, 60, 22, 67, "CHORUS");
g_keys(840, 448, 34, 22, 3, 68, 2, "CHO|FLA", -1);
g_kn(806, 504, 16, 69, 0.05, 10, 0.6, 1, "RATE", "%.2f Hz", 0);
g_kn(872, 504, 16, 70, 0, 100, 50, 0, "DEPTH", "%.0f%%", 0);
g_tog(924, 448, 60, 22, 71, "DELAY");
g_tog(990, 448, 68, 22, 75, "PING-PONG");
g_menu(924, 480, 48, 22, 72, "1/2|1/4D|1/4|1/4T|1/8D|1/8|1/8T|1/16");
g_capc(948, 510, "TIME");
g_kn(1000, 504, 16, 73, 0, 90, 35, 0, "FDBK", "%.0f%%", 0);
g_kn(1042, 504, 16, 74, 0, 100, 25, 0, "MIX", "%.0f%%", 0);

// MASTER
g_box(1076, 314, 152, 232, SHA, "MASTER");
g_kn(1128, 400, 21, 77, -24, 6, 0, 0, "VOLUME", "%+.1f dB", 0);
dh_mtr.ui_ledm(1206 * sc, 344 * sc, 10 * sc, 188 * sc, dh_pk_disp); dh_pk_disp *= 0.85;
dy_kst = 20;
sprintf(#gv, "%.0f%%", slider76); dy_knob(1104, 492, 13, 76, 0, 100, 25, 0, CAPG, "DRIFT", #gv);
dy_kst = 20;
sprintf(#gv, "%.0f%%", slider79); dy_knob(1152, 492, 13, 79, 0, 100, 0, 0, 0xD8B040, "SAT", #gv);
dy_kst = 0;

pk_end();
ui_end();
