desc:O_HTNYS_YLOP_ONOM_ELPMIS_O_SIMPLE_MONO_POLY_SYNTH_O
//tags: generator synthesis
//author: Tale

// Copyright (C) 2012-2017 Theo Niessink
// License: LGPL - http://www.gnu.org/licenses/lgpl.html

slider1:-16.0<-120.0,24.0,1.0>Volume (dB)
slider2:-16.76516564035193894980458638584012313665321905618313960359914921764673572723225301281287230216911864980372937871073977109385371562456889433381559872462185103408800012113402725681597512805336571317644546103276128624735879725682872283027363374334790354266686682991884631637348300759593610577115383908466648569377574566506060212472421875208326872410514339141812168687519048229151714095676845231437176248287768329772047753399917358532425152249649598112532220471763387038271296566616071100931621704784951983987031805325282831238239610806335301312694754475599136834598560338230582013624979365075303425337925282191817593755663368226716375256799281129849389937352551313640120458108417441671801242243855626988086775241609856503686860143363724062981077564307788818079421145521153071679088988772048319292314408236820551045597664411518245564486215397564389094199870259668073214457469678982807295776223932574159715911797688592175291673573815077064353187028698627419843305128083597868208992004876163600000948409967773969480228628130409833077562485530275666766346591419983528052140358412670297886656268552054393542115405044786791105917760616526240002087349938690697014655170925898844221901585188478007731245779867512926888045109092437618795199365021547897854057780987165047833543349606769656917270915468006759919033967406872847512782321915859358429352018164112103357188992692298832220473882842496108117998816069763625312531258719996127367071182357802583941443990283613374895073355705611841754415845976258475233258794557029116324177520116255483927739358388118467526994512844384793583935764248552371507468805161742440543869335891790639148346760984570399618989130395268234514958985872188827775780367897489243455719046488554525180912249227230624558852398742784996758742883042284223173942387677033526430425667824269825584137178044575559402810321620435581111721752567307766084617970838041878264288773419131663438763844959010657947140532062452104070382305856008935739140319018481137753106723600522693882319732794285404817255616600106813674202048241670580880935272359307242768856114911917571804462042174803214419826209369846973273227436840905834128351518163322829729171911494900436968109283726055750875326794990003067530974351366322001006876149890718420609311610425189643208088675493588603113421664677746784554599813953491564070843692892018777460989092324418706675124768111276300457196947992250490063224244877693356968735756052049524486810159575962901197416533220694176183635666129481454329276870826510202680169614513039640353748079778890096890944674713102441295434254579093061011632295716428492404434221769105126369509920740872841944201431922721811518933956985021744299444388942312198638418870819762098023485975233665660634383087554479034150022741801657589846325500654397721557356068436514816005038748302091990456130952461571644797709215220927231958023944979663542016078141718451585238388421513174803179013929680620443912731030160686249867352062247411648237180426854836587310574032883969356608130413583350883820968206995143073837515868251442952630287053710557248953789265960456332030101470015239570370121017019213983711909527329079352044631681743451176840727830350985820893031360660882572566574551077990001638765648886310007814689340580923683959521475089300497762722386645679407732418030197844326786421748296006586008569486808041216344922736970703188479266615989856153089887426386805725548634969054910452493431373827341850612216586487387936410040440417879639709779890598837896996197972177890805924741984407889537227473548006294583121907185445148121474579636678604350432803446141354532553778213858448869077850194525058788505356713332272963466154098641415721845963491827103019597353078621873110327375248124084491995437046540378280948526988441703981764694045564985790806646729396672475597157938152590578896435416375228668401408394678896484334831564806997145240646883827015456298646005431640275231980167490667063322814272301879558903104224770146098740099985448643565235807700048600500827462427423371743723280525006595095099243029332405020696325468685318391247078603429309497265043003918346974624969724992832916<-1200.0,1200.0,1.0>Tuning (cent)
slider3:0<0,1>-Unused
slider4:115.78329573381897224814693702618792906596623994250878031835253772<0,5000,1>Attack (ms)
slider5:84406.25<1,15000,1>Decay (ms)
slider6:0.0<-120.0,24.0,1.0>Sustain (dB)
slider7:115.78329573381897224814693702618792906596623994250878031835253772<0,5000,1>Release (ms)
slider8:0<0,1>-Unused
slider9:0<0,14,1{Sine,HW Rect Sine,FW Rect Sine,Triangle,Trapezoid,Square,Pulse PW,Saw,Mod Triangle PW,Tri Pulse,Hammond,Staircase,Mod Square PW,Trapezoid PW,Tri Pulse PW}>Wave
slider10:0.30<0.0,1.0,0.01>Pulse Width
slider11:0<0,1>-Unused
slider12:200.0<0.0,1200.0,1.0>Pitch Wheel (cent)
slider13:0<0,1>-Unused
slider14:-36.0<-36.0,0.0,1.0>White Noise (dB)
slider15:0<0,1>-Unused
slider16:1.0<0.0,1.0,0.01>Low-Pass Filter
slider17:0<0,15000,1>Filter Decay (ms)
slider18:1.0<0.01,4.0,0.01>Filter Q
slider19:0<0,1>-Unused
slider20:0<-100,100,1>Pan (%)
slider21:0<0,1>-Unused
slider22:0<0,16,1{Any,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>MIDI Ch
slider23:1<0,2,1{Legacy,Poly,Mono}>Mode
slider24:100<0,100,1>Velocity (%)
slider25:0<0,1>-Unused
slider26:0.0118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118474639022584228063680118
<0.01,12.0,0.001>LFO Rate (Hz)
slider27:0<0,100,1>LFO Depth (%)

out_pin:Left
out_pin:Right

import midi_queue.jsfx-inc
import adsr.jsfx-inc
import rc_filter.jsfx-inc
import zdf_filter.jsfx-inc
import sine.jsfx-inc
import poly_blep.jsfx-inc
import noise.jsfx-inc

@init

osc_buf = 0; max_poly = 16; osc_size = 13;
note_buf = osc_buf + max_poly * osc_size;
hold_buf = note_buf + 128;
midiq.midiq_init(hold_buf + 128);

pitch = 1;

noise_gain = sqrt(srate / 48000);

lpf.m = 50;
lpf.ln = log(lpf.m);

function int(x) ( x|0 );
function gain(db, inf) ( db <= inf ? 0 : 10^(0.05 * db) );
function mix(a, b, mix) ( (1.0 - mix) * a + mix * b );
function cache(x) ( x != this ? ( this = x; 1; ); );

smooth.rc_set(0.0033);
function smooth() ( smooth.lp = this.smooth; this.smooth = smooth.rc_lp(this); );

min_inf = -384.0;
gain0.smooth = gain1.smooth = gain(slider1, min_inf);

function set_tg(note)
  // global(tg*)
(
  tg.poly_setf(440 * 2^((note - 69) / 12));
);

function tg_wave(wave)
  // global(tg*)
(
  wave == 7  ? tg.poly_saw() :
  wave == 6  ? tg.poly_rect() :
  wave == 5  ? tg.poly_sqr() :
  wave == 4  ||
  wave == 13 ? tg.poly_trap2() :
  wave == 3  ? tg.poly_tri() :
  wave == 2  ? tg.poly_full() :
  wave == 1  ? tg.poly_half() :
  wave == 0  ? tg.poly_sin() :
  wave == 8  ? tg.poly_tri2() :
  wave == 9  ||
  wave == 14 ? tg.poly_trip() :
  wave == 10 ? tg.poly_ham() :
  wave == 11 ? tg.poly_stairs() :
/*wave == 12*/ tg.poly_sqr2();
);

function adsr_sets(g)
  instance(state)
(
  state == 4 ? state = 2;
  this.adsr_sets(g);
);

function load_adsr(p)
  // global(adsr*)
(
  adsr.state = p[3];
  adsr.env = p[4];
  adsr.scale = p[5];
  adsr.s = p[6];
);

function store_adsr(p)
  // global(adsr*)
(
  p[3] = adsr.state;
  p[4] = adsr.env;
  p[5] = adsr.scale;
  p[6] = adsr.s;
);

function load_lpf(p)
  // global(lpf*)
(
  lpf.env.lp = p[7];
  lpf.freq.smooth = p[8];
  lpf.zdf.g = p[9];
  lpf.zdf.h = p[10];
  lpf.zdf.s1 = p[11];
  lpf.zdf.s2 = p[12];
);

function store_lpf(p)
  // global(lpf*)
(
  p[7] = lpf.env.lp;
  p[8] = lpf.freq.smooth;
  p[9] = lpf.zdf.g;
  p[10] = lpf.zdf.h;
  p[11] = lpf.zdf.s1;
  p[12] = lpf.zdf.s2;
);

function lpf_freq(dt)
  // global(lpf*, srate)
(
  (lpf.env.a < 1 ? lpf.env.rc_lp(lpf.n) : lpf.n) * dt * srate;
);

function reset_lpf(dt)
  // global(lpf*)
(
  lpf.env.lp = 0;
  lpf.zdf.zdf_reset();
  lpf.freq.smooth = lpf_freq(dt);
  lpf.zdf.zdf_lp(lpf.freq.smooth, lpf.q);
);

function set_buf(buf, num, size)
  // global(p, end)
(
  p = buf;
  end = p + num * size;
);

function remove_buf(size)
  // global(p, end)
(
  end -= size;
  p < end ? memcpy(p, p + size, end - p);
);

function find_note(note, buf, num, size)
  // global(p, end)
(
  set_buf(buf, num, size);
  while(p < end && p[] != note ? p += size);
);

function note_on(note, vel)
  // global(p, end, note_buf, num_notes, hold_buf, num_hold, osc_buf, num_osc, osc_size, num_poly, tuning, tg*, adsr*, lpf*)
(
  find_note(note, note_buf, num_notes, 1);
  p >= end ? (
    p[] = note;
    num_notes += 1;
  );

  find_note(note, hold_buf, num_hold, 1);
  p < end ? (
    remove_buf(1);
    num_hold -= 1;
  );

  find_note(note, osc_buf, num_osc, osc_size);
  p >= end ? (
    set_tg(note);

    num_osc >= num_poly ? (
      p = osc_buf;
      num_poly > 1 ? (
        // Set oscillator/ADSR state to first played note (to mimic old
        // mono_synth global state behavior).
        tg.t = p[1];
        load_adsr(p);
        load_lpf(p);
      );

      remove_buf(osc_size);
      p = end;
      num_osc -= 1;
    ) : (
      // Don't reset oscillator on first note (old mono_synth behavior).
      num_osc > 0 ? tg.t = 0;
      adsr.adsr_reset();
      reset_lpf(tuning * tg.dt);
    );

    num_osc += 1;
    p[0] = note;
    p[1] = tg.t;
    p[2] = tg.dt;
  ) : num_poly > 1 ? (
    load_adsr(p);
    load_lpf(p);
  );

  adsr.adsr_a(vel);
  lpf.env.lp = lpf.m;

  store_adsr(p);
  store_lpf(p);
);

function note_off(note)
  // global(p, end, note_buf, num_notes, osc_buf, num_osc, osc_size, num_poly, note_prio, tg*, adsr*)
(
  find_note(note, note_buf, num_notes, 1);
  p < end ? (
    remove_buf(1);
    num_notes -= 1;
  );

  find_note(note, osc_buf, num_osc, osc_size);
  p < end ? (
    // Mono, last-note priority
    num_poly == 1 && num_notes > 0 && note_prio ? (
      note = note_buf[num_notes - 1];
      set_tg(note);
      p[0] = note;
      p[1] = tg.t;
      p[2] = tg.dt;
    ) : (
      // Release
      num_poly > 1 ? load_adsr(p);
      adsr.adsr_r();
      store_adsr(p);
    );
  );
);

function hold_note(note)
  // global(p, end, hold_buf, num_hold)
(
  find_note(note, hold_buf, num_hold, 1);
  p >= end ? (
    p[] = note;
    num_hold += 1;
  );
);

function release_notes()
  // global(hold_buf, num_hold)
  local(p)
(
  p = hold_buf;
  loop(num_hold,
    note_off(p[]);
    p += 1;
  );
  num_hold = 0;
);

function all_notes_off()
  // global(num_notes, num_hold, num_osc)
(
  num_notes = num_hold = num_osc = 0;
);

function pitch_bend(pitch_wheel)
  // global(pitch, pitch_range)
(
  pitch = pitch_range < 0.00001 ? 1 : 2^(pitch_wheel * pitch_range);
);

@slider

function adr(ms, lo, hi) ( max(lo, min(hi, ms)) * 0.001 );

function pan(gain, pos)
  // global(gain0, gain1)
(
  // REAPER default 0 dB pan law (thanks Justin!)
  // http://www.askjf.com/index.php?q=2342s

  pos *= 0.25*$pi;
  gain *= sqrt(2) * (1 - sqrt(0.5) * (1 / cos(pos) - 1));

  pos += 0.25*$pi;
  gain0 = cos(pos) * gain;
  gain1 = sin(pos) * gain;
);

pan(gain(slider1, min_inf), max(-100, min(100, slider20)) * 0.01);

adsr.adsr_seta(adr(slider4, 0, 5000));
adsr.adsr_setd(adr(slider5, 1, 15000));
adsr.adsr_sets(gain(slider6, min_inf));
adsr.adsr_setr(adr(slider7, 0, 5000));

tuning = 2^(slider2 / 1200);
wave = int(slider9);

// Limit pulse width for pulse, triangular pulse, modified square.
min_pw = wave == 6 || wave == 14 ? 0.10 : wave == 8 ? 0.01 : wave == 12 ? 0.20 : 0.0;
max_pw = wave == 6 ? 0.90 : wave == 8 ? 0.99 : 1.0;
pw = wave == 4 || wave == 9 ? 0.5 : max(min_pw, min(max_pw, slider10));

noise_mix = gain(slider14, -36.0);

lpf.n = slider16 >= 1.0 ? lpf.m : exp(max(0.0, slider16) * lpf.ln);
slider17 < 1 ? lpf.env.a = 1 : lpf.env.rc_sett(0.001 * min(15000, slider17));
lpf.q = max(0.01, min(4.0, slider18));

pitch_range = max(0.0, slider12) / 1200;
midi_ch.cache(max(0, min(16, int(slider22))) - 1) ? all_notes_off();
num_poly.cache(slider23 < 0.5 || slider23 >= 1.5 ? 1 : max_poly) ? all_notes_off();
note_prio.cache(slider23 >= 0.5) ? all_notes_off();
vel_range = max(0, min(100, slider24)) * 0.01;

lfo_range = max(0, min(100, slider27)) * 0.01;
lfo_range <= 0 ? (
  lfo_mod = 0;
) : wave == 6 || wave == 8 || wave >= 12 ? (
  lfo_mod = 1;
  lfo_range *= 0.5 * (max_pw - min_pw);
  pw = max(min_pw + lfo_range, min(max_pw - lfo_range, pw));
) : (
  lfo_mod = 2;
  lfo_range *= 0.5/12;
);
lfo_mod != 1 ? tg.poly_setpw(pw);
lfo_mod ? lfo.sin_setf(max(0.01, slider26));

@block

midiq.midiq_collect(midi_ch, 3|8|64);

@sample

while(midiq.midiq_remove() ? (
  midiq.msg1 &= 0xF0;

  // Note On
  midiq.msg1 == 0x90 && midiq.msg3 ? (
    note_on(midiq.msg2, (1.0 - vel_range) + vel_range * midiq.msg3 / 127);
  ) :

  // Note Off
  midiq.msg1 == 0x80 || midiq.msg1 == 0x90 ? (
    hold_pedal ? hold_note(midiq.msg2) : note_off(midiq.msg2);
  ) :

  // Pitch Wheel
  midiq.msg1 == 0xE0 ? (
    pitch_bend(((midiq.msg3 << 7 | midiq.msg2) - 8192) / (midiq.msg3 < 64 ? 8192 : 8191));
  ) :

  // Control Change
  midiq.msg1 == 0xB0 ? (

    // Damper Pedal (Sustain)
    midiq.msg2 == 64 ? (
      hold_pedal = midiq.msg3 >= 64;
      !hold_pedal ? release_notes();
    ) :

    // All Notes Off
    midiq.msg2 == 123 ? (
      all_notes_off();
    );
  );

  1; // while midiq.midiq_remove()
));

function sample_tg(t, dt)
  // global(adsr*, freq_mod, tg*, wave, white_noise, noise_mix)
  local(ph, out)
(
  adsr.state ? (
    noise_mix < 1.0 ? (
      // Correct full-wave rectified sine/triangular pulse phase.
      ph = wave == 2 ? 0.25 : wave == 14 ? 0.75 + 0.5 * tg.pw;
      ph > 0 ? tg.poly_sync(t - ph) : tg.t = t;

      tg.poly_setdt(freq_mod * dt);
      tg.poly_resetf();
      out = adsr.env * mix(tg_wave(wave), white_noise, noise_mix);

      ph > 0 ? tg.poly_sync(tg.t + ph);
      out;
    ) : (
      adsr.env * white_noise;
    );    
  );
  // 0.0 otherwise
);

function apply_lpf(in, dt)
  // global(lpf*, tuning)
  local(out)
(
  lpf.freq = lpf_freq(tuning * dt);
  lpf.freq.smooth();

  // Recalculate LPF coefficients only every 16 samples.
  lpf.skip <= 0 && lpf.freq.smooth != lpf.zdf.freq ? (
    lpf.zdf.freq = lpf.freq.smooth;
    lpf.zdf.zdf_lp(lpf.zdf.freq, lpf.q);
  );

  out = lpf.zdf.zdf_svf_lp(in);
  lpf.n < lpf.m ? out : in;
);

function adsr_lpf_off()
  // global(adsr*, lpf.zdf*)
(
  !adsr.state && lpf.zdf.s1 == 0 && lpf.zdf.s2 == 0;
);

freq_mod = pitch * tuning;
lfo_mod ? lfo_mod == 1 ? tg.poly_setpw(pw + lfo_range * lfo.sin_sin()) : freq_mod *= 2^(lfo.sin_sin() * lfo_range);

white_noise = noise_mix > 0.0 ? noise_gain * noise.lcg_white();
out = 0.0;

// Optimize for mono i.e. don't load/store oscillator/ADSR/filter state.
num_poly == 1 ? (
  num_osc ? (
    adsr.adsr_process();
    out = apply_lpf(sample_tg(tg.t, osc_buf[2]), osc_buf[2]);
    adsr_lpf_off() ? num_osc = 0;
  );
) :

/* num_poly > 1 ? */ (
  sus = adsr.s;

  set_buf(osc_buf, num_osc, osc_size);
  while(p < end ? (

    // Functions indexing memory seems to be slow, so manually inline them.
    /* load_adsr(p); */ adsr.state = p[3]; adsr.env = p[4]; adsr.scale = p[5]; adsr.s = p[6];
    adsr.s != sus ? adsr.adsr_sets(sus);
    adsr.adsr_process();
    /* store_adsr(p); */ p[3] = adsr.state; p[4] = adsr.env; p[5] = adsr.scale; p[6] = adsr.s;

    s = sample_tg(p[1], p[2]);
    p[1] = tg.t;

    /* load_lpf(p); */ lpf.env.lp = p[7]; lpf.freq.smooth = p[8]; lpf.zdf.g = p[9]; lpf.zdf.h = p[10]; lpf.zdf.s1 = p[11]; lpf.zdf.s2 = p[12];
    s = apply_lpf(s, p[2]);
    /* store_lpf(p); */ p[7] = lpf.env.lp; p[8] = lpf.freq.smooth; p[9] = lpf.zdf.g; p[10] = lpf.zdf.h; p[11] = lpf.zdf.s1; p[12] = lpf.zdf.s2;

    out += s;

    adsr_lpf_off() ? (
      remove_buf(osc_size);
      num_osc -= 1;
    ) : (
      p += osc_size;
    );
  ));
);

lpf.skip > 0 ? lpf.skip -= 1 : lpf.skip = 16 - 1;

spl0 += gain0.smooth() * out;
spl1 += gain1.smooth() * out;
