Lets talk about stereo link modes (compressors)

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

I've been playing with coding an always-stereo-linked rms comp. I treated the left and right independently until after both left and right were squared.

After squaring Lt and Rt, I add the squared values and do mono smoothing from that point on. Alternately I could take the greater of the Lt or Rt squared values at that same point. Might add that option some time. I decided to do the Lt+Rt summing because it seemed a "more idealized" rms compression-- Even though it is possible that summing compression might not work as good as "greater of the two" compression on real music. :)

Ideally I'd prefer to calc the Lt and Rt envelopes completely independent, up to the point of either summing or "greater of the two" right before calculating the gain. But the smoothing in this rms comp is "fairly cpu hungry" and I didn't want to "double the load" by duplicating the entire envelope chains.

This one has sidechain highpass filters. Seems those make the most sense before everything else, and highpass of the "greater of the two channels" or highpass of the sum of the two channels probably would not work as desired. So the Lt and Rt have their own highpass filters.

Then I use a small allpass network for a "bass frequency hilbert" to reduce bass ripple. I suspect the allpass nets probably wouldn't work so great unless there are separate allpass networks, one for the Lt and one for Rt.

The Lt allpass has two phase shifted outputs. Squaring each output and adding them together is what reduces Lt bass ripple. It is the same story with the two Rt allpass outputs.

So it is stereo until the squaring summing step after the allpass filters, and mono smoothing afterwards--

MonoSquaredSamp = 0.25 * ((AP_Lt_1^2 + AP_Lt_2^2) + (AP_Rt_1^2 + AP_Rt_2^2));

Post

JCJR wrote: Then I use a small allpass network for a "bass frequency hilbert" to reduce bass ripple. I suspect the allpass nets probably wouldn't work so great unless there are separate allpass networks, one for the Lt and one for Rt.

The Lt allpass has two phase shifted outputs. Squaring each output and adding them together is what reduces Lt bass ripple. It is the same story with the two Rt allpass outputs.
I don't think it's possible to build a true hilbert transformer without running the input through a network also. The idea is that the quadrature section is based on the difference from the input transformation.

Post

camsr wrote:
JCJR wrote: Then I use a small allpass network for a "bass frequency hilbert" to reduce bass ripple. I suspect the allpass nets probably wouldn't work so great unless there are separate allpass networks, one for the Lt and one for Rt.

The Lt allpass has two phase shifted outputs. Squaring each output and adding them together is what reduces Lt bass ripple. It is the same story with the two Rt allpass outputs.
I don't think it's possible to build a true hilbert transformer without running the input through a network also. The idea is that the quadrature section is based on the difference from the input transformation.
Thanks camsr

So far as I know that is correct. Bernie Hutchins published in Electronotes the first "allpass cascade hilbert" I saw.

I use 8 first order ZDF allpass for the "bass hilbert". Four filters for the left channel and four for the right.

For instance on the left channel AP network, the left input goes thru two series allpass filters for the first "hilbert" output, and the left input also goes thru the other two series allpass filters for the second "hilbert" output. It puts the two outputs "decently 90 degrees out of phase" from about 20 Hz to 100 Hz. My main RMS smoothing is "pretty effective" above about 100 Hz even with short time constants. So I only need extra ripple reduction in the bass.

I experimented with bigger allpass chains for "wideband hilbert" but the long allpass chains I tested added significant latency. More latency than the short "bass only" allpass chains. Maybe some variants of wideband hilbert don't have as much latency as the ones I tested. Also the "wideband hilbert" was fabulous at smoothing any frequency of sine wave, but acted kind of weird deriving envelopes from such as square or ramp waves. For some waveshapes, the "wideband hilbert" seemed to make the signal harder to smooth, not easier to smooth.

I found the allpass frequencies by tweaking values in an openoffice spreadsheet until the phase differences looked "about as good as it was gonna get". But maybe there are much better frequencies that would stay "close to 90 degrees" over a wider frequency range.

The frequencies I found--
1st Allpass cascade frequencies = 8.336807 Hz, 75.031263 Hz
2nd Allpass cascade frequencies = 30.195915 Hz, 271.763235 Hz

Code: Select all

  AP0_L = APFilt_0_L.FirstOrdTrapezoidFilter_DoSamp(LeftSample);
  AP0_L = APFilt_1_L.FirstOrdTrapezoidFilter_DoSamp(AP0_L);
  AP1_L = APFilt_2_L.FirstOrdTrapezoidFilter_DoSamp(LeftSample);
  AP1_L = APFilt_3_L.FirstOrdTrapezoidFilter_DoSamp(AP1_L);
  //Square and add the two left channel allpass chains
  MonoEnvVal = AP0_L * AP0_L + AP1_L * AP1_L;
  
  AP0_R = APFilt_0_R.FirstOrdTrapezoidFilter_DoSamp(RightSample);
  AP0_R = APFilt_1_R.FirstOrdTrapezoidFilter_DoSamp(AP0_R);
  AP1_R = APFilt_2_R.FirstOrdTrapezoidFilter_DoSamp(RightSample);
  AP1_R = APFilt_3_R.FirstOrdTrapezoidFilter_DoSamp(AP1_R);
  //Square and add the two right channel allpass chains, resulting in a mono cumulative envelope
  MonoEnvVal += (AP0_R * AP0_R + AP1_R * AP1_R);
  MonoEnvVal *= 0.25; //Scale down the sum of four signals
Last edited by JCJR on Mon Aug 01, 2016 11:10 pm, edited 1 time in total.

Post

The phase shift from allpass will make squares or saws much different in shape. About the only way to avoid that is to use an FIR hilbert, but then there is latency and lower frequency limit to contend with.

Post

JCJR wrote:After squaring Lt and Rt, I add the squared values and do mono smoothing from that point on. Alternately I could take the greater of the Lt or Rt squared values at that same point. Might add that option some time. I decided to do the Lt+Rt summing because it seemed a "more idealized" rms compression-- Even though it is possible that summing compression might not work as good as "greater of the two" compression on real music. :)
That's also what I'm doing in my SideChainCompressor and Expander. Only the Limiter uses max instead. Seemed reasonable to me as well.

Post

Miles1981 wrote:
JCJR wrote:After squaring Lt and Rt, I add the squared values and do mono smoothing from that point on. Alternately I could take the greater of the Lt or Rt squared values at that same point. Might add that option some time. I decided to do the Lt+Rt summing because it seemed a "more idealized" rms compression-- Even though it is possible that summing compression might not work as good as "greater of the two" compression on real music. :)
That's also what I'm doing in my SideChainCompressor and Expander. Only the Limiter uses max instead. Seemed reasonable to me as well.
Yeah that seems reasonable to me as well, though max and sum in this situation does slightly different things in stereo mode. Max doesn't need an explanation, but sum is a different matter. With sum your compressor will see a slightly bigger signal at multiples of 0 and 180 degrees L/R phase correlation, and will see a slightly smaller signal at multiples of 90 degrees. So this topology is phase dependent, which is kinda cool IMHO, but it won't mess with the overall L/R stereo image balance which is nice.

Post

JCJR wrote:I found the allpass frequencies by tweaking values in an openoffice spreadsheet until the phase differences looked "about as good as it was gonna get". But maybe there are much better frequencies that would stay "close to 90 degrees" over a wider frequency range.
Maybe have a look at this this page. It has an implementation of 2nd order allpass cascades at the end. Most of the coefficients are zero or one, so I guess it'd be efficient especially if you using direct form 2.

Post

camsr wrote:The phase shift from allpass will make squares or saws much different in shape. About the only way to avoid that is to use an FIR hilbert, but then there is latency and lower frequency limit to contend with.
Yes. Most likely well-understood by experts but puzzling to a dummie. :) I understand the mechanism how allpass changes a complex waveform's shape. But am rather fuzzy why a wideband Hilbert-based RMS rectifier would have trouble smoothing harmonically complex waveforms.

Because music can theoretically be decomposed into sine waves, one might naively expect that if a wideband hilbert can shift any audio frequency of sine into quadrature pairs, it "ought to" perfectly smooth and rectify any wave shape-- By "perfectly smoothing" every sine wave component in the harmonically complex waveform-- It "ought to" perfectly smooth the entire waveform.

Maybe it "misbehaves" on harmonically complex waveforms because of some unfortunate imperfection of Hilberts made from allpass filter cascades? Maybe a "more ideal" Hilbert built with FIR or whatever would be better behaved? Or maybe I did something wrong testing it.

Am ignorantly guessing that envelope smoothing obviously involves frequency smoothing, but time smoothing is of most concern for a good dynamics control envelope. Intermodulation distortion is envelope ripple interacting with the audio signal, so audio frequencies obviously need heavy attenuation in the envelope. But when the allpass filters spread out a waveform's harmonics in time, that time-spread causes weird wiggles in the envelope, with strong peaks and valleys that need strong filtering to remove.

A square or ramp wave raises the crest factor of each hilbert output. The odd shapes of each hilbert output do not "neatly dovetail to each other" as is the case with a quadrature sine wave.

Adding quadrature only to the bass frequencies doesn't misbehave so badly. It was interesting that this doesn't mess up the frequency response of the envelope follower. For instance, two quadrature copies of a low frequency sine wave, peaking at +/- 1.0-- Squaring each gives a sine wave double the frequency, peaking at +1 and 0. The double-frequency sine waves are 180 degrees apart and sum to 1.0 at all points, giving nearly perfect rectification and smoothing.

But at higher frequencies the two signals fall out of quadrature and ripple rises. At very high frequencies the phase differences would be negligible and the two signals are nearly in phase. So at high frequencies the squared sum of the two signals would approach a sine wave peaking at +2 and 0. But the average of that [+2, 0] sine wave is also 1.0.

So a 20 to 20 kHz sine sweep viewed from the raw squared summed "low frequency hilbert" would be a low-ripple DC level of 1.0 at low frequencies, gradually getting more ripple as the sweep increases in frequency. At high frequencies it is "almost all ripple". But the "midpoint of the ripple" is always 1.0

Higher frequencies are easier to smooth with reasonably short time constants, so after feeding this weird-looking "small ripple in the bass, big ripple in the treble" sweep thru a subsequent smoothing stage, it outputs low ripple over the entire audio band!

Maybe this could also somehow be made workable for a peak envelope. Haven't given it much thought. It works so "good" for RMS because of the squaring. Maybe it would take some work to get a peak envelope to have a flat frequency response with the same technique. Dunno.

Post

JCJR wrote:But at higher frequencies the two signals fall out of quadrature and ripple rises. At very high frequencies the phase differences would be negligible and the two signals are nearly in phase. So at high frequencies the squared sum of the two signals would approach a sine wave peaking at +2 and 0.
Are you adding the two signals? This wont really work as adding two sinusoids at 90 degrees simply results in a single sinusoid with a boost of 3dB. So no smoothing and just a messed up phase.

Edit: Seems I'm wrong if it comes to adding the square of the quadrature signals. Nevermind.

Post

Thanks Matt42.

I should have phrased it better as "the sum of the two squared signals".

Post

JCJR wrote:Maybe it "misbehaves" on harmonically complex waveforms because of some unfortunate imperfection of Hilberts made from allpass filter cascades? Maybe a "more ideal" Hilbert built with FIR or whatever would be better behaved? Or maybe I did something wrong testing it.
Actually I had assumed that this kind of approach would work well. I have graphed the output of a square wave with 12 harmonics using perfect quadrature as the signals are synthesised and the results are not what I would have predicted:
Image
The reason being, it seems, is when you build up a square wave with partials using sin() the peaks of the harmonics which align with or oppose the fundamental are not the same as the quadrature signal.

Edit: Added a bit more detail.
Image
So black is the square wave, red the quadrature signal and blue the sum of the squared signals

Post

Square the addends, then square root the sum. This works great with a sine wave, but maybe not complex waveforms.

Post

camsr wrote:Square the addends, then square root the sum. This works great with a sine wave, but maybe not complex waveforms.
Indeed, I probably should have plotted that. In this case we would still have narrow peaks of ~2.2 magnitude. Also if we square, add the two signals, lowpass then sqrt() the problem would be further reduced.

But still the problem of random peaks or troughs in spectrally dense wave forms when shifting the phase in this way is always going to be there I guess.

Post

Thanks matt42 for taking the time to plot those from "synthetic Hilbert". It does illustrate the issue I was seeing, except it was weirder with a two-leg allpass cascade "hilbert" because at least in your pictures, one of the legs looks like a square wave. :) The output of a two-leg allpass cascade wide band "hilbert", neither of the outputs has any resemblance to a square wave.

IMO it isn't that the sum of the squared signals couldn't be filtered to knock out a lot of the jaggy ripple-- It would be comparing the Hilbert performance to the RMS smoothing of a bandlimited square wave with no hilbert at all.

Though simply squaring a sine wave leaves a good bit of ripple to clean up, simply squaring a bandlimited square wave yields a "very smooth" almost-DC envelope VERY EASY to clean up.

So thats why it seemed to me that a wideband hilbert made sines easier to smooth, but it made squares harder to smooth. And without the wideband hilbert, sines are harder to smooth but squares are easier to smooth.

Real music would be neither squares, ramps or sines (except in the limited case of compressing unfiltered synth tracks). I can't think of a way to properly evaluate whether a wideband hilbert would offer advantage or disadvantage on real music.

I'll try to post in the next day or two pictures of what it looks like feeding a square wave thru a two-leg wideband allpass hilbert.

Post

JCJR wrote:Thanks matt42 for taking the time to plot those from "synthetic Hilbert"
No, problem. You can probably tell I have too much time on my hands! Actually I'm also interested in this. A while back I played around with hilbert transform pairs for a compressor envelope and couldn't get satisfactory results. Now with this analysis I'm starting to see why.
JCJR wrote:Real music would be neither squares, ramps or sines (except in the limited case of compressing unfiltered synth tracks). I can't think of a way to properly evaluate whether a wideband hilbert would offer advantage or disadvantage on real music.
I think the problem is that we can't control the phases of the waveform harmonics and, as a result, we have no way of predicting what will happen to peak amplitude or waveshape when we shift the phases around. My hunch is sine waves are a special case, with ideal results, which is misleading.

Also with hilbert transform pairs the phase shifts are not linear. The effect on, say, a square wave would vary depending on it's frequency.

Post Reply

Return to “DSP and Plugin Development”