/*
'delay' (slider7):
slider range should fit to your project sample rate.
for 44100Hz the range is 4410 to 44100.
for 48000Hz the range is 4800 to 48000. etc.

'clock offset' (slider12) should be changed, too.
max offset should be set to your project samplerate.

(if you uncomment the 'xxx' lines in the code,
you can have bigger max values for these two sliders
(so the range can fit to any samplerate....
adjustments can only be made within
the project samplerate range though...))

'clock' sliders define the amount of samplerate reduction
when the delay time changes.
set 'clock offset' to max value for clean feedbacks.
(no samplerate reduction when delay time changes...)
*/

slider1:0.8<0,1,0.01>feedback
slider2:0.01<0,1,0.01>lfo depth
slider3:0.65<0,1,0.01>lfo speed
slider4:0.45<0,1,0.01>wet mix
slider5:0.5<0,1,0.01>output

slider6:9<0,10,1{64,128,256,512,1024,2048,4096,8192,16384,32768,65536}>stages
slider7:14860<4410,44100,1>>>> delay
slider8:30<0,100,1>hp filter
slider9:0.5<0,1,0.01>hp res
slider10:80<0,100,1>lp filter
slider11:0.5<0,1,0.01>lp res

slider12:0<0,44100,1>clock offset
slider13:1<0,1,0.001>clock scale
slider14:1<0.8,1.2,0.01>--clock curve

slider15:0<0,1,0.01>hiss

in_pin:L in
in_pin:R in
out_pin:L out
out_pin:R out

@init
ext_noinit = 1;
size = 65536; //set max delay time at max sample rate
buffer = 4096;
memset(buffer, 0, size);

ipos = 0;
env = 0;
phi = 0;
dlbuf = 0;

ddl = 0;
k = 0;

twopi = 2*$pi;
twopi2 = twopi*100;

fs = srate;
(fs < 8000) ? fs = 44100; //??? bug somewhere!

// resample
pos = 0;
nn_val = 0; // For nearest-neighbour interpolation

// filter
mX10 = mX20 = mY10 = mY20 = 0;
mX11 = mX21 = mY11 = mY21 = 0;

@slider
slider7 = floor(slider7);
//slider7 > fs ? slider7 = fs; //xxx
//slider7 < fs*0.1 ? slider7 = fs*0.1; //xxx
slider12 = floor(slider12);
//slider12 > fs ? slider12 = fs; //xxx

(old_s8 != slider8 && slider8 > slider10) ? slider10 = slider8;
(old_s10 != slider10 && slider10 < slider8) ? slider8 = slider10;
old_s8 = slider8;
old_s10 = slider10;

fParam1 = slider1; //feedback
fParam2 = slider2; //lfo depth
fParam3 = slider3; //lfo speed
fParam4 = slider4; //wet mix
fParam5 = slider5; //output

fParam6 = slider6; //stages
fParam7 = slider7; //delay
fParam8 = slider8; //hp filter
fParam9 = slider9; //hp res
fParam10 = slider10; //lp filter
fParam11 = slider11; //lp res
fParam12 = slider12; //clock offset
fParam13 = slider13; //clock scale
fParam14 = slider14; //clock curve
fParam15 = slider15; //hiss

// stages
bits = (fParam6+2|0);
bits<0 ? bits=0 : bits>12 ? bits=12;
stages = 2^(bits+4);

// delay
delay = max(min(fParam7,fs),fs*0.1|0);

del = (stages/fs)*delay|0;
mod = 0.49 * fParam2 * del; //mod = 0.049 * fParam2 * del;

fbk = abs(2.2 * fParam1 - 1.1);
(fParam1>0.5) ? (rel=0.9997; ):( rel=0.8; ); //limit or clip

wet = 1 - fParam4;
wet = fParam5 * (1 - wet * wet); //-3dB at 50% mix
dry = fParam5 * 2 * (1 - fParam4 * fParam4);

dphi = (twopi2 * pow(10, 3 * fParam3 - 2) / fs); //100-sample steps

// clock
clock = max(min((((delay ^ fParam14 ) * fParam13 ) + fParam12)|0,fs),0);

// resample
samples = fs/clock;

// filter hp
sx0 = 16+fParam8*1.20103;
cx0 = floor(exp(sx0*log(1.059))*8.17742);
cutoff0 = 2 * cx0 / fs;
resx0 = (0.5 * fParam9) - 0.25;
res0 = pow(10, -resx0);
kx0 = 0.5 * res0 * sin($pi * cutoff0);
c10 = 0.5 * (1 - kx0) / (1 + kx0);
c20 = (0.5 + c10) * cos($pi * cutoff0);
c30 = (0.5 + c10 + c20) * 0.25;

mA00 = 2 * c30;
mA10 = -4 * c30;
mA20 = 2 * c30;
mB10 = -2 * c20;
mB20 = 2 * c10;

// filter lp
sx1 = 16+fParam10*1.20103;
cx1 = floor(exp(sx1*log(1.059))*8.17742);
cutoff1 = 2 * cx1 / fs;
resx1 = (0.5 * fParam11) - 0.25;
res1 = pow(10, -resx1);
kx1 = 0.5 * res1 * sin($pi * cutoff1);
c11 = 0.5 * (1 - kx1) / (1 + kx1);
c21 = (0.5 + c11) * cos($pi * cutoff1);
c31 = (0.5 + c11 - c21) * 0.25;

mA01 = 2 * c31;
mA11 = 4 * c31;
mA21 = 2 * c31;
mB11 = -2 * c21;
mB21 = 2 * c11;

// noise
hiss = fParam15 * 0.05;

@sample
dl=dlbuf;
db=dlbuf;
i=ipos;

a = spl0;
b = spl1;
c = (spl0+spl1)*0.5;
noise = (lp+=(((rand(2)-1)-lp) * 0.02)) * hiss; //white noise lp-filtered ~140Hz

// resample using NN interpolation
(pos % floor(samples) > 0) ? (
c = nn_val;
pos += 1;
(pos >= fs) ? (pos = 0);
) : (
nn_val = c;
pos += 1;
);

(k==0) ? //update delay length at slower rate (could be improved!)
(
db += 0.01 * (del - db - mod - mod * sin(phi)); //smoothed delay+lfo
ddl = 0.01 * (db - dl); //linear step
phi += dphi;
(phi>twopi) ? phi-=twopi;
k = 100;
);
k -= 1;
dl += ddl; //lin interp between points

i -= 1;
(i<0) ? i = size; //delay positions

l = dl|0;
tmp = dl - l; //remainder

l += i;
(l>size) ? l -= (size+1);

ol = buf[buffer + l]; //delay output

l += 1;
(l>size) ? l = 0; 
ol += tmp * (buf[buffer + l] - ol); //lin interp

ol += noise;

tmp = c + fbk * ol; //mix input (Mono!) & feedback

// filter hp
in0 = tmp;
out0 = mA00*in0 + mA10*mX10 + mA20*mX20 - mB10*mY10 - mB20*mY20;
mX20 = mX10;
mX10 = in0;
mY20 = mY10;
mY10 = out0;
tmp = out0;

// filter lp
in1 = tmp;
out1 = mA01*in1 + mA11*mX11 + mA21*mX21 - mB11*mY11 - mB21*mY21;
mX21 = mX11;
mX11 = in1;
mY21 = mY11;
mY11 = out1;
tmp = out1;

g=(tmp<0) ? -tmp : tmp; //simple limiter
env *= rel;
(g>env) ? env = g;
(env>1) ? tmp /= env;

buf[buffer + i] = tmp; //delay input

ol *= wet; //wet

spl0 = dry * a + ol; //dry
spl1 = dry * b + ol;

ipos = i;
dlbuf = dl;

@gfx 0 220
gfx_r=0; gfx_g=0.9; gfx_b=0; gfx_a=1;
gfx_setfont(1,"Arial", 16);

gfx_x =20; gfx_y =10;  gfx_printf("%.0f",del * 1000 / fs);
gfx_x =80; gfx_y =10;  gfx_printf("ms");
gfx_x =160; gfx_y =10;  gfx_printf("Delay");

gfx_x =20; gfx_y =30;  gfx_printf("%.0f",220 * fParam1 - 110);
gfx_x =80; gfx_y =30;  gfx_printf("Sat <> Lim");
gfx_x =160; gfx_y =30;  gfx_printf("Feedback");

gfx_x =20; gfx_y =50;  gfx_printf("%.0f",100 * fParam2);
gfx_x =80; gfx_y =50;  gfx_drawchar($'%');
gfx_x =160; gfx_y =50;  gfx_printf("LFO Depth");

gfx_x =20; gfx_y =70;  gfx_printf("%.2f",pow(10, 2 - 3 * fParam3));
gfx_x =80; gfx_y =70;  gfx_printf("sec");
gfx_x =160; gfx_y =70;  gfx_printf("LFO Speed");

gfx_x =20; gfx_y =90;  gfx_printf("%.0f",100 * fParam4);
gfx_x =80; gfx_y =90;  gfx_drawchar($'%');
gfx_x =160; gfx_y =90;  gfx_printf("Wet Mix");

gfx_x =20; gfx_y =110;  gfx_printf("%.1f",20 * log10(2 * fParam5));
gfx_x =80; gfx_y =110;  gfx_printf("dB");
gfx_x =160; gfx_y =110;  gfx_printf("Output");

gfx_r=0.2; gfx_g=0.8; gfx_b=1; gfx_a=1;

gfx_x =20; gfx_y =130;  gfx_printf("%.0f",cx0);
gfx_x =80; gfx_y =130;  gfx_printf("Hz");
gfx_x =160; gfx_y =130;  gfx_printf("HP Filter");

gfx_x =20; gfx_y =150;  gfx_printf("%.1f",(20*resx0));
gfx_x =80; gfx_y =150;  gfx_printf("dB");
gfx_x =160; gfx_y =150;  gfx_printf("HP Res");

gfx_x =20; gfx_y =170;  gfx_printf("%.0f",cx1);
gfx_x =80; gfx_y =170;  gfx_printf("Hz");
gfx_x =160; gfx_y =170;  gfx_printf("LP Filter");

gfx_x =20; gfx_y =190;  gfx_printf("%.1f",(20*resx1));
gfx_x =80; gfx_y =190;  gfx_printf("dB");
gfx_x =160; gfx_y =190;  gfx_printf("LP Res");

gfx_r=1; gfx_g=0.5; gfx_b=0; gfx_a=1;

gfx_x =320; gfx_y =10;  gfx_printf("%.0f",max(min(((((fs*0.1) ^ fParam14 ) * fParam13 ) + fParam12)|0,fs),0));
gfx_x =280; gfx_y =10;  gfx_printf("min");

gfx_x =320; gfx_y =30;  gfx_printf("%.0f",max(min((((fs ^ fParam14 ) * fParam13 ) + fParam12)|0,fs),0));
gfx_x =280; gfx_y =30;  gfx_printf("max");

gfx_x =320; gfx_y =50;  gfx_printf("%.0f",max(min((((delay ^ fParam14 ) * fParam13 ) + fParam12)|0,fs),0));
gfx_x =280; gfx_y =50;  gfx_printf("clock");

gfx_r=0; gfx_g=1; gfx_b=1; gfx_a=1;

gfx_x =280; gfx_y =110;  gfx_printf("%.1f",20 * log10(hiss/11));
gfx_x =320; gfx_y =110;  gfx_printf("dB");
gfx_x =350; gfx_y =110;  gfx_printf("Hiss");
