"Normal" values for AudioFilterStateVariable

Status
Not open for further replies.

C0d3man

Well-known member
Hi all,

I am trying to understand how the AudioFilterStateVariable works. There are three parameters. How do I have to set them in order not to influence (or with no hearable influence) the original signal (when using only Out 0 (Low Pass Output))?

e.g. setting frequency=10000.0, resonance=1.0 and octaveControl=0.0?

TIA, Holger

Code:
frequency(freq);

Set the filter's corner frequency. When a signal is connected to the control input, the filter will implement this frequency when the signal is zero.

resonance(Q);

Set the filter's resonance. Q ranges from 0.7 to 5.0. Resonance greater than 0.707 will amplify the signal near the corner frequency. You must attenuate the signal before input to this filter, to prevent clipping.

octaveControl(octaves);

Set how much (in octaves) the control signal can alter the filter's corner freqency. Range is 0 to 7 octaves. For example, when set to 2.5, a full scale positive signal (1.0) will shift the filter frequency up 2.5 octaves, and a full scale negative signal will shift it down 2.5 octaves.
 
To have no audible effect, you'd have to set the corner frequency to something between 18000.0 and 20000.0Hz. To avoid ringing/clipping/resonance but maintaining ideal flat frequency response, set the resonance to 0.707 (Butterworth-like characteristic) and the octave control to 0.0
 
Sadly, it seems I didn't get the 2X oversampling quite right inside the state variable filter. It has stability issues when run at those extreme settings (high corner frequency and low Q/resonance). This is on my low priority bug list, but the honest reality of "low priority" means it'll be a very long time until I work on it, if ever.

Generally speaking for IIR filters, the biquads are best for higher corner frequency and the state variable is best for lower corner frequency. Biquads demand huge numerical precision in their coefficients & state variables as you go to lower corner frequency and the state variable approach has low phase margin for stability when the corner frequency is high. Theoretically the stability is supposed to go to 1/4 of the sample frequency, so 2X internal oversampling was supposed to give all the way up to the Nyquist frequency. The code currently in the library is close but not that good. You will hit the stability bug with those settings, and you *will* know you've hit it since the output will go wild.

If you want to bypass any filter, I recommend adding a mixer so you can switch or adjust gain levels between the filtered signal and the original. If you want very smooth fading without writing code to gradually adjust the mixer gain, you could put the fader objects in front of the mixer and configure the mixer channels for 1.0 gain, then use the faders to smoothly transition between the signals.
 
Sadly, it seems I didn't get the 2X oversampling quite right inside the state variable filter. It has stability issues when run at those extreme settings (high corner frequency and low Q/resonance). This is on my low priority bug list, but the honest reality of "low priority" means it'll be a very long time until I work on it, if ever.

Ok. It seems currently not to be a big problem for me because I decided to disable the filter not by settings but with a mixer channel (as you proposed).

Generally speaking for IIR filters, the biquads are best for higher corner frequency and the state variable is best for lower corner frequency. Biquads demand huge numerical precision in their coefficients & state variables as you go to lower corner frequency and the state variable approach has low phase margin for stability when the corner frequency is high. Theoretically the stability is supposed to go to 1/4 of the sample frequency, so 2X internal oversampling was supposed to give all the way up to the Nyquist frequency. The code currently in the library is close but not that good. You will hit the stability bug with those settings, and you *will* know you've hit it since the output will go wild.

I like the handling of the AudioFilterStateVariable and also the sound is ok for my usage. I do not have much CPU time left, so the biquads may not suite.

If you want to bypass any filter, I recommend adding a mixer so you can switch or adjust gain levels between the filtered signal and the original. If you want very smooth fading without writing code to gradually adjust the mixer gain, you could put the fader objects in front of the mixer and configure the mixer channels for 1.0 gain, then use the faders to smoothly transition between the signals.

I already had a mixer with a free input after the filter, so it was a really simple thing to disable the filter by setting mixer gains to 0.0/1.0 and vice versa.

Many thanks!

Regards, Holger
 
@Paul, I would not mind taking a look and try to fix the filter during the upcoming Thanksgiving holiday... If you have any pointer that you'd like to share (whatever you remember) let me know, and I will start from there.
 
Here's an article with basic info.

http://www.earlevel.com/main/2003/03/02/the-digital-state-variable-filter/

The code is doing 2X oversampling.

Here's the same link you'll see in the code comments, which has lots of info.

http://www.musicdsp.org/showArchiveComment.php?ArchiveID=92

When you look at the code, hopefully it's obvious the loop is doing the filter calculation twice and storing only the 2nd result. That's the 2X oversampling. The first time uses "((input + inputprev)>>1)" as the input value, and the 2nd one uses just "input". This averaging with the prior input is intended as the interpolation for the 2X oversampling of the input. Maybe this isn't the best way to upconvert to input to 2X oversampled? The output is done by just taking the result of the 2nd calculation.

There's a ton of info about IIR filter stability, but most of it is very academic stuff heavy on use of mathematical language and light on tips for practical implementation.

If you're using Teensy 3.5 or 3.6 with the FPU, it'd be interesting to know if implementing this in floating point make any difference? I'm guessing it won't, since you can see in the code we're keeping 12 extra bits of resolution.
 
Since you drop every 2nd sample afterwards, there is no need to interpolate at the input. You might feed it twice with the same new value and save a few CPU cycles. It is more important to take care of the frequency coefficient which is not longer f = 2 * sin( pi * fc / fs) but f = 2 * sin( pi * fc / (2 * fs)).
One of the several stability criteria for a so-called Chamberlin filter is that f must remain smaller or equal than 1.0 which limits the practical application to fc / fs = 1/6 without oversampling (which makes a maximal fc of 7350Hz). With 2x oversampling, fc / fs might become 1/3 which "lifts" the max fc to 14700Hz. Thus, I'd suggest to hard-limit the maximum center frequency in the filter code to prevent accidental malfunctioning.
 
Where'd the "1/6" number come from?

It's been years since I wrote this, but I recall seeing "1/4" at the time. The idea of 2X oversampling was to make it stable all the way out to 22 kHz.

If the stability criteria really is 1/6, maybe just going to 3X oversampling would be enough to always make it stable for any settings?
 
The 1/6 comes from here:
f <= 1.0 ... // requirement
2 * sin( pi * fc / fs) <= 1.0
sin( pi * fc / fs) <= 0.5
pi * fc / fs <= arcsin(0.5)
pi * fc / fs <= pi / 6 ... // or 30°
fc / fs <= 1/6

In fact, 3x oversampling would cover the whole range below the Nyquist frequency of 22050Hz.
 
Status
Not open for further replies.
Back
Top