desc:Waveform generator
// Copyright (C) 2017-2022 Theo Niessink
// License: LGPL - http://www.gnu.org/licenses/gpl.html

slider1:0<0,50,1{Sine,Cosine,Triangle,Square,Rectangle PW,Saw,Ramp,Mod Triangle PW,Mod Square PW,HW Rect Sine,HW Rect Sine PW,FW Rect Sine,Pulse Sine,Saw Pulse,Tri Pulse PW,HW Rect Saw PW,Alt Sine,Camel Sine,Alt Camel Sine,Bipolar Camel Sine,Bipolar Sine^2,Parabola,Circle,Cycloid,Trapezoid,Trapezoid PW,Round Square FC,Round Rectangle PW/FC,LP Square FC,LP Square Approx 2,LP Square Approx 3,Integrated LP Square 2,Integrated LP Square 3,HP Square FC,Hyper Triangle,Integrated Saw,Cubic Saw,Sine Saw,HP Saw FC,HP Saw Approx,Logit Approx,Square Min Fund,Saw Min Fund,Step Sine N,Step Triangle N,Step Saw N,Hard Sync Saw FC,88 8000 000,Staircase,Staircase PW,Uneven Staircase PW}>Waveform
slider2:440.0<20.0,20000.0,1.0>Freq (Hz)
slider3:0.5<0.0,1.0,0.01>Pulse Width
slider4:1.0<0.0,10.0,0.01>Freq Cutoff
slider5:4<1,25,1>Steps [N]
slider6:1<0,2,1{Naive,Fourier Series,PolyBLEP}>Mode
slider7:0.0<-150.0,24.0,0.1>Dry (dB)
slider8:-24.0<-150.0,24.0,0.1>Wet (dB)

import Tale/lfo.jsfx-inc

import Tale/poly_blep.jsfx-inc
import Tale/rc_filter.jsfx-inc

import Tale/fft_real_synth.jsfx-inc
import Tale/fourier_series.jsfx-inc
import Tale/wavetable.jsfx-inc

@init

half2 = 10;
trip = 14;
lpsqr = 28;
hpsqr = 33;
hpsaw = 38;

ofs_tbl = four.four_init(0, 2048);
pw_tbl = ofs_tbl + 51;

ofs_tbl[10] = +0.25; pw_tbl[10] = -0.5;
ofs_tbl[11] = -0.25;
ofs_tbl[14] = +0.25; pw_tbl[14] = -0.5;
ofs_tbl[15] = +0.5;  pw_tbl[15] = -1;
ofs_tbl[23] = -0.25;

gfx_clear = -1;
gfx_ext_retina = 1;

function lfo_wave(wave)
(
  wave == 0 ? this.lfo_sin() :
  wave == 1 ? this.lfo_cos() :
  wave == 2 ? this.lfo_tri() :
  wave == 3 ? this.lfo_sqr() :
  wave == 4 ? this.lfo_rect() :
  wave == 5 ? this.lfo_saw() :
  wave == 6 ? this.lfo_ramp() :

  wave == 7 ? this.lfo_tri2() :
  wave == 8 ? this.lfo_sqr2() :

  wave == 9 ? this.lfo_half() :
  wave == 10 ? this.lfo_half2() :
  wave == 11 ? this.lfo_full() :
  wave == 12 ? this.lfo_sinp() :
  wave == 13 ? this.lfo_sawp() :
  wave == 14 ? this.lfo_trip() :
  wave == 15 ? this.lfo_hwsaw() :
  wave == 16 ? this.lfo_alt() :
  wave == 17 ? this.lfo_camel() :
  wave == 18 ? this.lfo_camela() :
  wave == 19 ? this.lfo_camel2() :

  wave == 20 ? this.lfo_sin2() :
  wave == 21 ? this.lfo_para() :
  wave == 22 ? this.lfo_circ() :
  wave == 23 ? this.lfo_arc() :
  wave == 24 ? this.lfo_trap() :
  wave == 25 ? this.lfo_trap2() :
  wave == 26 ? this.lfo_rsqr() :
  wave == 27 ? this.lfo_rrect() :

  wave == 28 ? this.lfo_lpsqr() :
  wave == 29 ? this.lfo_lpsqr2() :
  wave == 30 ? this.lfo_lpsqr3() :
  wave == 31 ? this.lfo_intlpsqr2() :
  wave == 32 ? this.lfo_intlpsqr3() :
  wave == 33 ? this.lfo_hpsqr() :
  wave == 34 ? this.lfo_hyptri() :
  wave == 35 ? this.lfo_intsaw() :
  wave == 36 ? this.lfo_cubsaw() :
  wave == 37 ? this.lfo_sinsaw() :
  wave == 38 ? this.lfo_hpsaw() :
  wave == 39 ? this.lfo_hpsaw6() :
  wave == 40 ? this.lfo_logit3() :

  wave == 41 ? this.lfo_sqrm1() :
  wave == 42 ? this.lfo_sawm1() :

  wave == 43 ? this.lfo_sinn() :
  wave == 44 ? this.lfo_trin() :
  wave == 45 ? this.lfo_sawn() :
  wave == 46 ? this.lfo_hssaw() :

  wave == 47 ? this.lfo_ham() :
  wave == 48 ? this.lfo_stairs() :
  wave == 49 ? this.lfo_stairs3() :
  wave == 50 ? this.lfo_stairs2() :

  ( this.lfo_inc(); wave < 0 ? wave : 0; );
);

function poly_wave(wave)
(
  wave == 0 ? this.poly_sin() :
  wave == 1 ? this.poly_cos() :
  wave == 2 ? this.poly_tri() :
  wave == 3 ? this.poly_sqr() :
  wave == 4 ? this.poly_rect() + this.lfo_rect_dc() :
  wave == 5 ? this.poly_saw() :
  wave == 6 ? this.poly_ramp() :

  wave == 7 ? this.poly_tri2() :
  wave == 8 ? this.poly_sqr2() :

  wave == 9 ? this.poly_half() + lfo_half_dc() :
  wave == 10 ? this.poly_half2() + this.lfo_half2_dc() :
  wave == 11 ? this.poly_full() + lfo_full_dc() :
  wave == 12 ? this.poly_sinp() + lfo_sinp_dc() :
  wave == 13 ? this.poly_sawp() + lfo_sawp_dc() :
  wave == 14 ? this.poly_trip() + this.lfo_trip_dc() :
  wave == 15 ? this.poly_hwsaw() + this.lfo_hwsaw_dc() :
  wave == 16 ? this.poly_alt() :
  wave == 17 ? this.poly_camel() + lfo_camel_dc() :
  wave == 18 ? this.poly_camela() + lfo_camela_dc() :
  wave == 19 ? this.poly_camel2() :

  wave == 20 ? this.poly_sin2() :
  wave == 21 ? this.poly_para() :

  wave == 22 ? this.poly_circ() :
  wave == 23 ? this.poly_arc() :
  wave == 24 ? this.poly_trap() :
  wave == 25 ? this.poly_trap2() : /*
  wave == 26 ? this.lfo_rsqr() :
  wave == 27 ? this.lfo_rrect() : */

  wave == 28 ? rc.rc_lp(this.poly_sqr()) :
  wave == 29 ? this.poly_lpsqr2() :
  wave == 30 ? this.poly_lpsqr3() :
  wave == 31 ? this.poly_intlpsqr2() :
  wave == 32 ? this.poly_intlpsqr3() :
  wave == 33 ? rc.rc_hp_precise(this.poly_sqr()) :
  wave == 34 ? this.poly_hyptri() + lfo_hyptri_dc() :
  wave == 35 ? this.poly_intsaw() + lfo_intsaw_dc() :
  wave == 36 ? this.poly_cubsaw() :
  wave == 37 ? this.poly_sinsaw() :
  wave == 38 ? rc.rc_hp_precise(this.poly_saw()) + this.lfo_hpsaw_dc() :
  wave == 39 ? this.poly_hpsaw6() + lfo_hpsaw6_dc()  :
  wave == 40 ? this.poly_logit3() :

  wave == 41 ? this.poly_sqrm1() :
  wave == 42 ? this.poly_sawm1() :

  wave == 43 ? this.poly_sinn() :
  wave == 44 ? this.poly_trin() :
  wave == 45 ? this.poly_sawn() :
  wave == 46 ? this.poly_hssaw() + this.lfo_hssaw_dc() :

  wave == 47 ? this.poly_ham() :
  wave == 48 ? this.poly_stairs() :
  wave == 49 ? this.poly_stairs3() + this.lfo_stairs3_dc() :
  wave == 50 ? this.poly_stairs2() + this.lfo_stairs2_dc() :

  ( this.poly_inc(); wave < 0 ? wave : 0; );
);

function four_wave(wave)
(
  wave == 0 ? this.four_sin() :
  wave == 1 ? this.four_cos() :
  wave == 2 ? this.four_tri() :
  wave == 3 ? this.four_sqr() :
  wave == 4 ? this.four_rect() :
  wave == 5 ? this.four_saw() :
  wave == 6 ? this.four_ramp() :

  wave == 7 ? this.four_tri2() :
  wave == 8 ? this.four_sqr2() :

  wave == 9 ? this.four_half() :
  wave == 10 ? this.four_half2() :
  wave == 11 ? this.four_full() :
  wave == 12 ? this.four_sinp() :
  wave == 13 ? this.four_sawp() :
  wave == 14 ? this.four_trip() :
  wave == 15 ? this.four_hwsaw() :
  wave == 16 ? this.four_alt() :
  wave == 17 ? this.four_camel() :
  wave == 18 ? this.four_camela() :
  wave == 19 ? this.four_camel2() :

  wave == 20 ? this.four_sin2() :
  wave == 21 ? this.four_para() :
  wave == 22 ? this.four_circ() :
  wave == 23 ? this.four_arc() :
  wave == 24 ? this.four_trap() :
  wave == 25 ? this.four_trap2() : /*
  wave == 26 ? this.lfo_rsqr() :
  wave == 27 ? this.lfo_rrect() : */

  wave == 28 ? this.four_lpsqr() :
  wave == 29 ? this.four_lpsqr2() :
  wave == 30 ? this.four_lpsqr3() :
  wave == 31 ? this.four_intlpsqr2() :
  wave == 32 ? this.four_intlpsqr3() :
  wave == 33 ? this.four_hpsqr() :
  wave == 34 ? this.four_hyptri() :
  wave == 35 ? this.four_intsaw() :
  wave == 36 ? this.four_cubsaw() :
  wave == 37 ? this.four_sinsaw() :
  wave == 38 ? this.four_hpsaw() :
  wave == 39 ? this.four_hpsaw6() :
  wave == 40 ? this.four_logit3() :

  wave == 41 ? this.four_sqrm1() :
  wave == 42 ? this.four_sawm1() :

  wave == 43 ? this.four_sinn() :
  wave == 44 ? this.four_trin() :
  wave == 45 ? this.four_sawn() :
  wave == 46 ? this.four_hssaw() :

  wave == 47 ? this.four_ham() :
  wave == 48 ? this.four_stairs() :
  wave == 49 ? this.four_stairs3() :
  wave == 50 ? this.four_stairs2() :

  ( memset(this.coef, 0, this.size); );
);

@slider

wave = slider1|0;
freq = max(slider2, 0);

pw = min(max(0, slider3), 1);
fc = max(slider4, 0);
n = max(slider5|0, 1);

mode = slider6|0;

dry = exp(log(10)/20 * slider7);
wet = exp(log(10)/20 * slider8);

lfo.lfo_setf(freq);
lfo.lfo_setpw(pw);
lfo.lfo_setfc(fc * freq);
lfo.lfo_setn(n);

poly.poly_setf(freq);
poly.poly_setpw(pw);
poly.poly_setf2(fc * freq);
poly.poly_setn(n);

four.four_setf(freq);
four.four_setpw(pw);
four.four_setfc(fc * freq);
four.four_setn(n);

lp = wave == lpsqr;
hp = wave == hpsqr;

mode == 0 ? (
  lp || hp ? (
    gain = exp(-$pi * fc);
    gain = (1 - gain) / (1 + gain) + hp;
  ) : (
    gain = 1;
  );
) :

mode == 1 ? (
  four.four_update();
  four.four_wave(wave);
  four.four_ifft();
) :

mode == 2 ? (
  (wave == half2 || wave == trip) && pw < poly.dt * 2 ? wave = -1;
);

rc.rc_setf_precise(freq * fc);
!lp || rc.a <= 0 ? rc.lp = 0;
!(hp |= wave == hpsaw) ? rc.hp = rc.in = 0;

gfx_update = 1;

@sample

poly.t = four.t = lfo.t;

mode < 2 ? (
  mode < 1 ? (
    out = lfo.lfo_wave(wave) * gain;
  ) : (
    out = four.wave_spline5();
    lfo.t = four.t;
  );
  lp ? rc.rc_lp(out) :
  hp ? rc.rc_hp_precise(out);
) : (
  out = poly.poly_wave(wave);
  lfo.t = poly.t;
);

out *= wet;
spl0 = spl0 * dry + out;
spl1 = spl1 * dry + out;

@gfx 440 330

gfx_w != old_w || gfx_h != old_h ? (
  old_w = gfx_w;
  old_h = gfx_h;
  gfx_update = 1;
);

gfx_update ? (
  gfx_update = 0;

  gfx_r = 0; gfx_g = 1/15; gfx_b = 2/45; gfx_a = 1;
  gfx_x = gfx_y = 0;
  gfx_rectto(gfx_w, gfx_h);

  gfx_g = 1/3; gfx_b = 2/9;
  gfx_x = 8;
  loop(3,
    gfx_y = 8;
    gfx_lineto(gfx_x, gfx_h - 9);
    gfx_x += (gfx_w - 17) / 2;
  );
  gfx_y = 8;
  loop(3,
    gfx_x = 8;
    gfx_lineto(gfx_w - 9, gfx_y);
    gfx_y += (gfx_h - 17) / 2;
  );

  gfx_g = 1/6; gfx_b = 1/9;
  gfx_x = 8 + (gfx_w - 17) / 4;
  loop(2,
    gfx_y = 8;
    gfx_lineto(gfx_x, gfx_h - 9);
    gfx_x += (gfx_w - 17) / 2;
  );
  gfx_y = 8 + (gfx_h - 17) / 4;
  loop(2,
    gfx_x = 8;
    gfx_lineto(gfx_w - 9, gfx_y);
    gfx_y += (gfx_h - 17) / 2;
  );

  gfx_lfo.lfo_setf(srate / (gfx_w - 18));
  gfx_lfo.lfo_setpw(pw);
  gfx_lfo.lfo_setn(n);
  gfx_lfo.lfo_setfc(fc * gfx_lfo.dt * srate);
  gfx_lfo.lfo_sync(-0.5/(gfx_w - 18) + ofs_tbl[wave] + pw_tbl[wave] * pw);

  gfx_r = 0.25; gfx_g = 1; gfx_b = 5/6;
  x = 0;
  loop(gfx_w - 16,
    h = (0.5 - 0.5 * gfx_lfo.lfo_wave(wave)) * (gfx_h - 17);
    x > 0 ? (
      gfx_ext_retina >= 1.5 ? (
        gfx_a *= 0.5;
        gfx_x = (tmp_x = gfx_x) + 1;
        gfx_y = (tmp_y = gfx_y) + 1;
        gfx_lineto(9 + x, 9 + h, 1);
        gfx_x = tmp_x;
        gfx_y = tmp_y + 1;
        gfx_lineto(8 + x, 9 + h, 1);
        gfx_x = tmp_x + 1;
        gfx_y = tmp_y;
        gfx_lineto(9 + x, 8 + h, 1);
        gfx_x = tmp_x;
        gfx_y = tmp_y;
        gfx_a *= 2;
      );
      gfx_lineto(8 + x, 8 + h, 1);
    ) : (
      gfx_x = 8 /* + x */;
      gfx_y = 8 + h;
    );
    x += 1;
  );
);
