/**************************
         LeetDelay
Copyright (C) 2015 Stige T.
**************************/

desc:LeetDelay

slider6:0<0,1,1{Off,On}>-Tempo Delay
slider7:5<0,11,1{0.25,0.33,0.50,0.66,0.75,1.00,1.25,1.33,1.50,1.66,1.75,2.00}>-Tempo Multiplier [x]
slider8:250<1,2000,1>-Delay Time [ms]
slider9:0.5<-1,1,0.1>-Color
slider10:50<0,100,1>-Feedback [%]

slider20:0<-40,6,1>-Wet Mix [dB]
slider21:0<-40,6,1>-Dry Mix [dB]


EffectName: Leet Delay
VendorString: Stige T.
VendorVersion: 1000
UniqueId: 3798

@init

!"#define VAL(A) (A)" "//";

buildStr = "build 160218";

function delay_init(samples,index)
(
  this.len = samples;
  this.sloop = this.splay = samples * index;  
);

function delay_reset()
(
  memset(0,0,8388608);
);

function delay(input)
(
  this.sloop[this.sindex] = input;
  this.sindex += 1;
  this.sindex >= this.len ? this.sindex = 0;
  this.sloop[this.sindex];
);

function HFLF_init(freq)
instance(n0,weight)
(
  n0 = 0;
  weight = 1-exp(-2*$pi*freq/srate);
);

function HFcut(input)
instance(out,n0,weight)
(  
  out = (n0+=((input-n0)*weight));
);

function LFcut(input)
instance(out,n0,weight)
(
  out = input - (n0+=((input-n0)*weight));
);

function follower(input,att,rel)
instance (env,tmp) (
  tmp = input;
  (tmp > env) ? (
      env = att * (env - tmp) + tmp;
  ) : (
      env = rel * (env - tmp) + tmp;
  );
);

function SVF_init(freq,q)
instance(F1,Q1) (
  Q1 = 1/q;
  F1 = 2 * 3.14159 * freq/srate;
);

function SVF(in)
instance(D1,D2,F1,Q1,L,H,B,N) (
  L = D2 + F1 * D1;
  H = in - L - Q1*D1;
  B = F1 * H + D1;
  //N = H + L;
  D1 = B;
  D2 = L;
  B;
);

function samplesniffer(input) (
  !abs(input) ? (
    this.splCount <= 128 ? this.splCount += 1;
  ) : (
    this.splCount = 0;
  );
  this.splCount >= 128 ? this.flag = 0 : this.flag = 1;
);

function process_slider() (
  feedback = slider10 / 100;
  color = abs(slider9);
  cpol = slider9;
  wmix = 10^(slider20/20);
  dmix = 10^(slider21/20);

  slider7 == 0 ? tempo_mul = 0.25;
  slider7 == 1 ? tempo_mul = 0.33;
  slider7 == 2 ? tempo_mul = 0.5;
  slider7 == 3 ? tempo_mul = 0.66;
  slider7 == 4 ? tempo_mul = 0.75;
  slider7 == 5 ? tempo_mul = 1;
  slider7 == 6 ? tempo_mul = 1.25;
  slider7 == 7 ? tempo_mul = 1.33;
  slider7 == 8 ? tempo_mul = 1.50;
  slider7 == 9 ? tempo_mul = 1.66;
  slider7 == 10 ? tempo_mul = 1.75;
  slider7 == 11 ? tempo_mul = 2;
  
  tempo_delay = slider6;
  //drive = feedback/2.5 + 0.75;
  drive = (1 + ((1-color)*0.33));
  
  cpol >= 0 ? (
    colf0.SVF_init(675,0.5+color);
    colf1.SVF_init(625,0.5+color);
    colf2.SVF_init(1600,1);
    colf3.SVF_init(1800,1);
  ) : (
    colf0.SVF_init(300,0.5+color);
    colf1.SVF_init(350,0.5+color);
    colf2.SVF_init(1000,1);
    colf3.SVF_init(1200,1);  
  );
  
  !tempo_delay ? (
    delay_samples = slider8 / 1000 * srate;
    d0.delay_init(delay_samples-20,0);
    d1.delay_init(delay_samples,1);  
  );
  
  dirty = 1;
);

t = exp(-1/(srate*1));
tmp_tmp = 0;
sgn.fbctrl = 0;

delay_reset();
sgn.l.d = 0;
sgn.r.d = 0;

@slider

s06.checked = slider6;
s07.value = slider7;
s08.value = slider8;
s09.value = slider9;
s10.value = slider10;
s20.value = slider20;
s21.value = slider21;

process_slider();

@block

tmp_tmp != tempo ? (dirty = 1);

tempo_delay && dirty ? (
  delay_samples = srate / (tempo / 60);
  d0.delay_init((delay_samples-20) * tempo_mul,0);
  d1.delay_init(delay_samples * tempo_mul,1);
  tmp_tmp = tempo;
  dirty = 0;
);

@sample

samplesniffer(spl0) ? (
  sgn.l = spl0;
  sgn.r = spl1;
) : (
  sgn.l = 0;
  sgn.r = 0;
);

sgn.l.d = d0.delay(sgn.l + (sgn.l.d * feedback * sgn.fbctrl));
sgn.r.d = d1.delay(sgn.r + (sgn.r.d * feedback * sgn.fbctrl));
  
b0 = colf0.SVF(sgn.l.d);
b1 = colf1.SVF(sgn.r.d);
b2 = colf2.SVF(sgn.l.d);
b3 = colf3.SVF(sgn.r.d);

sgn.l.d = (b0 * color) + (sgn.l.d * (1-color)) + (b2 * color * 0.25);
sgn.r.d = (b1 * color) + (sgn.r.d * (1-color)) + (b3 * color * 0.25);

sgn.fbmax = max(max(abs(sgn.l.d),abs(sgn.r.d)),sgn.fbmax);

sgn.fbmax && (feedback > 0.5) ? (
  sgn.fbctrl = drive/(sgn.fbmax+1);
) : (
  sgn.fbctrl = 1;
);

sgn.fbmax = max(sgn.fbmax-0.000005,0);

sgn.l.d = min(max(sgn.l.d,-0.891),0.891);
sgn.r.d = min(max(sgn.r.d,-0.891),0.891);

spl0 = (-sgn.l.d * wmix) + (spl0 * dmix);
spl1 = (sgn.r.d * wmix) + (spl1 * dmix);

@gfx 500 250

function draw_hselector(x,y,w,h,f,t,s,d,title)
(
  
  // Change detecion
  this.value.temp != this.value ? (
    _sliderDirty = 1;
    this.value.temp = this.value;
  );

  // Frame
  gfx_r = gfx_g = gfx_b = 1;
  gfx_rect(x,y,w,h);
  gfx_r = gfx_g = gfx_b = 0.1;
  gfx_rect(x+1,y+1,w-2,h-2);
  
  // Knob parameters
  this.knob.w = 10;
  this.knob.hw = this.knob.w * 0.5;
  this.range = abs(f - t) + 0;
  
  // Mouse Logic
  mouse_x >= x && mouse_x <= x+w && mouse_y >= y && mouse_y <= y+h ? (
    !mouse_cap ? this.hasEntered = 1;
    mouse_cap ? this.hasClicked = 1;
    this.hasEntered && this.hasClicked ? this.canChange = 1;
  ) : (
    this.hasEntered = this.hasClicked = 0;
  );
  !mouse_cap ? this.canChange = 0;
  
  // Process
  this.canChange ? (
    this.knob.input = (mouse_x - x - this.knob.w - this.knob.hw);
    
    this.knob.input.normalized = this.knob.input / (w-this.knob.w-2);
    this.value.rect = this.range*this.knob.input.normalized;
    this.step = 1/s;
    this.value.rect = ceil(this.value.rect * this.step) / this.step;
    this.value = this.value.rect + f;
    
    mouse_cap & 4 ? this.value = d;
  );
  
  this.value = max(min(this.value,t),f);
  this.knob.pos = (this.range + this.value - t) / (this.range) * (w-this.knob.w-2);

  // Knob
  gfx_r = gfx_g = gfx_b = 0.5;
  gfx_rect(x+1+this.knob.pos,y+2,this.knob.w,h-4);
  gfx_r = gfx_g = gfx_b = 0.2;  
  gfx_rect(x+this.knob.hw+this.knob.pos,y+3,1,h-6);
  gfx_r = gfx_g = gfx_b = 0.8;  
  gfx_rect(x+this.knob.hw+1+this.knob.pos,y+3,1,h-6);

  // Title
  gfx_r = gfx_g = gfx_b = 0.8;
  this.str1 = title;
  gfx_measurestr(this.str1,this.str1.w,this.str1.h);
  gfx_x = x - this.str1.w - 5 ; gfx_y = y + (h * 0.5) - (this.str1.h * 0.5);
  gfx_drawstr(this.str1); 
  
  this.value;
);

function draw_hslider(x,y,w,h,f,t,s,d,unit,title)
(
  
  // Change detecion
  this.value.temp != this.value ? (
    _sliderDirty = 1;
    this.value.temp = this.value;
  );

  // Frame
  gfx_r = gfx_g = gfx_b = 1;
  gfx_rect(x,y,w,h);
  gfx_r = gfx_g = gfx_b = 0.1;
  gfx_rect(x+1,y+1,w-2,h-2);
  
  // Knob parameters
  this.knob.w = 10;
  this.knob.hw = this.knob.w * 0.5;
  this.range = abs(f - t);

  // Mouse Logic
  mouse_x >= x && mouse_x <= x+w && mouse_y >= y && mouse_y <= y+h && !this.disabled ? (
    !mouse_cap ? this.hasEntered = 1;
    mouse_cap ? this.hasClicked = 1;
    this.hasEntered && this.hasClicked ? this.canChange = 1;
  ) : (
    this.hasEntered = this.hasClicked = 0;
  );
  !mouse_cap ? (this.canChange = 0; this.init_x = 0;);
  
  // Process
  this.canChange ? (
    mouse_cap & 8 ? (
      !this.init_x ? this.init_x = mouse_x;
      this.knob.input = (this.init_x - x - this.knob.w) + (((mouse_x/this.init_x) - 1) * 10 + this.knob.hw);
    ) : (
      this.knob.input = (mouse_x - x - this.knob.hw);
      this.init_x = 0;
    );
    
    this.knob.input.normalized = this.knob.input / (w-this.knob.w-2);
    this.value.rect = this.range*this.knob.input.normalized;
    this.step = 1/s;
    this.value.rect = ceil(this.value.rect * this.step) / this.step;
    this.value = this.value.rect + f;
    
    mouse_cap & 4 ? this.value = d;
  );
  
  this.value = max(min(this.value,t),f);
  this.knob.pos = (this.range + this.value - t) / (this.range) * (w-this.knob.w-2);

  // Knob
  gfx_r = gfx_g = gfx_b = 0.5;
  gfx_rect(x+1+this.knob.pos,y+2,this.knob.w,h-4);
  gfx_r = gfx_g = gfx_b = 0.2;  
  gfx_rect(x+this.knob.hw+this.knob.pos,y+3,1,h-6);
  gfx_r = gfx_g = gfx_b = 0.8;  
  gfx_rect(x+this.knob.hw+1+this.knob.pos,y+3,1,h-6); 

  // Title
  gfx_r = gfx_g = gfx_b = 0.8;
  this.str1 = title;
  gfx_measurestr(this.str1,this.str1.w,this.str1.h);
  gfx_x = x - this.str1.w - 5 ; gfx_y = y + (h * 0.5) - (this.str1.h * 0.5);
  gfx_drawstr(this.str1);

  // Readout
  !this.hidereadout ? (
    gfx_r = gfx_g = gfx_b = 0.8;
    this.str2 = strcat(sprintf(#,"%.2f",this.value),unit);
    gfx_measurestr(this.str2,this.str2.w,this.str2.h);
    gfx_x = x + w + 5; gfx_y = y + (h * 0.5) - (this.str2.h * 0.5);
    gfx_drawstr(this.str2);
  );
    
  this.value;
);

function draw_chkbox(x,y,s,title)
(

  // Change detecion
  this.checked.temp != this.checked ? (
    _sliderDirty = 1;
    this.checked.temp = this.checked;
  );

  gfx_r = gfx_g = gfx_b = 0.7;
  gfx_measurestr(title,this.str.w,this.str.h);
  gfx_x = x - this.str.w - 5; gfx_y = y + (s * 0.5) - (this.str.h * 0.5);
  gfx_drawstr(title);

  // Mouse Logic
  mouse_x >= x && mouse_x <= x+s && mouse_y >= y && mouse_y <= y+s && !this.disabled ? (
    !mouse_cap ? this.hasEntered = 1;
    mouse_cap ? this.hasClicked = 1;
    this.hasEntered && this.hasClicked ? this.canChange = 1;
  ) : (
    this.hasEntered = this.hasClicked = this.canChange = 0;
  );
  !mouse_cap ? (this.canChange = 0;);

  this.canChange ? (
    this.checked = 1-this.checked;
    this.hasEntered = this.hasClicked = this.canChange = 0;
  );

  // Frame
  gfx_r = gfx_g = gfx_b = 1;
  gfx_rect(x,y,s,s);
  gfx_r = gfx_g = gfx_b = 0.1;
  gfx_rect(x+1,y+1,s-2,s-2);
  
  // Checked
  this.checked ? (
    gfx_r = 0.1; gfx_g = 0.75; gfx_b = 1;
    gfx_rect(x+2,y+2,s-4,s-4);
  );
    
  this.checked; 
);

// Title & background

gfx_r = 0.1; gfx_g = 0.12; gfx_b = 0.12;
gfx_rect(0,0,500,250);
gfx_setfont(1,"Arial",24,'b');
gfx_r = 1; gfx_g = 1; gfx_b = 1;
gfx_x = 10; gfx_y = 10;
gfx_drawstr("Leet Delay");

gfx_setfont(1,"Arial",16);

// Sliders

slider_align = 150;
slider_y = 50;

  slider6 = s06.draw_chkbox(slider_align,slider_y+30,20,"Tempo Delay");
tempo_delay ? (
  slider7 = s07.draw_hselector(slider_align,slider_y,200,20,0,11,1,5,"Tempo Multiplier");
  gfx_x = slider_align + 205;
  slider7 == 0 ? gfx_drawstr("0.25x");
  slider7 == 1 ? gfx_drawstr("0.33x");
  slider7 == 2 ? gfx_drawstr("0.50x");
  slider7 == 3 ? gfx_drawstr("0.66x");
  slider7 == 4 ? gfx_drawstr("0.75x");
  slider7 == 5 ? gfx_drawstr("1.00x");
  slider7 == 6 ? gfx_drawstr("1.25x");
  slider7 == 7 ? gfx_drawstr("1.33x");
  slider7 == 8 ? gfx_drawstr("1.50x");
  slider7 == 9 ? gfx_drawstr("1.66x");
  slider7 == 10 ? gfx_drawstr("1.75x");
  slider7 == 11 ? gfx_drawstr("2.00x");  
) : (
  slider8 = s08.draw_hslider(slider_align,slider_y,200,20,1,2000,1,250,"ms","Delay Time");
);
  slider9 = s09.draw_hslider(slider_align,slider_y+60,200,20,-1,1,0.1,0.5,"","Color");
  slider10 = s10.draw_hslider(slider_align,slider_y+90,200,20,0,100,1,50,"%","Feedback");
  slider20 = s20.draw_hslider(slider_align,slider_y+120,200,20,-40,6,1,0,"dB","Wet Mix");
  slider21 = s21.draw_hslider(slider_align,slider_y+150,200,20,-40,6,1,0,"dB","Dry Mix");  

_sliderDirty ? (
  process_slider();
  _sliderDirty = 0;
);

gfx_x = 440; gfx_y = 230;
gfx_a = 0.5;
gfx_setfont(1,"Arial",11);
gfx_drawstr(buildStr);
