Multiband Compressor?

Hey,

has anyone made a multiband compressor in SC? I’d be keen to use one but I don’t have anywhere near the DSP chops to make one myself.

Cheers,
Jordan

I’m not aware if someone implemented a class for this, but it would not be difficult to write an effect SynthDef that splits the input signal with UGens from the BandSplitter quark and applies Compander to each band with desired compressor settings.
However, as so many multiband compressors are out there, I don’t know if it makes sense, except you are wanting to do something very special.

… and, of course, there’s Christof Ressi’s VSTPlugin as a linkage option …

First step is to get yourself a compressor that sounds good. Compander won’t cut it, since it internally downsamples the gain signal and gives you horrible aliasing instead of nice distortion if you set fast attack. I would almost consider it a bug but it would be hard to change after 20-some years…

You can make your own compressor using Amplitude.ar. Here’s a textbook, copypastable compressor design, no soft knee:

(
var compressor;

compressor = { |snd, attack, release, threshold, ratio|
	var amplitudeDb, gainDb;
	amplitudeDb = Amplitude.ar(snd, attack, release).ampdb;
	gainDb = ((amplitudeDb - threshold) * (1 / ratio - 1)).min(0);
	snd * gainDb.dbamp;
};

{
	var snd;
	snd = Env.perc(0.05, 1).ar;
	compressor.(snd, 0.01, 0.1, -6, 4);
}.plot(1);
)

Next, we can split the spectrum in two using what’s called a fourth-order Linkwitz-Riley crossover filter. This is done like so:

low = LPF.ar(LPF.ar(snd, crossoverFreq), crossoverFreq);
high =  snd - low; // equivalent to HPF.ar(HPF.ar(snd, crossoverFreq), crossoverFreq);

The choice to use two cascaded LPF’s is not arbitrary, but a product of the mathematics of crossover filters. In particular, LPF and HPF are second-order Butterworth filters, and cascading two Butterworths always results in a valid crossover filter. The band split isn’t a perfect brickwall filter, but a bit of mushiness around the crossovers is actually a good thing since sounds smoothly transition between different bands.

You will need to perform this split twice, once for the low/mids and once for the mids/highs. All in all, a working multiband compressor looks like this (I haven’t tested this much):

(
var compressor;

compressor = { |snd, attack, release, threshold, ratio|
	var amplitudeDb, gainDb;
	amplitudeDb = Amplitude.ar(snd, attack, release).ampdb;
	gainDb = ((amplitudeDb - threshold) * (1 / ratio - 1)).min(0);
	snd * gainDb.dbamp;
};

{
    var snd, low, mid, high;
    var lowFreq, highFreq;

    snd = Saw.ar(100);

    lowFreq = 300;
    highFreq = 3200;
    low = LPF.ar(LPF.ar(snd, lowFreq), lowFreq);
    snd = snd - low;
    mid = LPF.ar(LPF.ar(snd, highFreq), highFreq);
    high = snd - mid;

    low = compressor.(low, 0.01, 0.1, -6, 4);
    mid = compressor.(mid, 0.01, 0.1, -6, 4);
    high = compressor.(high, 0.01, 0.1, -6, 4);

    snd = low + mid + high;
    snd * 0.1 ! 2;
}.play;
)
16 Likes

Thanks so much for such a helpful response!