desc:TriLeveler MONO [BETA Build 20152802]

slider1:-26<-40,0,1>Target [dB]

slider3:0<20,500,10>LF Cut [Hz]
slider4:22000<5000,22000,1000>HF Cut [Hz]

slider6:0<0,10,1>Micro Dynamics Max [dB]
slider7:-10<-10,0,1>Micro Dynamics Min [dB]

slider9:10<0,20,1>Medium Dynamics Max [dB]
slider10:-10<-20,0,1>Medium Dynamics Min [dB]

slider12:20<0,40,1>Macro Dynamics Max [dB]
slider13:-20<-40,0,1>Macro Dynamics Min [dB]

slider15:-40<-80,0,1>Expander Gate [dB]

slider17:20<10,30,1>Headroom [dB]

slider18:0<-3,3,0.1>Output Trim [dB]

@init

t001 = exp(-1/(srate*0.0001));
t005 = exp(-1/(srate*0.0005));
t01 = exp(-1/(srate*0.001));
t05 = exp(-1/(srate*0.005));
t10 = exp(-1/(srate*0.01));
t20 = exp(-1/(srate*0.02));
t30 = exp(-1/(srate*0.03));
t50 = exp(-1/(srate*0.05));
t100 = exp(-1/(srate*0.1));
t200 = exp(-1/(srate*0.2));
t300 = exp(-1/(srate*0.3));
t500 = exp(-1/(srate*0.5));
t1000 = exp(-1/(srate*1));


function filterLP_init(freq,Q)
instance(omega,tsin,tcos,alpha,b0,b1,b2,a0,a1,a2)
(
  Q = max(Q,0.01);
  Q = min(Q,10);
  freq = max(freq,0.1);
  freq = min(freq,srate/2);

  omega = 2*$pi*freq/srate;
  tsin = sin(omega);
  tcos = cos(omega);
  alpha = tsin/(2.0*Q);
  
  b0 = (1-tcos)/2;
  b1 = 1-tcos;
  b2 = (1-tcos)/2;
  a0 = 1+alpha;
  a1 = -2*tcos;
  a2 = 1-alpha;
);

function filterHP_init(freq,Q)
instance(omega,tsin,tcos,alpha,b0,b1,b2,a0,a1,a2)
(
  Q = max(Q,0.01);
  Q = min(Q,10);
  freq = max(freq,0.1);
  freq = min(freq,srate/2);

  omega = 2*$pi*freq/srate;
  tsin = sin(omega);
  tcos = cos(omega);
  alpha = tsin/(2.0*Q);

  b0 = (1+tcos)/2;
  b1 = -(1+tcos);
  b2 = (1+tcos)/2;
  a0 = 1+alpha;
  a1 = -2*tcos;
  a2 = 1-alpha;  
);

function filter(input)
instance(mX1,mX2,mY1,mY2,b0,b1,b2,a0,a1,a2,output)
(
  output = (b0/a0)*input + (b1/a0)*mX1 + (b2/a0)*mX2 - (a1/a0)*mY1 - (a2/a0)*mY2; 
  mX2 = mX1;
  mX1 = input;
  mY2 = mY1;
  mY1 = output;
);

function HFLF_init(freq)
instance(n0,weight)
(
  n0 = 0;
  weight = 1-exp(-2*$pi*freq/srate);
);

function HFcut(input)
instance(out,n0,weight)
(  
  out = (n0+=((input-n0)*weight));
);

function LFcut(input)
instance(out,n0,weight)
(
  out = input - (n0+=((input-n0)*weight));
);

function delay_init(samples,index)
instance (len,sloop,splay)
(
  len = samples > srate ? srate : samples;
  sloop = splay = srate * index;
);

function delay(input)
instance(sindex,splay,sloop,len,sloop,splay)
(
  sloop[sindex] = input;
  sindex += 1;
  sindex >= len ? sindex = 0;
  splay[sindex];
);

function rms_init(weight_ms)
instance (weight)(
  weight = 1-exp(-1/(weight_ms / 1000 * srate));
);

function rms(input)
instance (s,rms,weight)(
  rms = sqrt(s +=  weight * ( input^2 - s ));
);

function gatedrms(input)
instance (s,rms,weight)(
  gating == 1 ? (
  rms = sqrt(s +=  weight * ( input^2 - s ));
  ) : (rms;);
);

function follower(input,att,rel,inertia)
instance (env,tmp) (
  tmp = input >= tmp ? input : input + t05 * (tmp-input);
  (tmp > env) ? (
      env = att * (env - tmp) + tmp;
  ) : (
      env = rel * (env - tmp) + tmp;
  );
);

function gate(key,input)
instance(gating,env,e) (
  env = e.follower(key,t001,t100,t05);
  gating = min(env/gate,1)^ratio;
  input = input * gating;
);

function releasemod(input)
instance(e0,peak,out) (
  peak = e0.follower(abs(input),0,t1000,0);
  out = recovery - (peak*0.00003);
);

function limiter(input,threshold)
instance(env0,env1,env,e0,e1,th,rmod,rlmod,d0)
(
  th = max(abs(input),threshold);
  th = th / threshold;
  rlmod = rmod.releasemod(th);
  env0 = e0.follower(th,0,t10,t10);
  env1 = e1.follower(env0,t10,rlmod,0);
  env = max(env0,env1);
  input = d0.delay(input) / env;
);

function leveler(input)
instance (r0,r1,r2,rms,rms0,rms1,rms2,d0,d1,d2,cur,hp)
(
  cur.rms(input);

  // Fast
  gating > 0.891 ? (
    r0.rms(input);
    rms0 = cur.rms / max(min(r0.rms,cur.rms*range0.dwn),cur.rms*range0.up);
  );
  input = d0.delay(input) * rms0;

  // Medium
  gating > 0.891 ? (
    r1.rms(input);
    //r1.rms  *= target * (1/cur.rms);
    rms1 = target / max(min(r1.rms,target*range1.dwn),target*range1.up);
  );
  input = d1.delay(input) * rms1;

  // Slow 
  gating > 0.891 ? (
    r2.rms(input);
    rms2 = target / max(min(r2.rms,target*range2.dwn),target*range2.up);
  );
  input = d2.delay(input) * rms2;

);

srate_mul = srate/44100;

/* Channel Init */
l0.r0.rms_init(50);
l0.r1.rms_init(500);
l0.r2.rms_init(2000);
g0.r.rms_init(100);
l0.cur.rms_init(3000);

l0.d0.delay_init(1102*srate_mul,0);
l0.d1.delay_init(11025*srate_mul,1);
l0.d2.delay_init(22050*srate_mul,2);
l1.d0.delay_init(44*srate_mul,3);
/* ************ */

post.rms_init(5000);
pre.rms_init(300);

/* Level checks */
chk.med1.rms_init(0.5);
chk.med2.rms_init(0.5);
chk.med3.rms_init(0.5);
chk.mad1.rms_init(0.5);
chk.mad2.rms_init(0.5);
chk.mad3.rms_init(0.5);
/* ************ */

chk.gate.rms_init(1);

peak = 1;
gate = 0.001;
peakhold = 1;
recovery = 0;

@slider

target = 10^(slider1/20);
headroom = 10^(slider17/20);
peak = min(target * headroom, 0.966);

LFfreq = slider3;
LF.filterHP_init(max(LFfreq,20),0.75);
HFfreq = slider4;
HF.filterLP_init(HFfreq,0.75);

range0.up = 10^(-slider6/20);
range0.dwn = 10^(-slider7/20);
range1.up = 10^(-slider9/20);
range1.dwn = 10^(-slider10/20);
range2.up = 10^(-slider12/20);
range2.dwn = 10^(-slider13/20);

gate = 10^(slider15/20);
ratio = min( max( (1/gate)*0.01 , 0.5) , 10);

trim = 10^(slider18/20);

l0.rms0 = 1;
l0.rms1 = 1;
l0.rms2 = 1;

@block

pdc_delay = 34217*srate_mul;
pdc_bot_ch = 0; pdc_top_ch = 2;

mtr0 = l0.rms0;
mtr1 = l0.rms1;
mtr2 = l0.rms2;
limit = 1/l1.env;
peakhold >= limit ? ( peakhold = limit; counter = 0; ) : ( (counter += 1) < 250 ? peakhold : ( peakhold += 0.01; ); );

@sample

//spl3 = spl0;

sgn.l = spl0;
sgn.r = spl1;
sgn.m = (sgn.l + sgn.r) * 0.5;

sgn.m = LF.filter(sgn.m);
HFfreq < 22000 ? (
  sgn.m = HF.filter(sgn.m);
);

pre.rms(sgn.m);

sgn.m = g0.gate(sgn.m,sgn.m);
gating = g0.gating;

sgn.m = l0.leveler(sgn.m);
sgn.m = l1.limiter(sgn.m,peak);

sgn.m = sgn.m * trim;

post.rms(sgn.m);

spl0 = spl1 = sgn.m * play_state;

//spl2 = g0.gating;

@gfx 430 230

play_state ? (
  flash = ((time_precise() - floor(time_precise()) )*10) & 1;
);

gfx_r = 0.1; gfx_g = 0.1; gfx_b = 0.1; gfx_a = 1;
gfx_rect(1,1,gfx_w,230);

function arrow_right(x,y) (
  gfx_rect(x,2+y,8,3);
  gfx_rect(8+x,3+y,1,1);
  gfx_rect(6+x,1+y,1,5);
  gfx_rect(5+x,y,1,7);  
);

function arrow_left(x,y) (
  gfx_rect(1+x,2+y,8,3);
  gfx_rect(x,3+y,1,1);
  gfx_rect(2+x,1+y,1,5);
  gfx_rect(3+x,y,1,7);
);

function dynmeter(in,x_pos,y_pos)
instance(redux,col1,col2,in_gfx)
(
  gfx_r = 0.3; gfx_g = 0.3; gfx_b = 0.3; gfx_a = 1;
  gfx_rect(x_pos,y_pos,200,10);
  gfx_r = 0; gfx_g = 0; gfx_b = 0; gfx_a = 1;
  gfx_rect(x_pos+1,y_pos+1,198,8);
  gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 1;
  
  gfx_x = 101+x_pos; gfx_y = y_pos+2;
  in = max(in,0.1);
  in = min(in,9.8);
  gfx_r = 0; gfx_g = 0.9; gfx_b = 0.9; gfx_a = max(gating,0.5);
  in_gfx = ceil(100 - log(1/in)*43.3);
  gfx_rectto( in_gfx + x_pos, 8 + y_pos);
    
  gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 1;
  gfx_rect(100+x_pos,y_pos+1,1,8);
);

function labels(x_pos,y_pos)
(
    gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 1;
    gfx_x = 199+x_pos; gfx_y = y_pos;gfx_drawnumber(20,0);
    gfx_x = 161+x_pos; gfx_y = y_pos;gfx_drawnumber(12,0);
    gfx_x = 130+x_pos; gfx_y = y_pos;gfx_drawnumber(6,0);
    gfx_x = 100+x_pos; gfx_y = y_pos;gfx_drawnumber(0,0);
    gfx_x = 69+x_pos; gfx_y = y_pos;gfx_drawnumber(-6,0);
    gfx_x = 38+x_pos; gfx_y = y_pos;gfx_drawnumber(-12,0);
    gfx_x = 0+x_pos; gfx_y = y_pos;gfx_drawnumber(-20,0);
);

function grid(x_pos,y_pos,len)
(
    gfx_r = 0.3; gfx_g = 0.3; gfx_b = 0.3; gfx_a = 1;
    gfx_x = 199+x_pos; gfx_y = y_pos;gfx_lineto(199+x_pos,len+y_pos,0);
    gfx_x = 161+x_pos; gfx_y = y_pos;gfx_lineto(161+x_pos,len+y_pos,0);
    gfx_x = 130+x_pos; gfx_y = y_pos;gfx_lineto(130+x_pos,len+y_pos,0);
    gfx_x = 100+x_pos; gfx_y = y_pos;gfx_lineto(100+x_pos,len+y_pos,0);
    gfx_x = 69+x_pos; gfx_y = y_pos;gfx_lineto(69+x_pos,len+y_pos,0);
    gfx_x = 38+x_pos; gfx_y = y_pos;gfx_lineto(38+x_pos,len+y_pos,0);
    gfx_x = 0+x_pos; gfx_y = y_pos;gfx_lineto(0+x_pos,len+y_pos,0);
);

/* METERS */

gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 1;
gfx_setfont(1,Arial,14,'b');

#str = "MICRO DYNAMICS";
gfx_measurestr(#str,w,h);
gfx_x = gfx_w/2-(w/2); gfx_y = 35;
gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 1;
gfx_drawstr(#str);
m0.dynmeter(mtr0,gfx_w/2-100,53);
grid(gfx_w/2-100,63,1);

#str = "MEDIUM DYNAMICS";
gfx_measurestr(#str,w,h);
gfx_x = gfx_w/2-(w/2); gfx_y = 75;
gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 1;
gfx_drawstr(#str);
m1.dynmeter(mtr1,gfx_w/2-100,93);
grid(gfx_w/2-100,103,1);

#str = "MACRO DYNAMICS";
gfx_measurestr(#str,w,h);
gfx_x = gfx_w/2-(w/2); gfx_y = 115;
gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 1;
gfx_drawstr(#str);
m2.dynmeter(mtr2,gfx_w/2-100,133);
grid(gfx_w/2-100,143,1);

labels(gfx_w/2-100,148);

/* GATE */

gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 1;
chk.gate.rms(gating < 0.891 && gating > 0.501 ? 1 : 0);
chk.gate.rms > 0.5 ? (
  gfx_r = 1; gfx_g = 1-chk.gate.rms; gfx_b = 0; gfx_a = flash;
):(
  gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 1;
);

#str = "GATING ";
gating > 0.0000001 ? (
  strcat( #str, (strcat(sprintf(#,"%.0f",20 * log10(1/gating)),"dB")) );
) : (
  strcat( #str, "-inf");
);
gfx_measurestr(#str,w,h);
gfx_x = gfx_w/2-w-20; gfx_y = 173;
gfx_drawstr(#str);

/* LIMIT */

gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 1;
gfx_x = gfx_w/2+20; gfx_y = 173;
gfx_drawstr("LIMITING ");
gfx_drawstr(strcat(sprintf(#,"%.0f",20 * log10(1/peakhold)),"dB"););

/* OUTPUT */

output = gating == 1 ? post.rms : output;
output <= target * 1.12 && output >= target * 0.891 ? (
  gfx_r = 0; gfx_g = 1; gfx_b = 0; gfx_a = 1;
) : (
  output > target * 1.12 ?
    (gfx_r = 1; gfx_g = 0; gfx_b = 0; gfx_a = 1;)
  :
    (gfx_r = 0; gfx_g = 0.5; gfx_b = 1; gfx_a = 1;);
);
#str = "OUTPUT: ";
output > 0 ? (
  strcat(#str,strcat(sprintf(#,"%.1f",  20 * log10(output) ),"dB"));
) : (
  strcat(#str,"-inf");
);
gfx_measurestr(#str,w,h);
gfx_x = gfx_w/2-(w/2); gfx_y = 200;
gfx_drawstr(#str);

/* HARD LIMIT */

gfx_r = 0.5; gfx_g = 0.5; gfx_b = 0.5; gfx_a = 1;
#str = "HARD LIMIT @ ";
strcat(#str,strcat(sprintf(#,"%.0f",20 * log10(target*headroom)),"dBfs"));
gfx_measurestr(#str,w,h);
gfx_x = (gfx_w/2)-(w/2); gfx_y = 10;
gfx_drawstr( #str );

/* LEVEL CHECKS */

chk.med1.rms(l0.rms1);
1/range1.up <= chk.med1.rms+0.001 ? meg = 1 : meg = 0;
megi = chk.med2.rms(meg);

1/range1.dwn >= chk.med1.rms-0.001 ? meg2 = 1 : meg2 = 0;
megi2 = chk.med3.rms(meg2);

chk.mad1.rms(l0.rms2);
1/range2.up <= chk.mad1.rms+0.001 ? mag = 1 : mag = 0;
magi = chk.mad2.rms(mag);

1/range2.dwn >= chk.mad1.rms-0.001 ? mag2 = 1 : mag2 = 0;
magi2 = chk.mad3.rms(mag2);

gfx_setfont(1,Arial,12,'');

flash ? (
  range1.up != 1 && megi > 0.5 ? (
  gfx_r = 1; gfx_g = 1-megi; gfx_b = 0; gfx_a = 1;
  arrow_right((gfx_w/2)+102,95);
  );
  
  range1.dwn != 1 && megi2 > 0.5 ? (
  gfx_r = 1; gfx_g = 1-megi2; gfx_b = 0; gfx_a = 1;
  arrow_left((gfx_w/2)-114,95);
  );
  
  range2.up != 1 && magi > 0.5 ? (
  gfx_r = 1; gfx_g = 1-magi; gfx_b = 0; gfx_a = 1;
  arrow_right((gfx_w/2)+102,135);
  );
  
  range1.dwn != 1 && magi2 > 0.5 ? (
  gfx_r = 1; gfx_g = 1-magi2; gfx_b = 0; gfx_a = 1;
  arrow_left((gfx_w/2)-114,135);
  );
);
