A few months back I decided to teach myself how to write a compressor in order to teach myself how to use one! I'd never "grokked" them before. After going through this exercise I find that I understand more sophisticated compressors much better.

My compressor has no attack or release, it just kills peaks - like trannykiller or (the original) eventhorizon. In fact I started by recreating the functionality of trannykiller completely with my own hand-written code. After I had that working, I started changing the functionality and it mutated into something different.

What I ended up with is sort of a compressor, sort of a limiter, sort of a distortion effect. With the axe murderer, you don't pick a ratio you pick a threshold and a ceiling. The code calculates a ratio that will compress zero dBfs down to the chosen ceiling.

In soft mode, the effect is "all knee" (like the bundled soft clipper JS). At the threshold, the ratio actually used is 1:1. Between the threshold and "zero", the ratio increases linearly. At zero, the calculated ratio is actually used.

Samples above zero are simply clipped. You can adjust where "zero" is using the "full scale" slider, in case you have a signal that you know is going to go over zero. Or you can adjust it down if you want to add more clipping deliberately. Or you could turn the clipping off and just let 'em through.

Anyhoo, here it is:

Code:

// axe murderer by jeff.robertson@gmail.com
desc: ratio-calculating compressor
slider1:-6<-64,24,.1>Threshold (dB)
slider2: -3<-64,24,.1>Ceiling (dB)
slider3: 1<1,1000,.1>Ratio (read only)
slider4:1<0,1,1{Off,On}>Make Up?
slider5:1<0,1,1{Off,On}>Soft?
slider6: 0<-64,24,.1>Full Scale (dB)
slider7:1<0,1,1{Off,On}>Clip?
@init
maxRatio = 300;
@slider
// assign these guys to variables so we
// don't have to keep remembering which
// is "slider1" and 2..
threshDB = slider1;
peakDB = slider2;
soft = slider5;
slider6 = max(slider6,slider2);
scaleDB = slider6;
// calculate a ratio such that full scale gets squashed
// to our chosen ceiling.
size = peakDB - threshDB;
ratio = (scaleDB - threshDB) / (peakDB - threshDB);
(threshDB >= peakDB) ?
(
ratio = maxRatio;
threshDB = peakDB;
slider1 = threshDB;
);
(ratio > maxRatio) ?
(
ratio = maxRatio;
);
slider3 = ratio;
// do our DB conversions in @slider code
threshold = 10^(threshDB/20);
makeupGain = 10^( abs(peakDB)/20);
@sample
// work with absolute values
abs0 = abs(spl0);
abs1 = abs(spl1);
(abs0 >= threshold) ? (
// now we gotta convert this to DB
db = 20*log10(abs0);
// how far are we over?
diff = db - threshDB;
soft ?
(
cratio = 1 + (ratio - 1) * min(diff/ (scaleDB - threshDB),1);
) :
(
cratio = ratio;
);
// squash it down!
squashed = diff / cratio + threshDB;
// if it's still over, clip it (maybe)
slider7 ? ( squashed = min(squashed, peakDB));
// convert back from DB and apply sign
spl0 = sign(spl0) * 10^(squashed/20);
);
(abs1 >= threshold) ? (
// now we gotta convert this to DB
db = 20*log10(abs1);
// how far are we over?
diff = db - threshDB;
soft ?
(
cratio = 1 + (ratio - 1) * min(diff/ (scaleDB - threshDB),1);
) :
(
cratio = ratio;
);
// squash it down!
squashed = diff / cratio + threshDB;
// if it's still over, clip it (maybe)
slider7 ? ( squashed = min(squashed, peakDB));
// convert back from DB and apply sign
spl1 = sign(spl1) * 10^(squashed/20);
);
// apply makeup gain
slider4 ? (
spl0 *= makeupGain;
spl1 *= makeupGain;
);