// w_limiter - GUI mod

slider1:0<-30,0,0.1>Threshold (dB)
slider2:0<-12,0,0.1>Ceiling (dB)
slider3:250<50,1000,1>Release (ms)
slider4:50<10,100,1>Env Decay (ms)
slider5:0<0,1,1{peak,rms}>Mode 1
slider6:0<0,1,1{hard,soft}>Mode 2

slider11:350<50,1000,50>--RMS window ms (user input)
slider12:-60<-63,-3,3>--RMS meter min dB (user input)
slider13:0<-60,3,0.01>PEAK in
slider14:0<-60,3,0.01>PEAK out
slider15:0<-60,3,0.01>--CURRENT dB in
slider16:0<-60,3,0.01>--CURRENT dB out
slider17:0<-60,3,0.01>RMS in
slider18:0<-60,3,0.01>RMS out
slider19:0<-60,3,0.01>--min dB in
slider20:0<-60,3,0.01>--min dB out
slider21:0<-60,3,0.01>--max dB in
slider22:0<-60,3,0.01>--max dB out
slider23:0<0,60,0.01>--range dB in
slider24:0<0,60,0.01>--range dB out

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

@init
output = 1;
transfer_A = 0;
transfer_B = 1;
gain = 1;
env0 = 0;
env1 = 0;
env2 = 0;

rms = exp(-1/(0.005/1000*srate));

log2dB = 8.6858896380650365530225783783321;
DB_MAX = 0;

GFX_RANGE_L = 0.05;
GFX_RANGE_R = 0.95;
GFX_RANGE_H = 0.23;
GFX_RANGE_Y = 0.15;

GFX_PEAK_W = 0.02;
GFX_PEAK_H = 0.03;
GFX_PEAK_Y = 0.07;
GFX_CUR_Y = 0.08;

GFX_SUM_W = 0.02;
GFX_SUM_H = 0.03;
GFX_SUM_Y = GFX_RANGE_Y+GFX_RANGE_H+0.04;

GFX_METER_MAJ_Y = 0.47;
GFX_METER_MIN_Y = 0.49;

prevGfxW = 0;
prevGfxH = 0;

densityBufN = 2048; // Something over the maximum screen width.
densityBufL = 0;
densityBufR = densityBufL + densityBufN;
bufL = densityBufR + densityBufN;
bufR = bufL + 131072; // Something over the max # of samples in the RMS window.

winMS = slider11;
holdIdx = bufN = (0.001*winMS*srate)|0;
nSamples = playing = 0;
bufOK = bufIdx = initBuf = 0;
peakL = peakR = ampEnvL = ampEnvR = 0;
sumL = sumR = sum2L = sum2R = win2L = win2R = 0;
win2LMin = win2LMax = win2RMin = win2RMax = 0;
memset(densityBufL, 0, densityBufN);
memset(densityBufR, 0, densityBufN);
maxDensityL = maxDensityR = 0;

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

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

release = exp(-1/(slider3/1000*srate));
envelope_decay0 = exp(-1/((10+(slider4*0.9))/1000*srate));
envelope_decay1 = exp(-1/((10+(slider4*0.6))/1000*srate));
envelope_decay2 = exp(-1/((10+(slider4*0.3))/1000*srate));

mode = slider5;
hard = slider6;

vol = ceiling/threshold;

winMS = slider11;
dbMin = slider12;

dbRange = DB_MAX - dbMin;

holdIdx = bufN = (0.001*winMS*srate)|0;
ampEnvCoeff = exp(-1.0/bufN);

nSamples = playing = 0;
bufOK = bufIdx = initBuf = 0;
peakL = peakR = ampEnvL = ampEnvR = 0;
sumL = sumR = sum2L = sum2R = win2L = win2R = 0;
win2LMin = win2LMax = win2RMin = win2RMax = 0;
memset(densityBufL, 0, densityBufN);
memset(densityBufR, 0, densityBufN);
maxDensityL = maxDensityR = 0;

@block
(playing && bufOK) ? (
slider13 = floor(log2dB*log(peakL)*100)/100;
slider14 = floor(log2dB*log(peakR)*100)/100;
slider15 = floor(dbRMSL*100)/100;
slider16 = floor(dbRMSR*100)/100;
slider17 = floor(log2dB*log(sqrt(sum2L/nSamples))*100)/100;
slider18 = floor(log2dB*log(sqrt(sum2R/nSamples))*100)/100;
slider19 = floor(log2dB*log(sqrt(win2LMin/bufN))*100)/100;
slider20 = floor(log2dB*log(sqrt(win2RMin/bufN))*100)/100;
slider21 = floor(log2dB*log(sqrt(win2LMax/bufN))*100)/100;
slider22 = floor(log2dB*log(sqrt(win2RMax/bufN))*100)/100;
slider23 = slider21-slider19;
slider24 = slider22-slider20;
);

(play_state == 1) ? (
playing = 1;
):(
playing = 0;
);

@sample
inL = spl0;
inR = spl1;

mode ? (
ave = inL*inL + inR*inR;
runave = ave + rms * (runave - ave);
det = sqrt(runave);
):(
ave = 0;
det = max(abs(inL),abs(inR));
);
det += 0.000000000001;

hard ? (
env2 = det >= env2 ? det : det+envelope_decay0*(env2-det);
):(
env0 = det >= env0 ? det : det+envelope_decay0*(env0-det);
env1 = env0 >= env1 ? env0 : env0+envelope_decay1*(env1-env0);
env2 = env1 >= env2 ? env1 : env1+envelope_decay2*(env2-env1);
);

transfer_gain = env2 > threshold ? pow(env2,transfer_A)*transfer_B:output;
gain = transfer_gain < gain ? transfer_gain : transfer_gain+release*(gain-transfer_gain);

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

out0 = spl0;
out1 = spl1;

(playing) ? (
(holdIdx) ? (
holdIdx -= 1;
) : (
nSamples += 1;

sumL += (inL+inR)*0.5;
sumR += (out0+out1)*0.5;

ampL = abs((inL+inR)*0.5);
ampR = abs((out0+out1)*0.5);

peakL = max(peakL, ampL);
peakR = max(peakR, ampR);

(ampL > ampEnvL) ? (
ampEnvL = ampL;
) : (
ampEnvL = ampL + ampEnvCoeff * (ampEnvL - ampL);
);
(ampR > ampEnvR) ? (
ampEnvR = ampR;
) : (
ampEnvR = ampR + ampEnvCoeff * (ampEnvR - ampR);
);

amp2L = ampL*ampL;
amp2R = ampR*ampR;
sum2L += amp2L;
sum2R += amp2R;
win2L += amp2L;
win2R += amp2R;

prev2L = bufL[bufIdx];
prev2R = bufR[bufIdx];
bufL[bufIdx] = amp2L;
bufR[bufIdx] = amp2R;

bufIdx += 1;
(bufIdx == bufN) ? (
bufIdx = 0;
(!bufOK) ? (
win2LMin = win2LMax = win2L;
win2RMin = win2RMax = win2R;
bufOK = 1;
);
);

(bufOK) ? (
win2LMin = min(win2LMin, win2L);
win2LMax = max(win2LMax, win2L);
win2RMin = min(win2RMin, win2R);
win2RMax = max(win2RMax, win2R);
win2L -= prev2L;
win2R -= prev2R;

dbRMSL = log2dB*log(sqrt(win2L/bufN));
dbRMSLX = min(max((dbRMSL-dbMin)/dbRange, 0), 1);
densityIdx = floor(dbRMSLX*(densityBufN-1));
densityBufL[densityIdx] += 1;
maxDensityL = max(maxDensityL, densityBufL[densityIdx]);

dbRMSR = log2dB*log(sqrt(win2R/bufN));
dbRMSRX = min(max((dbRMSR-dbMin)/dbRange, 0), 1);
densityIdx = floor(dbRMSRX*(densityBufN-1));
densityBufR[densityIdx] += 1;
maxDensityR = max(maxDensityR, densityBufR[densityIdx]);
);
);
);

@gfx 0 150
(gfx_w != prevGfxW) || (gfx_h != prevGfxH) ? (
prevGfxW = gfx_w;
prevGfxH = gfx_h;
rangeL = floor(gfx_w*GFX_RANGE_L);
rangeR = floor(gfx_w*GFX_RANGE_R);
rangeW = rangeR-rangeL;
rangeH = floor(gfx_h*GFX_RANGE_H);
rangeY = floor(gfx_h*GFX_RANGE_Y);
peakW = floor(gfx_w*GFX_PEAK_W);
peakH = floor(gfx_h*GFX_PEAK_H);
peakY = floor(gfx_h*GFX_PEAK_Y);
curY = floor(gfx_h*GFX_CUR_Y);
sumW = floor(gfx_w*GFX_SUM_W);
sumH = floor(gfx_h*GFX_SUM_H);
sumY = floor(gfx_h*GFX_SUM_Y);
meterMajY = floor(gfx_h*GFX_METER_MAJ_Y);
meterMinY = floor(gfx_h*GFX_METER_MIN_Y);

// Would be nice to preserve the existing data.
densityBufN = rangeW;
memset(densityBufL, 0, densityBufN);
memset(densityBufR, 0, densityBufN);
maxDensityL = maxDensityR = 0;
);

// Frame elements.

gfx_r = gfx_g = gfx_b = gfx_a = 1;

gfx_x = rangeL-1;
gfx_y = rangeY-1;
gfx_lineto(gfx_x+rangeW+2, gfx_y, 0);
gfx_lineto(gfx_x, gfx_y+rangeH+2, 0);
gfx_lineto(gfx_x-rangeW-2, gfx_y, 0);
gfx_lineto(gfx_x, gfx_y-rangeH-2, 0);

gfx_x = rangeL-1;
gfx_y = gfx_h-rangeY-rangeH-1;
gfx_lineto(gfx_x+rangeW+2, gfx_y, 0);
gfx_lineto(gfx_x, gfx_y+rangeH+2, 0);
gfx_lineto(gfx_x-rangeW-2, gfx_y, 0);
gfx_lineto(gfx_x, gfx_y-rangeH-2, 0);

// Meter.

dbMeter = ceil(dbMin);
while (gfx_x = rangeL+(dbMeter-dbMin)/dbRange*rangeW;
(dbMeter/3 == floor(dbMeter/3)) ? (
gfx_y = meterMajY;
) : (
gfx_y = meterMinY;
);
gfx_lineto(gfx_x, gfx_h-gfx_y, 0);
dbMeter += 1;
dbMeter <= DB_MAX;
);

// Current amp lines.

gfx_r = gfx_g = gfx_b = 0.5;

ampDB = log2dB*log(ampEnvL);
ampX = min(max((ampDB-dbMin)/dbRange, 0), 1);
gfx_x = rangeL;
gfx_y = curY;
gfx_rectto(gfx_x+ampX*rangeW, gfx_y+3);

ampDB = log2dB*log(ampEnvR);
ampX = min(max((ampDB-dbMin)/dbRange, 0), 1);
gfx_x = rangeL;
gfx_y = gfx_h-curY;
gfx_rectto(gfx_x+ampX*rangeW, gfx_y-3);

// RMS markers.

gfx_r = gfx_g = gfx_b = 0.75;

peakX = min(max((slider21-dbMin)/dbRange, 0), 1);
gfx_x = rangeL+peakX*rangeW;
gfx_y = peakY;
gfx_lineto(gfx_x-peakW/2, gfx_y-peakH, 1);
gfx_lineto(gfx_x+peakW, gfx_y, 0);
gfx_lineto(gfx_x-peakW/2, gfx_y+peakH, 1);

peakX = min(max((slider22-dbMin)/dbRange, 0), 1);
gfx_x = rangeL+peakX*rangeW;
gfx_y = gfx_h-peakY;
gfx_lineto(gfx_x-peakW/2, gfx_y+peakH, 1);
gfx_lineto(gfx_x+peakW, gfx_y, 0);
gfx_lineto(gfx_x-peakW/2, gfx_y-peakH, 1);

(bufOK) ? (

// Peak markers.

gfx_r = gfx_g = gfx_b = 1;

sumX = min(max((slider13-dbMin)/dbRange, 0), 1);
gfx_x = rangeL+sumX*rangeW;
gfx_y = sumY;
gfx_lineto(gfx_x-sumW/2, gfx_y+sumH, 1);
gfx_lineto(gfx_x+sumW, gfx_y, 0);
gfx_lineto(gfx_x-sumW/2, gfx_y-sumH, 1);

sumX = min(max((slider14-dbMin)/dbRange, 0), 1);
gfx_x = rangeL+sumX*rangeW;
gfx_y = gfx_h-sumY;
gfx_lineto(gfx_x-sumW/2, gfx_y-sumH, 1);
gfx_lineto(gfx_x+sumW, gfx_y, 0);
gfx_lineto(gfx_x-sumW/2, gfx_y+sumH, 1);

// Density.

gfx_b = 0;
gfx_x = rangeL;
idx = 0;
loop(rangeW,
densityAtPxL = sqrt(densityBufL[idx]/maxDensityL);
densityAtPxR = sqrt(densityBufR[idx]/maxDensityR);

gfx_r = max(2*(densityAtPxL-0.5), 0);
gfx_g = 1-abs(2*densityAtPxL-1);
gfx_y = rangeY;
gfx_lineto(gfx_x, gfx_y+rangeH, 0);

gfx_r = max(2*(densityAtPxR-0.5), 0);
gfx_g = 1-abs(2*densityAtPxR-1);
gfx_y = gfx_h-rangeY;
gfx_lineto(gfx_x, gfx_y-rangeH, 0);

gfx_x += 1;
idx += 1;
);

// Current RMS lines.

gfx_r = gfx_g = gfx_b = 1;

gfx_x = rangeL+dbRMSLX*rangeW;
gfx_y = rangeY-3;
gfx_lineto(gfx_x, gfx_y+rangeH+6, 0);

gfx_x = rangeL+dbRMSRX*rangeW;
gfx_y = gfx_h-rangeY+3;
gfx_lineto(gfx_x, gfx_y-rangeH-6, 0);
);
