slider1:0<-30,0,0.1>Threshold (dB)
slider2:2<1,20,0.1>Ratio
slider3:3<1,20,0.1>Attack (ms)
slider4:100<20,1000,1>Release (ms)
slider5:50<10,100,1>Env Decay (ms)
slider6:0<-24,24,0.1>Output (dB)

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

@init
// Compressor
output = 1;
transfer_A = 0;
transfer_B = 1;
gain = 1;
env = 0;

// Meter
log2dB = 8.6858896380650365530225783783321;

function GfxDrawStr(x1, y1, x2, y2, s) local(w, h)
(
gfx_measurestr(s, w, h);
gfx_x = (x1 + x2 - w) * 0.5;
gfx_y = (y1 + y2 - h) * 0.5;
gfx_drawstr(s);
);

function vu_grmeter(x,y,w,h,gr) (
this.gr = gr;
this.vgr = gr/1.5;
this.vu_meter_b = 10; // Needle ballistics, default is 10, can be less...

this.vdif = abs(this.vgr-this.agr);
this.vcomp = this.vu_meter_b/10;
playing ? (
this.vdif > this.vcomp ? (
this.vgrv += (this.vgr-this.vgrv)/this.vu_meter_b;
):(
this.vgrv += (this.vgr/this.vu_meter_b);
this.vgrv = min(0,this.vgrv+0.3);
this.vgr = max(0,this.vgr+0.3);
this.gr = min(0,this.gr+0.3);
);
):(
this.vgrv = min(0,this.vgrv+0.3);
this.vgr = max(0,this.vgr+0.3);
this.gr = min(0,this.gr+0.3);
);
this.agr = this.vgrv;

gfx_r=gfx_g=gfx_b=0;
this.gr_meter *= exp(1/30);
this.gr_meter > 1 ? this.gr_meter = 1;

this.meter_x = x;
this.meter_y = y;
this.meter_w = w;
this.meter_bot = 21;
this.meter_h = 20;
this.vu_h = h;
this.xscale = this.meter_w*20/this.meter_bot;
this.vu_xscale = this.meter_w/this.meter_bot;

gfx_r=gfx_g=gfx_b=0; gfx_a=1;
gfx_rect(this.meter_x,this.meter_y,this.meter_w,this.vu_h);

gfx_r=1; gfx_g=gfx_b=0; gfx_a=0.8;
gfx_y = this.meter_y;
this.gr_pos = max(this.meter_x,this.meter_w + this.vgrv*this.vu_xscale+this.meter_x);
this.gr_posi = max(this.meter_x,this.meter_w + this.gr*this.vu_xscale+this.meter_x);
gfx_x = this.gr_posi+1;
gfx_rectto(this.meter_w+this.meter_x+1,this.meter_h+this.meter_y);
gfx_x = this.gr_pos;
// Arc
gfx_r=gfx_g=gfx_b=1; gfx_a=0.8;
this.vu_pad = min(this.meter_w,this.vu_h);
gfx_arc(this.meter_x+this.meter_w/2-1,this.meter_y+this.vu_h-2,this.vu_pad-this.meter_h-this.vu_pad/30,-1,1);
this.lowangle = -1;
this.highangle = 1;
this.valueratio = max(0, min(1, (this.vgrv + 21)/(0 + 21)));
this.angle = this.lowangle + (this.highangle - this.lowangle)*(this.valueratio*0.99 + 0.01);
this.xx = sin(this.angle) * (this.vu_pad-this.meter_h-this.vu_pad/15);
this.yy = -cos(this.angle) * (this.vu_pad-this.meter_h-this.vu_pad/15);
this.n_xorig = this.meter_x+this.meter_w/2;
this.n_yorig = this.meter_y+this.vu_h-2;
// Needle
gfx_line(this.n_xorig-1,this.n_yorig,this.n_xorig+this.xx,this.n_yorig+this.yy);
gfx_line(this.n_xorig,this.n_yorig,this.n_xorig+this.xx,this.n_yorig+this.yy);
gfx_line(this.n_xorig+1,this.n_yorig,this.n_xorig+this.xx,this.n_yorig+this.yy);
//
gfx_r=gfx_g=gfx_b=0.3; gfx_a=1;
gfx_circle(this.n_xorig-1,this.n_yorig,this.meter_w/10,1,1);
//gfx_r=gfx_g=gfx_b=0.6; gfx_a=1;
//gfx_rect(this.n_xorig-this.meter_w/10,this.n_yorig+2,this.meter_w/5+2,this.meter_w/10+2);

gfx_r=gfx_g=gfx_b=1; gfx_a=0.6;
this.s2 = sqrt(2)/2;
this.g = this.s2;
while(
gfx_x = this.meter_w + log10(this.g)*this.xscale + this.meter_x;
gfx_x >= this.meter_x ? (
gfx_y = this.meter_y;
gfx_lineto(gfx_x,this.meter_h+this.meter_y,0);
gfx_y = this.meter_h-gfx_texth+this.meter_y;
gfx_x += 2;
gfx_drawnumber(log10(this.g)*20,0);
gfx_drawchar($'d');
gfx_drawchar($'B');
);
this.g *= this.s2;
gfx_x >= 0;
);

gfx_r=gfx_g=gfx_b=1; gfx_a=1;
// True GR
gfx_measurestr(sprintf(this.s, "%.1f%s", this.vgrv,"dB"),this.grw,this.grh);
GfxDrawStr(this.meter_x+this.meter_w-this.grw-2, this.n_yorig-10, this.meter_x+this.meter_w, this.n_yorig, sprintf(this.s, "%.1f%s", this.vgrv,"dB"));
// Processed GR
gfx_measurestr(sprintf(this.s, "%.1f%s", this.gr,"dB"),this.grw,this.grh);
GfxDrawStr(this.meter_x, this.n_yorig-10, this.meter_x+this.grw, this.n_yorig, sprintf(this.s, "%.1f%s", this.gr,"dB"));
);

@slider
threshold = 10^(slider1/20);

transfer_A = (1/slider2)-1;
transfer_B = output * pow(threshold,-transfer_A);

attack = exp(-1/(slider3/1000*srate));
release = exp(-1/(slider4/1000*srate));
envelope_decay = exp(-1/(slider5/1000*srate));

vol = 10^(slider6/20);

@sample
// Meter
spl0 || spl1 != 0 ? (playing=1):(playing=0);

// Compressor
inL = spl0;
inR = spl1;

det = max(abs(inL),abs(inR));
det += 0.000000000001;
env = det >= env ? det : det+envelope_decay*(env-det);
transfer_gain = env > threshold ? pow(env,transfer_A)*transfer_B:output;
gain = transfer_gain < gain ? transfer_gain+attack*(gain-transfer_gain) : transfer_gain+release*(gain-transfer_gain);

spl0 = inL * gain * vol;
spl1 = inR * gain * vol;

// Meter
gr = log(gain)*log2dB;

@gfx 0 300
meter_x=0;
meter_y=0;
meter_w=gfx_w;
vu_h=gfx_h;
vu_grmeter(meter_x,meter_y,meter_w,vu_h,gr);
