desc:Simple mono synth
// (c) Theo Niessink 2012, 2013
// License: GPL - http://www.gnu.org/licenses/gpl.html

slider1:-6.0<-120.0,24.0,1.0>Volume (dB)
slider2:0.0<-1200.0,1200.0,1.0>Tuning (cent)
slider4:3<0,5000,1>Attack (ms)
slider5:1000<1,15000,1>Decay (ms)
slider6:0.0<-120.0,24.0,1.0>Sustain (dB)
slider7:8<0,5000,1>Release (ms)
slider9:7<0,11,1{Sine,Half-wave rectified sine,Full-wave rectified sine,Triangle,Trapezoid,Square,Pulse,Sawtooth,Modified triangle,Triangular pulse,Hammond,Staircase}>Waveform
slider10:0.30<0.0,1.0,0.01>Pulse Width
slider12:200.0<0.0,1200.0,1.0>Pitch Wheel Range (cent)
slider14:-36.0<-36.0,0.0,1.0>White Noise (dB)
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
slider20:0<-100,100,1>Pan (%)
slider22:0<0,16,1{Any,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>MIDI Channel

out_pin:Synth output
out_pin:Synth output

import mono_synth.jsfx-inc
import adsr.jsfx-inc
import rc_filter.jsfx-inc
import zdf_filter.jsfx-inc
import poly_blep.jsfx-inc
import noise_generator.jsfx-inc


@init

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

gain0.rc_set(0.0033);
lpf.rc.a = gain1.a = gain0.a;

function set_freq()
(
  tuned_freq = synth.freq / synth.pitch;
  tg.poly_setf(synth.freq);
  lpf.env.a <= 0 ? lpf.freq = lpf.n * tuned_freq;
);

function adsr_a()
(
  adsr.adsr_a(synth.velocity);
  lpf.env.lp = lpf.m;
);


@slider

function amp(db, inf) ( db <= inf ? 0.0 : 10^(db / 20); );
function adr(ms, lo, hi) ( 0.001 * max(lo, min(hi, ms)); );

function pan(gain, pos)
(
  // 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.in = cos(pos) * gain;
  gain1.in = sin(pos) * gain;
);

pan(amp(slider1, -120.0), max(-100, min(100, slider20)) * 0.01);

adsr.adsr_seta(adr(slider4, 0, 5000));
adsr.adsr_setd(adr(slider5, 1, 15000));
adsr.adsr_sets(amp(slider6, -120.0));
adsr.adsr_setr(adr(slider7, 0, 5000));

// Don't call synth_setf() here, instead wait for synth_set_pitch() below to
// update the note frequency.
synth.tuning = 440 * 2^(slider2 / 1200);

slider9|0 != wave ? (
  wave = slider9|0;
  tg.poly_resetf();
  // Resync leaky integrator(s)
  tg.poly_leaky_reset();
);

// Pulse
wave == 6 ? tg.poly_setpw(max(0.10, min(0.90, slider10))) :
// Modified triangle
wave == 8 ? tg.poly_setpw(max(0.01, min(0.99, slider10))) :
// Triangular pulse
wave == 9 ? tg.poly_setpw(0.5);

noise = amp(slider14, -36.0);

lpf.n = slider16 >= 1.0 ? lpf.m : exp(max(0.0, slider16) * lpf.ln);
slider17 < 1 ? lpf.env.a = 0 : lpf.env.rc_sets(0.001 * min(15000, slider17));

synth.synth_set_pitch(max(0.0, slider12));
set_freq();

lpf.q = max(0.01, min(4.0, slider18));
lpf.zdf.zdf_lp(lpf.rc.lp, lpf.q);

slider22|0 != synth.ch ? (
  synth.synth_setch(slider22|0);
  synth.synth_all_notes_off();
);


@sample

synth.synth_midi();

gain0.rc_lp(gain0.in);
gain1.rc_lp(gain1.in);

synth.note_change ? synth.note_on ? adsr_a() : adsr.adsr_r();
!adsr.adsr_process() ? s = 0.0 : (

  synth.synth_pitch() || synth.synth_note() ? set_freq();

  noise < 1.0 ? (
    s = wave == 7  ? tg.poly_saw():
        wave == 6  ? tg.poly_rect():
        wave == 5  ? tg.poly_sqr():
        wave == 4  ? tg.poly_trap():
        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  ? tg.poly_trip():
        wave == 10 ? tg.poly_ham():
      /*wave == 11*/ tg.poly_stairs();
    noise > 0.0 ? s = noise * (noise.lcg_white()) + (1.0 - noise) * s;
  ) : (
    s = noise.lcg_white();
  );

  s *= adsr.env;
);

lpf.env.a > 0 ? lpf.freq = lpf.env.rc_lp(lpf.n) * tuned_freq;
lpf.rc.rc_lp(lpf.freq);

// Recalculate LPF coefficients only every 16 samples
lpf.skip > 0 ? lpf.skip -= 1 : (
  lpf.skip = 16 - 1;

  lpf.rc.lp != lpf.zdf.freq ? (
    lpf.zdf.freq = lpf.rc.lp;
    lpf.zdf.zdf_lp(lpf.rc.lp, lpf.q);
  );
);

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

spl0 += gain0.lp * s;
spl1 += gain1.lp * s;
