AudioFilterStateVariable unwanted noise

Status
Not open for further replies.

tigger

Member
I'm getting a raspy noise from the AudioFilterStateVariable under certain conditions.
(I'm using a Teensy 4.1 with TEENSY4_AUDIO adapter board).

when the combination of the freq control input, frequency(freq) and resonance(.707) push the filter parameters out of wack the filter outputs a awful, unwanted noise.

I've created an example design to demonstrate the problem. I believe that the problem occurs when the filter cutoff frequency is pushed close to or above Nyquist, particularly when the resonance is low (like .707).

It would be great if the filter could internally limit the F after the calculation F = Fcenter*2^(signal*octaves).

Or perhaps, someone has a work around? :)

The attached file has the example design is attached (filter_noise.ino).

View attachment filter_noise.ino
 
Apparently, there is a limit to the F after the calculation F = Fcenter*2^(signal*octaves).
in

void AudioFilterStateVariable::update_variable(const int16_t *in,
const int16_t *ctl, int16_t *lp, int16_t *bp, int16_t *hp)

...
if (fmult > 5378279) fmult = 5378279;
fmult = fmult << 8;

...
though I'm not sure what the magic number 5378279 and why fmult gets shifted left by 8 here.

Anyway, this doesn't explain why the filter makes that really loud, raspy white noise sound as described in the post above.

anyone know why it does that?
 
Ok, so after analyzing filter_variable.cpp, I fixed the problem with the noise.

I made a couple of changes to filter_variable.cpp
First, I uncommented line 42 to define IMPROVE_HIGH_FREQUENCY_ACCURACY
next I changed line 133 from:
if (fmult > 5378279) fmult = 5378279;
to:
if (fmult > 4205728) fmult = 4205728;

then after fmult << 8, fmult (normalized) is limited to slightly more than 1.0
This limits the adjusted filter cutoff ( Fcenter*2^(signal*octaves) ) to 14,744 Hz


this magic number of 4205728, should probably be tied to resonance value ('damp' in filter_variable.cpp), then the filter's adjusted cutoff could go higher with higher resonance values.

I'm not sure if there are other ramifications regarding this hack, but it seems to work well for my purposes.
 
Thanks very much for this. It's solved a problem where resonance values below 1.1 were causing noise for me.
 
I've made a couple more hacks to this so it works better for my purposes:
1. I changed void AudioFilterStateVariable::update_variable to use 4x oversampling instead of just 2x;

old 2x code snippet:
(starting around line 147 in filter_variable.cpp)
Code:
		// now do the state variable filter as normal, using fmult
		input = (*in++) << 12;
		lowpass = lowpass + MULT(fmult, bandpass);
		highpass = ((input + inputprev)>>1) - lowpass - MULT(damp, bandpass);
		inputprev = input;
		bandpass = bandpass + MULT(fmult, highpass);
		lowpasstmp = lowpass;
		bandpasstmp = bandpass;
		highpasstmp = highpass;
		lowpass = lowpass + MULT(fmult, bandpass);
		highpass = input - lowpass - MULT(damp, bandpass);
		bandpass = bandpass + MULT(fmult, highpass);
		lowpasstmp = signed_saturate_rshift(lowpass+lowpasstmp, 16, 13);
		bandpasstmp = signed_saturate_rshift(bandpass+bandpasstmp, 16, 13);
		highpasstmp = signed_saturate_rshift(highpass+highpasstmp, 16, 13);
		*lp++ = lowpasstmp;
		*bp++ = bandpasstmp;
		*hp++ = highpasstmp;

new 4x code snippet:
Code:
		// now do the state variable filter as normal, using fmult
		input = (*in++) << 12;

		lowpass = lowpass + MULT(fmult, bandpass);
		highpass = ((input + 3*inputprev)>>2) - lowpass - MULT(damp, bandpass);
		bandpass = bandpass + MULT(fmult, highpass);
		
		lowpass = lowpass + MULT(fmult, bandpass);
		highpass = ((input + inputprev)>>1) - lowpass - MULT(damp, bandpass);
		bandpass = bandpass + MULT(fmult, highpass);
        
		lowpass = lowpass + MULT(fmult, bandpass);
		highpass = ((3*input + inputprev)>>2) - lowpass - MULT(damp, bandpass);
		bandpass = bandpass + MULT(fmult, highpass);
        
                lowpass = lowpass + MULT(fmult, bandpass);
		highpass = input - lowpass - MULT(damp, bandpass);
		bandpass = bandpass + MULT(fmult, highpass);

		lowpasstmp = signed_saturate_rshift(lowpass, 16, 12);  // just decimate, no need to avg outputs
		bandpasstmp = signed_saturate_rshift(bandpass, 16, 12);
		highpasstmp = signed_saturate_rshift(highpass, 16, 12);
		inputprev = input;
		*lp++ = lowpasstmp;
		*bp++ = bandpasstmp;
		*hp++ = highpasstmp;

2. then I changed line 100:
//fcenter = setting_fcenter;
fcenter = setting_fcenter/2;

3. then I changed around line 133 a little more to:
if (fmult > 4194303) fmult = 4194303;

4. then I changed line 55 of filter_variable.h

//if (q < 0.7f) q = 0.7f;
if (q < 0.5f) q = 0.5f;

Now with these changes the filter is stable with Fc up to 29.4kHz, even with a q as low as 0.5.

the down sampling is just decimated by taking one of the 4 sample instead of averaging like it was before,
but this seems to work fine.

Anyway, I just wanted to share what i did with this in case it helps someone.
 
Last edited:
actually, I had to change fmult max to:
if (fmult > 2965372) fmult = 2965372; // fmult <= 0.7, to allow q to go down to 0.5 instead of 0.7

This means with 4x oversampling Fc max is about 20kHz.
 
Status
Not open for further replies.
Back
Top