-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix gain compensation for the Moog filter #11177
Fix gain compensation for the Moog filter #11177
Conversation
This would previously add a bunch of gain when the low-pass filter is engaged. These coefficients have been determined by filtering 1/sqrt(2) RMS pink noise using all resonance settings and then fitting a curve on the ratio between the input's RMS values and the output's.
For deriving the post gain compensation for the Moog filter.
* (2 - (1.0f - resonance / 4) * (1.0f - resonance / 4)); | ||
// See https://github.com/mixxxdj/mixxx/pull/11177 for the Jupyter | ||
// notebook used to derive this | ||
m_postGainNew = 1.0001784074555027f + (resonance * 0.9331585678097162f); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With float math
becomes:
1.00017797946929931640625
0.9331585678097162
becomes
0.933158576488494873046875
So lets use only significant digits, not not lead readers wrong:
m_postGainNew = 1.0001784074555027f + (resonance * 0.9331585678097162f); | |
m_postGainNew = 1.0001784f + (resonance * 0.93315857f); |
I have used https://www.h-schmidt.net/FloatConverter/IEEE754.html to check it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know. I kept the extra precision since the compiler truncates this anyways, and if for whatever reason you ever want to switch over to doubles you don't need to recompute the value. The definition of kPi
in the same source file also has excess decimals for a 32-bit floating point number.
Yes, that was intentional. I have no idea why the original implementation increased the gain for low center frequencies, but that's not typical. I checked at least ten other Moog-style ladder filter implementations and none increased the gain for lower cutoff frequencies. Increasing the gain when turning the low-pass filter down makes the filter sound very boomy/muddy when turned down far enough that just the lowest sub bass comes through.
See below. This is the notebook updated again: |
The original implemention adds a gain of 7 in the unity case with the resonance cranked up to 4. This is lowered to 3.5 when using a 500 Hz cut if frequency. Your implementation uses even more grain ~5 in that case. I think your intention is the opposite at low cut off frequencies. Unfortunately I cannot yet use the Jupyter notebooks. Can you show your results as a screenshot? For me it is a big issue that our Moog ladder is not neutral at unity. When comparing other implementations, did you find one that does not suffer that issue and also has not the gain issue? I wonder if the filter knob range is correct? How do you like the gain of the standard filter? Can we use this as reference? Maybe we can use a sweep over the whole frequency band to compare both? |
The reason why the resonance based gain compensation is needed for these ladder filters is because increasing the resonance also causes the passband's gain to decrease. I made this plot from the Moog filter used in Mixxx, with the gain compensation always set to 1: Adding the gain compensation I computed in this PR you get this: Notice how I derived the gain compensation formula for the low-pass filter by setting the cutoff frequency to 5 Hz below Nyquist, yet the passband gain is still unity at a cutoff frequency of 1 kHz (and this also goes for any other frequency, 1 kHz just made the plot nicer to look at). That's because the passband gain for these filters doesn't depend on the cutoff frequency. Like I said yesterday, just staring at spectrum analyzers and sending impulses through a dozen commercial Moog filter emulations I don't see any of them doing cutoff frequency based gain compensation. I also asked in a DSP community of others compensate ladder filter gain based on the cutoff frequency and I didn't get any responses.
You can't. The maximum cutoff frequency is the Nyquist frequency, or half the sample rate: https://en.wikipedia.org/wiki/Nyquist_frequency It is not possible to represent frequencies in discrete sampled signals above the Nyquist frequency. That's when you get aliasing, because those frequencies will appear the same as another frequency sampled at the same sample rate.
Not a huge fan of the standard filter's sound (that's why I use the Moog filter, it has more character and it doesn't suffer from the zipper noises the regular filter does), but just from a quick A/B test it seems to have the exact same gaining as the Moog filter with the changes from this PR. So that's good. |
Ah, I see. Thank you for all the details. I have measured some points but there is still a gain change in the pass band with some settings, tested with 44.1 kHz LPF = 0.236 Res 3.977 HPF = 0.0003 Gain -1,83 dB LPF = 0.025 Res 3.23 HPF = 0.0003 Gain −0.707 dB So it looks like the cut off frequency dependency cannot be removed. I wonder why this was wrong in the first place. The values are the faction for the sampling rate, hard to interpret. Since we have recently introduced the value display in the GUI this is exposed to the user. The HPF is also introducing a Gain As already mentioned this PR Improves the situation. Not sure how much love you like to put into it to iron out the remaining gain shift. |
if (MODE == MoogMode::HighPassOversampling || MODE == MoogMode::HighPass) { | ||
m_postGainNew = 1; | ||
m_postGainNew = 0.8478175496499384f + (0.09022626934231257f * resonance); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strange, these values makes the gain dependency worse.
Why do we see different results?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reran the numbers for a more aggressive cutoff frequency (0.001 Hz instead of 1 Hz) and that that got me the following polynomial:
Which is the 1.0 value that the filter used before this PR. Going back to this (or the above polynomial) does sound correct to me. When did you notice a problem with this? Note that I'm talking about the perceived loudness. You will obviously see your VU meter increase a bit when you start increasing the high-pass filter's frequency. This is to be expected because of the resonant bump around the cutoff frequency. At low cutoff values the low frequencies are amplified while the other frequencies in the filter's pass band stay at equal levels. So a 1.0 post gain compensation value for the HPF is correct here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have recorded a sweep sound generated by Audacity. One time without the filter and one time with the filter active. Than I have compared the peak samples in the pass-band. I will repeat this tonight.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try playing regular music through the filter. I optimized these curves so that when the filter gets engaged from its neutral position (the highest cutoff frequency for the low-pass filter, and the lowest cutoff frequency for the high-pass filter) there is no perceived gain different for most music.
Like I said, there is not supposed to be any post gain changes based on the cutoff frequency. At least not if this is supposed to be a Moog-style ladder filter. You can compensate for lots of things, but then it's not longer a ladder filter. Just like you usually don't compensate for the change in transition frequency when changing the resonance. If you hadn't already noticed, check the magnitude response screenshots I posted again. The effective center frequency is pushed upwards as the resonance parameter is increased. That's one of the defining characteristics of these filters. The reason why you might want gain compensation for these filters at all (many implementations don't even do that at all) is that increasing the resonance substantially decreases the gain of the pass band. Sometimes this idea is also referred to as bass compensation, since these filters were often used to design bass sounds and in those situations the pass band would mostly consists of bass frequencies. The gain compensation polynomials from this PR were derived to minimize the difference in perceived gain when the low-pass and high-pass filters are engaged from their neutral position. So, if you take the included Moog Filter effect chain, when turning the super knob clockwards or counter-clockwards from its neutral 12 o'clock position. Is this not the case for you? With these changes doing so sounds pretty smooth for me. |
This was changed in response to mixxxdj#11177 (comment), but the 1.0 behavior is correct. The pass band gain does not change with resonance.
This was changed in response to mixxxdj#11177 (comment), but the 1.0 behavior is correct. The pass band gain does not change with resonance.
I just had a close look at the two graphs of: This is is in the magnitude of my audacity measurements of The normal filter has always 0 dB
So the remaining Gain drop is desired? My impression was that the original author wants to compensate that as well and we have only messed up the coefficients when porting the code. |
I have found the original paper: But anyway, since It finally needs to sound good and not look good in graph, I am open to any type of compensation. |
The actual value there is I don't see having a slight gain drop in the passband as you almost close the filter is a bad thing. It makes the filter sound more gentle as you bring the cutoff frequency down until you finally close the filter completely. But in this case the gain difference is so minimal I'd be surprised if anyone not staring at a spectrum analyzer would point it out. Try playing a small bit of music on a 1-4 beat loop (so you can clearly hear what's going on) and play with the filters. The only thing that stands out to me is that in the 0-60 Hz range the high-pass resonance may be a bit too much when the resonant peak lines up just right with sub bass from the song. If we want would be possible to avoid that by reducing the resonance parameter when the filter is just barely engaged. I know some other people designing DJM-style filters who did that, but I don't know if it's really needed in practical use here. |
This was changed in response to mixxxdj#11177 (comment), but the 1.0 behavior is correct. The pass band gain does not change with resonance.
OK, than please add a comment to the source that we have intentionally removed the tuning compensation, that is suggested by Antti and was is part of the original implementation and add the reasons for our decision. |
I'd consider that a big improvement, because that resonance is the only reason I try to skip the sub bass range when doing a high-pass sweep. (talking about the current implementation, not this branch which I didn't test yet) |
The current implementation has never had any tuning compensation. That's the effect I described earlier where the effective center frequency is dependent on the resonance parameter, and is never quite at the frequency you set it to. I checked the paper and nowhere does it mention anything about tuning based gain compensation. It briefly mentions only resonance gain compensation.
What do you propose? I can add an (on by default?) parameter to the Moog filter that rolls off the resonance during the first 100 Hz (for both low-pass and high-pass filters). Might also be useful to do the same thing above 20 kHz to make that less ear piercing and to make the transition into the low-pass filter even smoother. Do you want me to track that onto this PR or should I do it in another PR? |
I don't "want", I just appreciate you take care of Mixxx' sound quality. And I think it'd be better to fix separate issues in separate PRs : ) small PRs getting merged quickly vs. feature creep |
Oh but I do ;). Would make engaging the high-pass filter just that little bit smoother. But yeah I'll look into this for a next PR after this one. The actual implementation will be super simple so it's mostly fine tuning some behavior until it feels right. |
Read the last sentence in § 6. To summarize: I am fine with the current state. We just need to document our decisions.
Thank you. This would finally allow to use a high resonance without blowing the speakers. (In a follow up PR) |
I did, that's what I quoted. Tuning compensation. And gain resonance compensation. We do the latter, but tuning compensation was never part of Mixxx's Moog filter. I could add a comment that the passband gain is slightly dependent on the center frequency (that's also to be expected because of the way the transition band in these filters work, it's always a gradual slope). I'm just curious where the original formula came from. |
It reads as a "Tuning and resonance compensation" This part was the original tuning compensation: m_postGainNew = (1 + resonance / 4 * (1.1f + cutoff / sampleRate * 3.5f)) I have introduced that here: My guess is that it as working at one pint and it was messed up later. And it is also part of https://github.com/esluyter/houvilainenfilter as *input = amf * (resonanceCorrPost + cutoffIn * resonance * 3.5f); |
That's not tuning compensation. That's gain compensation. Tuning compensation would alter the value of And |
I read the sentence "Tuning and resonance compensation" as add a gain depending on the tuning and the resonance parameter. This is what we original did and what the other implementation did. You have removed the tuning part, that's my point. IMHO 0 dB in the pass band for any tuning and resonance is desired just like the normal filter. I have recorded 3 times a 10 Hz sine waveform to see the current state. It is a LPF sweep with 3.5 1 and 0 resonance. However as said, this is not really an issue when paling music, because due to the resonance and the missing highs, you cannot note the gain issue. In current main the gain issue IS notable when the LGF is engaged. This issue is fixed here. |
It looks like the gain drops relative fast and than raises gently again. Maybe my original curve was not optimized for the part wher the gain drops? |
I posted this yesterday in Zullip but I did some more analysis on the filter. These are the DC gains with the original and this PR's gain compensation formulas: The new curve is much better gain between resonance values, but there is indeed a tiny dip in passband gain when the cutoff frequency parameter is set to around the equivalent of 10 kHz. I'll do some experimentation to see if compensating for this dip in DC gain makes the filter sound better. I can imagine it might actually be desirable since higher frequencies are perceived as louder, so when the resonant peak is centered around those frequencies having the overall gain of the rest of the signal reduced a bit might not be a bad thing. |
Also keep in mind that analyzing filters this way only works for linear filters, and these filters are not linear. With normal settings and input gains you can consider them to be almost linear which is why these analyses still mostly work, but they don't paint the full picture. And whether or not I see a dip in DC/bass register gain while the cutoff frequency is still well above that seems to depend a lot on the track. Which makes sense to me considering the tanh nonlinearities in the filter. With most inputs I tried I don't actually see a dip like that. And I personally do like how a downwards low-pass filter sweep sounds now, it rolls off in a very smooth way. Oh and another thing (for another PR) that I haven't mentioned yet but that I'm sure everyone has noticed is that the Moog high-pass filter doesn't close fully when you turn up the high-pass filter. Here's a filter sweep with the low-pass and the high-pass filter for reference: simplescreenrecorder-2023-01-08_14.33.44-reencode.mp4 |
Thank you for the analysis. So it looks like my original compensation was a tuning compensation for the pass band, but it fails at high cut off frequencies used when the filter is turned in. When I compare the moog type with the normal filter switching on at 10 KHz LPG, the difference is notable, but not bad. Regarding this PR the only missing point is a summary of these findings a some words why we pick the current compensation curve. Proposal:
|
There never was any tuning compensation. Tuning compensation would have changed the cutoff frequency parameter so the effective center frequency would remain constant when changing the resonance. And yeah that dip only occurs on some material. Of the four tracks I randomly grabbed it only happened with one of them. Notice how the bass keeps peaking at the same level I set the low-pass filter to around 0.20 in the video I posted above. That could be attributed to the filters being nonlinear. |
I don't mind how it is called. What do you think about my proposed comment? Does it work for you? |
I'll add a comment saying we don't do gain compensation based on the cutoff frequency but like I already showed I'm not actually seeing a meaningful dip in the bass region when staring at a spectrum analyzer during actual usage (so when playing music through it rather than an impulse. which I already explained mostly works but is not technically the correct way to analyze these kinds of filters). |
Done. Also updated the Jupyter notebook in the OP with the latest version containing more all the additional analysis. |
This was changed in response to mixxxdj#11177 (comment), but the 1.0 behavior is correct. The pass band gain does not change with resonance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for all the work you put into this. LGTM
Just waiting for CI to finish before merge.
This would previously add a bunch of gain when the low-pass filter is engaged. These new coefficients have been determined by filtering
1/sqrt(2)
RMS pink noise using all resonance settings and then fitting a curve on the ratio between the input's RMS values and the output's. Here is the Jupyter notebook used to compute these coefficients:mixxx-moog-filter-gain-compensation.tar.gz