Lets talk about stereo link modes (compressors)

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

I always thought that stereo link was:
sidechain = somefilter (sampleL + sampleR) / 2;
then applies the same gain reduction to both channels, but I found that:
the link mode combines the gain reduction and make both GR equals. It basically applies the largest GR value of both compressors to both compressors. The manley vari-mu works that way in Linked mode. I can have two completely different settings on both sides and whatever GR value is higher at the moment is applied to both channels.
Source: http://quantum-music.ca/wordpress/index ... separated/
(It can be done with the smooth abs function by Ichad.c to get the max GR)

Ok, I think there may be sonic differences between these two modes, obviously.
the first method only process one compressor, while the second method consume (on cpu) two compressors.
What do you think? is worth it? Do real compressors work that way?

Post

It should be:

sidechain = somefilter(max(abs(sampleL), abs(sampleR)));

or

sidechain = max(somefilter(abs(sampleL)), somefilter(abs(sampleR));

if you use

sidechain = abs(somefilter (sampleL + sampleR) / 2);

you are only compressing the mid-signal of a stereo track, if

SampleL = 1.0
SampleR = -1.0

the above sidechain would do nothing.

P.S. just a disclaimer - that smoothabs of mine is just an idea, I actually used it in a ring-modulator to very very roughly approximate the diode-ring. I haven't used it much in a compressor yet, I've been fiddling with a compressor idea for the past 18months and it still not good yet :dog: You might also want to look into the identity of

abs = sqrt(x*x);

so in theory you could also do

smoothabs = sqrt(somefilter(x*x));

Just some ideas, have fun!

Regards
Andrew

Post

Hi plusfer

Dunno how common are the various modes. I think some analog compressors would just sum left+right as in your first description. There are so many compressor plugins in the wild, there may be instances of almost every permutation of how to do a stereo link. :)

For instance with a simple analog compressor--

1. You could mix Left Audio + Right Audio-- Feed this mono audio mix thru a single rectifier and smoother, and drive gain of both channels from that one envelope.
2. You could send Left Audio thru a Left envelope follower and send Right Audio thru a Right envelope follower. Then you could mix the two envelopes together and drive the gain of both channels from that single mixed envelope.
3. You could use two envelope followers as in [2], but rather than mix the two envelopes-- Mix them together thru a couple of diodes. The Left envelope goes thru a diode into the mix point, and the Right envelope goes thru a diode into the mix point. In this case, the mixed control signal is "whichever envelope follower happens to be louder" at any particular time. If done with semiconductors rather than tubes as may be the case in that vari-mu, you could get the described "fancy" behavior with as low as 20 cents worth of cheap diodes.

In analog, one might try to do this trick with "precision diode circuits" to avoid uncertainty about the effect of the diode voltage drops on the mixing result. OTOH maybe some designs cunningly exploit the diode voltage drops to get "preferred behavior".

A possible problem with method [1]-- If the stereo image is wide, with significant out-of-phase information between channels, then the audio mix of A + B could be lower amplitude than either the Left alone or the Right alone. In the worst case, if the stereo image is "reverse phase mono"-- If the signal is identical on both channels but they are 180 degrees phase-flipped-- Then the audio mix of the two would be zero or nearly so-- Regardless how loud the stereo signal would get, the compressor wouldn't ever apply gain change, because the L+R mix always stays near zero.

A possible problem with method[2]-- If one channel is real loud at a certain point in the song, but the other channel is very quiet-- If you simply add the two envelopes, then the low-amplitude envelope will dilute the effect of the high-amplitude envelope. If you have two sections of the song with similar dynamics, but one section has fairly-balanced envelope amplitudes, and the other section has unbalanced envelope amplitudes-- The song section with relatively equal envelope amplitudes would get compressed stronger than the song section with a high-amplitude envelope mixed with a low-amplitude envelope.

Maybe there are applications where the sum of the two envelopes would be desirable. Where you want the compression to be weaker in cases where only one of the channels is loud.

But using the loudest of the two envelopes might be more consistent in some applications. For instance if you are trying to raise a song's level without clipping, "mastering" type tasks, then if the song clips it is bad even if the song is only clipping on one of the channels. In that case it might be good to compress against the loudest envelope.

I gather that one can get away with independent unlinked channel compression when the compression is fast and relatively rare. As with fast-release peak limiters. Quickly attacking and releasing on a spike on the right channel, without changing gain on the left channel, might sound more transparent to the ear than also unnecessarily pumping the left channel where limiting wasn't necessary.

But with longer release times, independent channel compression can move the stereo image for long enough periods that the ear hears the stereo image wandering around. So stereo link can help preserve the stereo image with longer release times or heavier compression.

Some plugins offer a knob for stereo link rather than a switch. So you can smoothly mix between stereo link versus no link at all.

Maybe even the variable-linking could be made program adaptive? In case of brief small gain changes, the program would link less, and in case of longer big gain changes, the program would link more? Maybe that is fairly common. Dunno. I don't know much about features of all the compressor plugins nowadays.

Post

Ichad.c wrote:It should be:

sidechain = somefilter(max(abs(sampleL), abs(sampleR)));

or

sidechain = max(somefilter(abs(sampleL)), somefilter(abs(sampleR));

if you use

sidechain = abs(somefilter (sampleL + sampleR) / 2);

you are only compressing the mid-signal of a stereo track, if

SampleL = 1.0
SampleR = -1.0

the above sidechain would do nothing.
You are right, I hadn't noticed that.
abs = sqrt(x*x);

so in theory you could also do

smoothabs = sqrt(somefilter(x*x));

Just some ideas, have fun!

Regards
Andrew
Great! I like sqrt(somefilter(x*x)), very useful, reduces a lot the thd.
Last edited by plusfer on Mon Jul 18, 2016 11:13 pm, edited 1 time in total.

Post

JCJR wrote: Some plugins offer a knob for stereo link rather than a switch. So you can smoothly mix between stereo link versus no link at all.

Maybe even the variable-linking could be made program adaptive? In case of brief small gain changes, the program would link less, and in case of longer big gain changes, the program would link more? Maybe that is fairly common. Dunno. I don't know much about features of all the compressor plugins nowadays.
I like the "program adaptive link" idea. I will try to translate these tips you gave me into my code.

Thanks!

Post

A very simple way is to take the greater-of the left or right channel. It generates distortion in the sidechain though, so good envelope following is required. Then you don't need two sidechains, unless you want to have both linked and unlinked operation.

Post

Hi,

What a great psot... Just looking into this topic.

Any hints about 'somefilter' ?
smoothabs = sqrt(somefilter(x*x));
I'm currently just experimenting with an RMS detector with which I can choose the size of RMS window (single sample to 256 samples). Some weird results...

So, are there any intersting filters to use for "somefilter" ?
I was thinking of using an LPF, but wouldn't that 'ignore' high frequencies in the SC channel ?

Post

Ross21 wrote:Hi,

What a great psot... Just looking into this topic.

Any hints about 'somefilter' ?
smoothabs = sqrt(somefilter(x*x));
I'm currently just experimenting with an RMS detector with which I can choose the size of RMS window (single sample to 256 samples). Some weird results...
somefilter can be a one pole lowpass filter:

y = y + c * (x - y);
where c = 1.0 - exp (-2.0 * PI * cutoff / sampleRate);
So, are there any intersting filters to use for "somefilter" ?
I was thinking of using an LPF, but wouldn't that 'ignore' high frequencies in the SC channel ?
I don't think so, sqrt(somefilter(x*x)), because x*x rectifies the whole signal. I mean, with x*x you get the DC, then the filter is to smooth the DC.

Post

I'm more than a bit foggy on every implication, but a common math identity I hadn't thought much about until the last year-- If you square a pure sine wave, the result is a pure sine wave of double the frequency, with a positive offset. For instance, given a sine wave peaking +/- 1.0 at frequency of 100 Hz, the squared signal is a pure sine wave peaking +1 / 0 with a frequency of 200 Hz.

It rectifies without increasing the number of harmonics. Input is a single harmonic, and output is a single harmonic at double the frequency. It doesn't add a nasty series of rectified harmonics reaching up past nyquist.

If we want an envelope with the lowest audio frequency ripple, the doubling in frequency is an asset-- If we filter the squared 100 Hz signal with a first-order lowpass filter tuned to 100 Hz, the ripple attenuation would be about -6 dB, rather than the -3 dB we would get if filtering the original 100 Hz signal.

Any rectification method would tend to double the frequency, but most simple rectification methods also add nasty harmonics not present in the original signal, requiring even better filtering to remove.

To get any advantage from this "clean rectification" of the squaring, filtering must be applied after the square, and before the sqrt. As in Ichad.c's sqrt(somefilter(x^2))

For instance, rectification with a memoryless sqrt(x^2) would add just as many nasty harmonics to filter out, as abs(x).

Which is GREAT for clean-compressing sine waves. OTOH if we listen to a squared music signal it will sound bad. I don't think it would just cleanly double every harmonic in the song, cleanly doubling the pitch of the whole song. But because any audio can be decomposed into sine waves, it is a slight puzzle, "why not?"

Regardless, filtering the squared signal seems a cleaner method compared to the filtering of abs().

It seems that filtering the squared signal requires some thought about attack and release time constants if one is obsessive about accurately representing the "true" attack and release times.

For instance, given a smoothing filter applied to a sine wave input step signal-- Which attacks to within -3 dB of the input level in 10 ms, and also releases -3 dB within 10 ms after the end of the sine wave step-- If you smooth abs(x) or sqrt(x^2), it is attacking and releasing in 10 ms.

If you smooth x^2 and then sqrt the smoothed envelope-- After the sqrt, envelope attacks to -1.5 dB in 10 ms, and releases to -1.5 dB in 10 ms. So if you have defined your attack as "the time required to rise to within -3 dB" and defined your release as "the time required to fall -3 dB", then the actual sqrt attack is FASTER than the expected 10 ms, and the actual sqrt release is SLOWER than the expected 10 ms. No big deal. Just needs adjustment to get the expected attack and release times.

Post

As Miles mentioned in an earlier thread, smoothing the squared signal is smoothing the power envelope rather than the amplitude envelope. He said he doesn't do a sqrt, merely calculating the gain on the power envelope, adjusting the gain calculation to make it work out in amplitude.

Good idea. I'd been playing with sqrt() and then a pow() calculation to apply the compression ratio to get the linear gain. The sqrt() could probably be eliminated by adjusting the exponent used in the pow() calculation but I haven't tried that so far.

Pow() is based on log(), so have been playing with log() on the squared signal, applying the gain calc on the log(), then exp() to get the linear gain coefficient. An efficient pair of log() and exp() perhaps not significantly worse-performance than the pair of sqrt() and pow(). Though if the entire gain calc could be done with a single pow(), maybe that would be a little faster. So far it seems a little easier on the brain to calc gain based on knee and threshold, when using log values. Knee is a bit confusing to "get right".

So anyway, compressing on log values of power (the squared signal), the final conversion to linear gain with exp() needs to multiply the log values of gain by 0.5 before the exp(). Halving the power log values before exp automatically does the square root, resulting in a linear amplitude gain multiplier. Without halving the power log gain before conversion to a linear amplitude gain multiplier, you get twice as much compression than desired. A desired comp ratio of 2:1 would result in an actual ratio of 4:1 without halving the power log gain before the exp().

If willing to tolerate a pair of log() and exp(), another alternative would be to apply log() to the squared signal before applying the smoothing. I haven't experimented with that.

Smoothing log values, then the antilog, would result in a geometric mean rather than arithmetic mean. Maybe a geometric mean would be somehow better for compression, or maybe not. Maybe will test it some time. Conventional RMS calc uses arithmetic mean, so a geometric mean envelope may work significantly different. Or not. Dunno. https://en.wikipedia.org/wiki/Geometric_mean

Post

That's exactly I do in my compression gain functions in ATK. Lots of them with softness/knee and other tricks.

Post

Can this be used for limiting as well?

Post

Limiting is just an infinite ratio, so yes, of course (also one of the transfer function I have).
The trick I used is derived from robust optimization procedures, when you want to have an L1 norm, but with derivability around 0. I that case, you make it look like L2 there, and depending on the radius of the parabola you want, you get a knee just like the one required for compressors.

Post

Many ways to do a limiter. Dunno all of them. Currently it is just a hobby toy.

In the past I used multiple series IIR envelopes on abs() signal, sometimes with forward-backward filtering and lookahead. Hard limiting doesn't absolutely require fancy gain calculation, though maybe some of the better limiters use it.

Hard limiting above a threshold only requires a divide to calc the gain. The trick is to get the envelope "transparent sounding" if calculating the gain so simply.

Last year was playing with a js lookahead limiter code, just trying stuff hadn't tried before. It seemed to work purt good, will eventually publish it after further fine tuning. That one does not bother smoothing below-threshold audio, so it doesn't matter how the signal is rectified. abs() works as good as any.

With a fairly long lookahead buffer, it "geometrically draws" the initial smoothing into the envelope lookahead buffer. For every above-threshold peak, for instance if an over-threshold peak happens to be 10 samples long-- It fills all 10 above-theshold sample locations with the max peak of that sample run. Then it draws about 1 or 2 ms linear slopes down to zero from each end of the flat-topped region. So that NO GAIN CHANGE happens for the duration of the over-threshold peak.

It is steady "max needed gain reduction" for the entire duration of each over-threshold peak. Gain change only happens before and after each peak. This is an automated equivalent of drawing fast gain changes into a DAW track volume automation strip to manually duck the gain of peaks. No gross intermodulation distortion because of the straight ramps and flat top in the control signal-- IOW, no intermodulation can happen from audio frequency ripple in the envelope, which can leak thru an ordinary fast envelope smoother.

The "flat topped, linear ramp" first stage envelope has discontinuous sudden slope changes at the beginning and end of each linear gain change ramp. So the flat topped, linear ramp envelope is processed thru a couple of short IIR lowpass filters, which turns each ramp into a smooth sigmoid-- No sharp corners in the control envelope.

Which works transparent for fast peaks, but loud long peaks can pump and distort, so the "sigmoid-smoothed" control signal goes thru a final adaptable-release envelope which raises the release time under conditions which would otherwise get nasty-distorted by "too fast gain recovery".

Post

Hi,

I wonder also about stereo compression. I mean, one needs to consider the two channels.
A common technique (in peak compressors) is to take the 'maximum' value of any of the inocing channels (Left or Right) in order of 'firing' the compressor.

With two input signals (Left/Right), and an RMS detector like described above, would it be wiser to first 'RMS' each channel and then choose the maxima, or first choose the temporary maxima and then use a single 'RMS detector block.

Thanks,
Ross

Post Reply

Return to “DSP and Plugin Development”