/*
Detune

Stereo pitch shifter.

Detune	Detune amount in cents (left channel is lowered in pitch, right channel is raised)
Mix	Wet / dry mix
Output	Level trim
Latency	Trade-off between latency and low-frequency response

A low-quality stereo pitch shifter for the sort of chorus and detune effects found on multi-effects hardware.
*/

slider1:0.5<0,1,0.001>detune
slider2:0.5<0,1,0.01>mix
slider3:0.5<0,1,0.01>output
slider4:0.5<0,1,0.01>latency

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

@init
//ext_noinit = 1;

BUFMAX = 4096;
buf = 0;
win = 4096;
buflen = 0;
memset(buf, 0, buf);
memset(win, 0, win);
pos0 = 0;
pos1 = pos2 = 0;

@slider
fParam0 = slider1; //detune fine
fParam1 = slider2; //mix
fParam2 = slider3; //output
fParam3 = slider4; //chunksize

semi = 3 * fParam0 * fParam0 * fParam0;
dpos2 = pow(1.0594631, semi);
dpos1 = 1 / dpos2;

wet = pow(10, 2 * fParam2 - 1);
dry = wet - wet * fParam1 * fParam1;
wet = (wet + wet - wet * fParam1) * fParam1;

tmp = 1 << (8 + (4.9 * fParam3));

(tmp!=buflen) ? //recalculate crossfade window
(
buflen = tmp;
(buflen > BUFMAX) ? buflen = BUFMAX;
bufres = 1000 * buflen / srate;
//hanning half-overlap-and-add
p = 0;
dp = 6.28318530718 / buflen;
i = 0;
loop(buflen,
win[i] = (0.5 - 0.5 * cos(p));
i+=1;
i<buflen;
p+=dp;
);
);

@sample
w=wet;
y=dry;
p1=pos1;
d1=dpos1;
p2=pos2;
d2=dpos2;
p0=pos0|0;
l=buflen-1|0;
lh=buflen>>1|0;
lf=buflen;

a = spl0;
b = spl1;

c = y * a;
d = y * b;

p0-=1;
p0&=l;

buf[p0] = w * ((a + b)*0.5); //input

p1 -= d1;
(p1<0) ? p1 += lf; //output
p1i = p1|0;
p1f = p1 - p1i;
a = buf[p1i];
p1i+=1;
p1i&=l;
a += p1f * (buf[p1i] - a); //linear interpolation

p2i = (p1i + lh) & l; //180-degree output
b = buf[p2i];
p2i+=1;
p2i&=l;
b += p1f * (buf[p2i] - b); //linear interpolation

p2i = (p1i - p0) & l; //crossfade
x = win[p2i];
c += b + x * (a - b);

p2 -= d2; //repeat for downwards shift - can't see a more efficient way?
(p2<0) ? p2 += lf; //output
p1i = p2|0;
p1f = p2 - p1i;
a = buf[p1i];
p1i+=1;
p1i&=l;
a += p1f * (buf[p1i] - a); //linear interpolation

p2i = (p1i + lh) & l; //180-degree output
b = buf[p2i];
p2i+=1;
p2i&=l;
b += p1f * (buf[p2i] - b); //linear interpolation

p2i = (p1i - p0) & l; //crossfade
x = win[p2i];
d += b + x * (a - b);

spl0 = c;
spl1 = d;

pos0=p0;
pos1=p1;
pos2=p2;

@gfx 0 100
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("%.1f",(100 * semi) );
gfx_x =70; gfx_y =10;  gfx_printf("cents");
gfx_x =120; gfx_y =10;  gfx_printf("Detune");

gfx_x =20; gfx_y =30;  gfx_printf("%.0f",(99 * fParam1) );
gfx_x =70; gfx_y =30;  gfx_drawchar($'%');
gfx_x =120; gfx_y =30;  gfx_printf("Mix");

gfx_x =20; gfx_y =50;  gfx_printf("%.1f",(40 * fParam2 - 20) );
gfx_x =70; gfx_y =50;  gfx_printf("dB");
gfx_x =120; gfx_y =50;  gfx_printf("Output");

gfx_x =20; gfx_y =70;  gfx_printf("%.1f",(bufres) );
gfx_x =70; gfx_y =70;  gfx_printf("ms");
gfx_x =120; gfx_y =70;  gfx_printf("Latency");
