/*
Limiter

Simple "opto-electronic" limiter.

Threshold		Threshold (this is not a brickwall limiter so this is only approximate)
Output		Level trim
Attack		Attack time
Release		Release time
Knee		Select Hard or Soft (both can pump and distort when pushed hard !)
*/

slider1:1<0,1,0.01>threshold
slider2:0.5<0,1,0.01>output
slider3:0.5<0,1,0.01>attack
slider4:0.5<0,1,0.01>release
slider5:0<0,1,1{off,on}>softknee

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

@init
gain = 1;

log2dB = 8.6858896380650365530225783783321;
db2log = 0.11512925464970228420089957273422;
gr_meter = 1;
gr_meter_decay = exp(1/(1*srate));

@slider
fParam1=slider1; //thresh
fParam2=slider2; //output
fParam3=0.5+slider3*0.5; //attack
fParam4=0.5+slider4*0.5; //release
fParam5=slider5; //softknee

(fParam5 > 0.5) ? //soft knee
(
thresh = pow(10, 1 - (2 * fParam1));
)
: //hard knee
(
thresh = pow(10, (2 * fParam1) - 2);
);

trim = pow(10, (2 * fParam2) - 1);
att = pow(10, -0.01 - (2 * fParam3));
rel = pow(10, -2 - (3 * fParam4));

@sample
th = thresh;
g = gain;
at = att;
re = rel;
tr = trim;

(fParam5 > 0.5) ? //soft knee
(
ol = spl0;
or = spl1;

lev = (1 / (1 + th * abs(ol + or)));

(g > lev) ?
(
g = g - at * (g - lev);
)
: //below level
(
g = g + re * (lev - g);
);

spl0 = (ol * tr * g);
spl1 = (or * tr * g);
)
: //hard knee
(
ol = spl0;
or = spl1;

lev = (0.5 * g * abs(ol + or));

(lev > th) ?
(
g = g - (at * (lev - th));
)
: //below threshold
(
g = g + (re * (1 - g));
);

spl0 = (ol * tr * g);
spl1 = (or * tr * g);
);

gain = g;

gr = log(gain)*log2dB;
grv = exp(gr*db2log);
grv < gr_meter ? gr_meter=grv : ( gr_meter*=gr_meter_decay; gr_meter>1?gr_meter=1; );
grmin = min( min(gr,gr) , grmin);

@gfx 0 120
gfx_r=1; gfx_g=gfx_b=0; gfx_a=0.8;

meter_bot=21;
meter_h=min(gfx_h,20);
xscale=gfx_w*20/meter_bot;

gfx_y=0;
gfx_x=gfx_w + log10(gr_meter)*xscale;
gfx_rectto(gfx_w,meter_h);

gfx_r=gfx_g=gfx_b=1; gfx_a=0.6;

s2=sqrt(2)/2;
g_ = s2;
while(
gfx_x=gfx_w + log10(g_)*xscale;
gfx_x >= 0 ? (
gfx_y=0;
gfx_lineto(gfx_x,meter_h,0);
gfx_y=meter_h-gfx_texth;
gfx_x+=2;
gfx_drawnumber(log10(g_)*20,0);
gfx_drawchar($'d');
gfx_drawchar($'B');
);
g_*=s2;
gfx_x >=0;
);

(mouse_cap) ? (grmin=0;);
gfx_x=2;
gfx_y=meter_h/2 - gfx_texth/2;
gfx_drawnumber(grmin,1);

//------------------------------------------------------------------------

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

gfx_x =20; gfx_y =30;  gfx_printf("%.1f",(40*fParam1 - 40) );
gfx_x =70; gfx_y =30;  gfx_printf("dB");
gfx_x =110; gfx_y =30;  gfx_printf("Threshold");

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

gfx_x =20; gfx_y =70;  gfx_printf("%.1f",(-301030.1 / (srate * log10(1 - att))) );
gfx_x =70; gfx_y =70;  gfx_printf("s");
gfx_x =110; gfx_y =70;  gfx_printf("Attack");

gfx_x =20; gfx_y =90;  gfx_printf("%.1f",(-301.0301 / (srate * log10(1 - rel))) );
gfx_x =70; gfx_y =90;  gfx_printf("ms");
gfx_x =110; gfx_y =90;  gfx_printf("Release");
