Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 13 of 13

Thread: How to avoid clipping with the filter "AudioFilterStateVariable"

  1. #1

    How to avoid clipping with the filter "AudioFilterStateVariable"

    Always when the filter frequency goes above a certain value I get a really loud noise, like white noise. When using a waveform to control the frequency I also need to be super careful with the corner frequency, because it's really easy to go to a frequency that will go beyond the "limit" and give me that loud noise.
    I tried reducing a lot the signal before the filter and it doesn't make any difference. Is there a way to solve that?

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,309
    This is a known problem. All IIR type filters have unstable behavior under certain conditions. I wrote this one with 2X oversampling and tried to set a lower limit on the resonance, but apparently that wasn't enough. It goes unstable with some settings. This is on my list of things to fix, but honestly I do not know of any good solution other that further limiting the range.

    If you need higher filter frequency settings, use the biquad filter. The state variable filter works best for low filter frequencies and biquad works best for high cutoff frequencies.

    For anyone finding this later by searching: these are the filter's frequency response curve, not the frequency of the actual signal. Both types of filters work great for all signal frequencies at their inputs, but each type has its strengths and weaknesses for implementing different response curves.

  3. #3
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,457
    State variable filters, implemented digitally with the famous Chamberlin recursive algorithm, have two parameters: f which is 2 * sin(PI * fc / fs), and q which is 1/Q, fc and Q being the classic filter parameters and fs the sampling frequency.
    One of the stability criteria is f < 1 which limits fc to a maximum of fs/6 without oversampling. Thus, it never makes sense to use such a filter beyond 7.35kHz without oversampling or 14.7kHz with 2x oversampling, using the audio library where the sampling frequency is fixed at 44.1kHz.
    The second stability criterion is more difficult to evaluate and consists in keeping the virtual filter poles within the unity gain circle. An approximative limit is the condition f * q < 2 which is equivalent to 2 * sin(PI * fc / fs) / Q < 2 or simpler, sin(PI * fc / fs) < Q. This first approximation is a necessary but not sufficient condition. In some rare cases with relatively high fc or Q, the filter will still be unstable, sometimes also due to rounding errors. Then, decrease the desired Q in 0.05 steps until the filter will behave as expected.

  4. #4
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,457
    I’m actually (triggered by the above post) studying alternatives to the Chamberlin filter structure, like the Zölzer and the modified Wise algorithms. In case I find something very useful, I’ll come up with further suggestions.

  5. #5
    Thanks guys.
    So, it seems that filter control in the AudioFilterStateVariable goes from the center frequency and above how many octaves it is set in the octave control. Is there a way to tweak the code so it goes down instead of upwards?
    Or using a biquad, is there a way for modulating the frequency?

  6. #6
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,457
    For biquads, it‘s much more complicated because each of the up to 6 coefficients (5 when normalized) has to be recalculated in a relatively complex way when the frequency or the damping factor parameter changes. The advantage of the Chamberlin state variable filter is that there are only two independent coefficients, one for the frequency and one for the damping factor.

  7. #7
    Gotcha... biquads are good, but too expensive. But I'm still wondering if I can modulate the state variable filter downwards, instead of upwards

  8. #8
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,457
    Indirectly, yes. Just by choosing an appropriate (lower) center frequency and carefully selecting the modulating range in octaves. Let’s take a modulating range from 50 to 5000Hz for example. Thus, you set the filter frequency to 50Hz, then you calculate log2(5000/50) to know that this range are 6.64 octaves which will be used as octaveControl parameter.

    With these settings, a control input of 0.0 will let the filter operate at a corner frequency of 50Hz, going through 500Hz with 0.5 as control and ending at 5000Hz with a control of 1.0. But negative control values are possible, too: still with the above settings, you’ll get a corner frequency of 5Hz with -0.5 control and even 0.5Hz with -1 at the control input. Don’t tell that this is not sufficient if you can sweep through a 1:10000 frequency range....

  9. #9
    Thank you so much for your answers! This is really helpful, I’ll try to implement this filter using these calculations.

  10. #10
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,457
    You are welcome. I just repeated with other words the content of the right sidebar information in the audio design tool on the PJRC website...

  11. #11
    I don't have any "notes", but I do have this test code.
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <math.h>

    double fc = 820; // max is 1/6th of sample rate
    double q = 5; // allow range 0.7 to 5

    double hz = 870;
    double amplitude = 0.25;
    double samplerate = 48e6 / 1088.0;
    int length=190;
    double pi = 3.141592654;

    // http://www.musicdsp.org/showArchiveC...p?ArchiveID=92

    static int32_t imult(int32_t a, int32_t b);

    int rs = 30;

    int main()
    {
    int i;
    double fmult, damp;
    double sample, prevsample=0;
    double low, band, high;
    double lowout, bandout, highout;
    int64_t ifmult, idamp;
    int32_t isample, iprevsample=0;
    int32_t ilow, iband, ihigh;
    int32_t ilowout, ibandout, ihighout;
    double elow, eband, ehigh;
    double scale = 32768.0 * 4096;

    fmult = 2 * sin(pi * fc / (samplerate * 2)); // fmult is 0 to 1.0, for 0 to samplerate/6
    damp = 1 / q;

    ifmult = fmult * 65536.0 * 16384.0;
    idamp = damp * 65536.0 * 16384.0;

    // filter is stable when: fmult*fmult + 2*damp < 4.0

    low = 0.0;
    band = 0.0;

    ilow = 0;
    iband = 0;

    for (i=0; i<length; i++) {
    sample = sin(pi*2.0 * hz * i / samplerate) * amplitude;

    printf("%6.3f", sample);

    // compute the filter at floating point
    low = low + fmult * band;
    high = (sample+prevsample)/2.0 - low - damp*band;
    prevsample = sample;
    band = band + fmult * high;
    lowout = low;
    highout = high;
    bandout = band;
    low = low + fmult * band;
    high = sample - low - damp*band;
    band = band + fmult * high;
    #if 1
    lowout = (lowout + low) / 2.0;
    bandout = (bandout + band) / 2.0;
    highout = (highout + high) / 2.0;
    #else
    lowout = lowout;
    bandout = bandout;
    highout = highout;
    #endif

    printf(" %6.3f, %6.3f, %6.3f", lowout, bandout, highout);

    isample = (int32_t)(sample * scale);
    printf("%9d", isample >> 12);

    // compute the filter at integers
    ilow = ilow + imult(ifmult, iband);
    ihigh = ((isample+iprevsample)>>1) - ilow - imult(idamp, iband);
    iprevsample = isample;
    iband = iband + imult(ifmult, ihigh);
    ilowout = ilow;
    ihighout = ihigh;
    ibandout = iband;
    ilow = ilow + imult(ifmult, iband);
    ihigh = isample - ilow - imult(idamp, iband);
    iband = iband + imult(ifmult, ihigh);
    #if 1
    ilowout = (ilowout + ilow) >> 1;
    ibandout = (ibandout + iband) >> 1;
    ihighout = (ihighout + ihigh) >> 1;
    #else
    ilowout = ilow;
    ihighout = ihigh;
    ibandout = iband;
    #endif

    // how much error is there between the intgers and floats?
    elow = 100.0 * (lowout - (double)ilowout / scale) / lowout;
    eband = 100.0 * (bandout - (double)ibandout / scale) / bandout;
    ehigh = 100.0 * (highout - (double)ihighout / scale) / highout;

    printf(" %7d, %7d, %7d (%.2f%%,%.2f%%,%.2f%%)",
    ilowout >> 12, ibandout >> 12, ihighout >> 12, elow, eband, ehigh);




    printf("\n");



    }
    return 0;
    }
    static int32_t imult(int32_t a, int32_t b)
    {
    int64_t result;
    int32_t out;

    result = (int64_t)a * (int64_t)b;
    #if 1
    if (result & 0x80000000) result += (int64_t)0x0000000080000000;
    out = result >> 32;
    return out << 2;
    #else
    return result >> rs;
    #endif
    }

  12. #12
    I don't have any "notes", but I do have this test code.
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <math.h>

    double fc = 820; // max is 1/6th of sample rate
    double q = 5; // allow range 0.7 to 5

    double hz = 870;
    double amplitude = 0.25;
    double samplerate = 48e6 / 1088.0;
    int length=190;
    double pi = 3.141592654;

    // http://www.musicdsp.org/showArchiveC...p?ArchiveID=92

    static int32_t imult(int32_t a, int32_t b);

    int rs = 30;

    int main()
    {
    int i;
    double fmult, damp;
    double sample, prevsample=0;
    double low, band, high;
    double lowout, bandout, highout;
    int64_t ifmult, idamp;
    int32_t isample, iprevsample=0;
    int32_t ilow, iband, ihigh;
    int32_t ilowout, ibandout, ihighout;
    double elow, eband, ehigh;
    double scale = 32768.0 * 4096;

    fmult = 2 * sin(pi * fc / (samplerate * 2)); // fmult is 0 to 1.0, for 0 to samplerate/6
    damp = 1 / q;

    ifmult = fmult * 65536.0 * 16384.0;
    idamp = damp * 65536.0 * 16384.0;

    // filter is stable when: fmult*fmult + 2*damp < 4.0

    low = 0.0;
    band = 0.0;

    ilow = 0;
    iband = 0;

    for (i=0; i<length; i++) {
    sample = sin(pi*2.0 * hz * i / samplerate) * amplitude;

    printf("%6.3f", sample);

    // compute the filter at floating point
    low = low + fmult * band;
    high = (sample+prevsample)/2.0 - low - damp*band;
    prevsample = sample;
    band = band + fmult * high;
    lowout = low;
    highout = high;
    bandout = band;
    low = low + fmult * band;
    high = sample - low - damp*band;
    band = band + fmult * high;
    #if 1
    lowout = (lowout + low) / 2.0;
    bandout = (bandout + band) / 2.0;
    highout = (highout + high) / 2.0;
    #else
    lowout = lowout;
    bandout = bandout;
    highout = highout;
    #endif

    printf(" %6.3f, %6.3f, %6.3f", lowout, bandout, highout);

    isample = (int32_t)(sample * scale);
    printf("%9d", isample >> 12);

    // compute the filter at integers
    ilow = ilow + imult(ifmult, iband);
    ihigh = ((isample+iprevsample)>>1) - ilow - imult(idamp, iband);
    iprevsample = isample;
    iband = iband + imult(ifmult, ihigh);
    ilowout = ilow;
    ihighout = ihigh;
    ibandout = iband;
    ilow = ilow + imult(ifmult, iband);
    ihigh = isample - ilow - imult(idamp, iband);
    iband = iband + imult(ifmult, ihigh);
    #if 1
    ilowout = (ilowout + ilow) >> 1;
    ibandout = (ibandout + iband) >> 1;
    ihighout = (ihighout + ihigh) >> 1;
    #else
    ilowout = ilow;
    ihighout = ihigh;
    ibandout = iband;
    #endif

    // how much error is there between the intgers and floats?
    elow = 100.0 * (lowout - (double)ilowout / scale) / lowout;
    eband = 100.0 * (bandout - (double)ibandout / scale) / bandout;
    ehigh = 100.0 * (highout - (double)ihighout / scale) / highout;

    printf(" %7d, %7d, %7d (%.2f%%,%.2f%%,%.2f%%)",
    ilowout >> 12, ibandout >> 12, ihighout >> 12, elow, eband, ehigh);




    printf("\n");



    }
    return 0;
    }
    static int32_t imult(int32_t a, int32_t b)
    {
    int64_t result;
    int32_t out;

    result = (int64_t)a * (int64_t)b;
    #if 1
    if (result & 0x80000000) result += (int64_t)0x0000000080000000;
    out = result >> 32;
    return out << 2;
    #else
    return result >> rs;
    #endif
    }

  13. #13
    Junior Member
    Join Date
    Jun 2018
    Posts
    3
    I have a slightly different question, but directly related to the title:

    In the audio design tool it says about the SVF resonance() function, "You must attenuate the signal before input to this filter, to prevent clipping."

    To do so, should I simply set the gain of the incoming signal to 1/resonance? My understanding is that the gain at the corner frequency is equal to resonance (Q) value, but I've also read that Chamberlin filters are not as simple. Not sure if the latter is true, though, and if so, whether it matters in the intended frequency range (< 7.35kHz, as noted above).

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •