/*
Re-Psycho!

Event-based pitch shifter.

Chops audio into individual beats and shifts each beat downwards in pitch.
Only allowing downwards shifts helps keep timing very tight
- depending on complexity, whole rhythm sections can be shifted!

Threshold		Trigger level to divide the input into chunks
Decay		Adjust envelope of each chunk (a fast decay can be useful while setting up)
Tune		Coarse tune (semitones)
Fine		Fine tune (cents)
Hold		Minimum chunk length
Mix		Mix original signal with output
Quality		The High setting uses smoother pitch-shifting and allows processing of stereo signals

Alternative uses include a triggered flanger or sub-octave doubler (both with mix set to 50% or less)
and a swing quantizer (with a high threshold so not all beats trigger).
*/

slider1:1<0,1,0.01>threshold
slider2:0.5<0,1,0.01>decay
slider3:1<0,1,0.01>tune
slider4:1<0,1,0.01>tune fine
slider5:0.5<0,1,0.01>hold
slider6:1<0,1,0.01>mix
slider7:1<0,1,1{Low,High}>quality

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

@init
size = srate*0.5|0;
buffer = size;
buffer2 = size;
memset(buffer, 0, size);
memset(buffer2, 0, size);

buf = 0;
buf2 = 0;

tim = size + 1;

gai = 0;

@slider
fParam1 = slider1; //threshold
fParam2 = slider2; //decay (env)
fParam3 = slider3; //tune
fParam4 = slider4; //tune fine
fParam5 = slider5; //hold (minimum chunk length)
fParam6 = slider6; //mix
fParam7 = slider7; //quality

dtim = size*0.2 + (0.2 * size * fParam5)|0;

thr = pow(10,(1.5 * fParam1) - 1.5);

(fParam2>0.5) ? ( env = (1 + 0.003 * pow(fParam2 - 0.5,5)); ):( env = (1 + 0.025 * pow(fParam2 - 0.5,5)); ); // default
//(fParam2>0.5) ? ( env = (1 + 0.01 * (fParam2 - 0.5)); ):( env = (1 + 0.01 * (fParam2 - 0.5)); ); // mod

tun = (((((fParam3 * 24)|0) - 24) + (fParam4 - 1)) / 24);
tun = pow(10, 0.60206 * tun);
wet = 0.5 * sqrt(fParam6);
dry = sqrt(1 - fParam6);

@sample
x=0;
x2=0;
we=wet;
dr=dry;
tu=tun;
en=env;
ga=gai;
xx=buf;
xx2=buf2;
ti=tim;
dti=dtim;

(fParam7>0.5) ? // high quality
(
we = (we*2);
a = spl0;
b = spl1;

((a+b > thr) && (ti > dti)) ? // trigger
(
ga = 1;
ti = 0;
);

(ti<size) ? // play out
(
(ti<(size*0.002|0)) ? // fade in
(
(ti==0) ? ( xx=x; xx2=x2; );

bufx[buffer + ti] = a;
bufx[buffer2 + ti] = b;
x = bufx[buffer + ((ti * tu)|0)];
x2 = bufx[buffer2 + ((ti * tu)|0)];

x = (xx * (1 - (0.0125 * ti)) + (x * 0.0125 * ti));
x2 = (xx2 * (1 - (0.0125 * ti)) + (x2 * 0.0125 * ti));
):(
// update to/from buffer
bufx[buffer + ti] = a;
bufx[buffer2 + ti] = b;

it1 = ti * tu; // interpolation
of1 = it1|0;
of2 = of1 + 1;
it1 = it1 - of1;
it2 = 1 - it1;

x = (it2* bufx[buffer + of1]) + (it1* bufx[buffer + of2]);
x2 = (it2* bufx[buffer2 + of1]) + (it1* bufx[buffer2 + of2]);
);

ti += 1;
ga *= en;
):( // mute
ga = 0;
);

c = (a * dr) + (x * ga * we); // output
d = (b * dr) + (x2 * ga * we);

spl0 = c;
spl1 = d;

):( // low quality

a = spl0;
b = spl1;

((a+b > thr) && (ti > dti)) ? // trigger
(
ga = 1;
ti = 0;
);

(ti<size) ? // play out
(
(ti<(size*0.002|0)) ? // fade in
(
(ti==0) ? xx = x;

bufx[buffer + ti] = (a + b);
x = bufx[buffer + ((ti * tu)|0)];

x = (xx * (1 - (0.0125 * ti)) + (x * 0.0125 * ti));
):(
// update to/from buffer
bufx[buffer + ti] = (a + b);
x = bufx[buffer + ((ti * tu)|0)];
);

ti += 1;
ga *= en;
):( // mute
ga = 0;
);

c = (a * dr) + (x * ga * we); // output
d = (b * dr) + (x * ga * we);

spl0 = c;
spl1 = d;

);

tim = ti;
gai = ga;
buf = xx;
buf2 = xx2;

@gfx 0 140
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",((24 * fParam1) - 24) );
gfx_x =70; gfx_y =10;  gfx_printf("dB");
gfx_x =130; gfx_y =10;  gfx_printf("Threshold");

gfx_x =20; gfx_y =30;  gfx_printf("%.0f",((fParam2 - 0.5) * 100) );
gfx_x =70; gfx_y =30;  gfx_drawchar($'%');
gfx_x =130; gfx_y =30;  gfx_printf("Decay");

gfx_x =20; gfx_y =50;  gfx_printf("%.0f",((24 * fParam3) - 24) );
gfx_x =70; gfx_y =50;  gfx_printf("semi");
gfx_x =130; gfx_y =50;  gfx_printf("Tune");

gfx_x =20; gfx_y =70;  gfx_printf("%.0f",((99 * fParam4) - 99) );
gfx_x =70; gfx_y =70;  gfx_printf("cent");
gfx_x =130; gfx_y =70;  gfx_printf("Tune fine");

gfx_x =20; gfx_y =90;  gfx_printf("%.0f",(1000 * dtim / srate) );
gfx_x =70; gfx_y =90;  gfx_printf("ms");
gfx_x =130; gfx_y =90;  gfx_printf("Hold");

gfx_x =20; gfx_y =110;  gfx_printf("%.0f",(100 * fParam6) );
gfx_x =70; gfx_y =110;  gfx_drawchar($'%');
gfx_x =130; gfx_y =110;  gfx_printf("Mix");
