/*
Equal Loudness Contours.

Loudness		Source level relative to listening level (based on a 100 dB SPL maximum level)
Trim		Level trim dB
Link		Automatically adjusts Trim to maintain a consistent tonal balance at all levels

The ear is less sensitive to low frequencies when listening at low volume.
This plug-in is based on the Stevens-Davis equal loudness contours and
allows the bass level to be adjusted to simulate or correct for this effect.

Example uses:
If a mix was made with a very low or very high monitoring level,
the amount of bass can sound wrong at a normal monitoring level.
Use Loudness to adjust the bass content.

Check how a mix would sound at a much louder level by decreasing Loudness.
(although the non-linear behaviour of the ear at very high levels is not simulated by this plug-in).

Fade out without the sound becoming "tinny" by activating Link
and using Loudness to adjust the level without affecting the tonal balance.
*/

slider1:0.5<0,1,0.001>loudness
slider2:0.5<0,1,0.001>trim
slider3:1<0,1,1{Off,On}>link

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

@init
ext_nodenorm = 1; //???

Z0 = Z1 = Z2 = Z3 = 0;

@slider
fParam0 = slider1; //loudness
fParam1 = slider2; //gain
fParam2 = slider3; //link gain and loudness

tmp0 = fParam0 + fParam0 - 1;
igain = 60 * tmp0 * tmp0;
(tmp0<0) ? igain *= -1;

tmp1 = fParam1 + fParam1 - 1;
ogain = 60 * tmp1 * tmp1;
(tmp1<0) ? ogain *= -1;

f = 0.1 * igain + 6; //coefficient index + fractional part
i = f;
f -= i;

i2 = i - (i|0);

(i>=0)&&(i<1) ? (x0=402-(i2*68); x1=0.0025+(i2*0.0096); x2=0;);
(i>=1)&&(i<2) ? (x0=334-(i2*78); x1=0.0121+(i2*0.0232); x2=0;);
(i>=2)&&(i<3) ? (x0=256-(i2*64); x1=0.0353+(i2*0.0547); x2=0;);
(i>=3)&&(i<4) ? (x0=192-(i2*42); x1=0.0900+(i2*0.1216); x2=0;);
(i>=4)&&(i<5) ? (x0=150; x1=0.2116+(i2*0.3069); x2=0;);
(i>=5)&&(i<6) ? (x0=150-(i2*149); x1=0.5185-(i2*0.5185); x2=0;);
(i>=6)&&(i<7) ? (x0=1+(i2*32.7); x1=0+(i2*5.5); x2=0+(i2*1););
(i>=7)&&(i<8) ? (x0=33.7+(i2*58.3); x1=5.5+(i2*3.2); x2=1-(i2*0.38););
(i>=8)&&(i<9) ? (x0=92-(i2*28.3); x1=8.7+(i2*9.7); x2=0.62-(i2*0.18););
(i>=9)&&(i<10) ? (x0=63.7-(i2*20.8); x1=18.4+(i2*29.8); x2=0.44-(i2*0.14););
(i>=10)&&(i<11) ? (x0=42.9-(i2*5.3); x1=48.2+(i2*68); x2=0.3-(i2*0.12););
(i>=11)&&(i<12) ? (x0=37.6-(i2*14.7); x1=116.2+(i2*312.5); x2=0.18-(i2*0.09););
(i>=12) ? (x0=22.9; x1=428.7; x2=0.09;);

tmpA = x0; A0 = tmpA + f * (x0 - tmpA);
tmpB = x1; A1 = tmpB + f * (x1 - tmpB);
tmpC = x2; A2 = tmpC + f * (x2 - tmpC);

A0 = 1 - exp(-6.283153 * A0 / srate);

(igain>=0) ? ( mode=1; ):( mode=0; );

tmp = ogain * 0.5; // '*0.5' -- needs better gain compensation...
(fParam2==1) ? ( tmp -= igain; (tmp>0) ? tmp = 0; );
gain = pow(10, 0.05 * tmp);

@sample
z0=Z0;
z1=Z1;
z2=Z2;
z3=Z3;

(mode==0) ? //cut
(
a = spl0;
b = spl1;

z0 += A0 * (a - z0 + 0.3 * z1);
a -= z0;
z1 += A0 * (a - z1);
a -= z1;
a -= z0 * A1;

z2 += A0 * (b - z2 + 0.3 * z3);
b -= z2;
z3 += A0 * (b - z3);
b -= z3;
b -= z2 * A1;

spl0 = a * gain;
spl1 = b * gain;
)
: //boost
(
a = spl0;
b = spl1;

z0 += A0 * (a  - z0);
z1 += A0 * (z0 - z1);
a += A1 * (z1 - A2 * z0);

z2 += A0 * (b  - z2);
z3 += A0 * (z2 - z3);
b += A1 * (z3 - A2 * z2);

spl0 = a * gain;
spl1 = b * gain;
);

(abs(z1)<0.0000000001 || abs(z1)>100) ? ( Z0 = Z1 = 0; ):( Z0 = z0; Z1 = z1; ); //catch denormals
(abs(z3)<0.0000000001 || abs(z3)>100) ? ( Z2 = Z3 = 0; ):( Z2 = z2; Z3 = z3; );

@gfx 0 60
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",igain );
gfx_x =70; gfx_y =10;  gfx_printf("dB");
gfx_x =110; gfx_y =10;  gfx_printf("Loudness");

gfx_x =20; gfx_y =30;  gfx_printf("%.1f",ogain );
gfx_x =70; gfx_y =30;  gfx_printf("dB");
gfx_x =110; gfx_y =30;  gfx_printf("Trim");
