LP/HP Filter

DSP, Plug-in and Host development discussion.
KVRer
21 posts since 17 Jan, 2021

Post Sat Oct 09, 2021 4:37 pm

Hi guys.

While studying how filters work, I came across this article: https://www.earlevel.com/main/2012/12/1 ... le-filter/

Code: Select all

        case OnepoleHighpass:
          b1 = exp(-2.0 * M_PI * mFreq);
          a0 = 1.0 - b1;
          break;
                
        case OnepoleLowpass:
          b1 = -exp(-2.0 * M_PI * mFreq);
          a0 = 1.0 + b1;
          break;
It looks more than logical. But when I started testing this code, I came across an incomprehensible behavior of the LP Filter. It looks very strange :scared: and does not work properly. Maybe you have some ideas on this? If you need more info don't hesitate, please.

LP Filter - 3kHz - 6dB/oct. - CURRENT
Screenshot 2021-10-10 at 03.30.42.png
LP Filter - 3kHz - 6dB/oct. - ETALON
Screenshot 2021-10-10 at 03.39.18.png
You do not have the required permissions to view the files attached to this post.

KVRian
695 posts since 21 Feb, 2006 from FI

Post Sat Oct 09, 2021 10:59 pm

This HP filter has already been in discussions before.
viewtopic.php?f=33&t=545280

Read the comments on Earlevel's site as well.

KVRer

Topic Starter

21 posts since 17 Jan, 2021

Post Sun Oct 10, 2021 4:43 am

juha_p wrote:
Sat Oct 09, 2021 10:59 pm
This HP filter has already been in discussions before.
viewtopic.php?f=33&t=545280

Read the comments on Earlevel's site as well.
The fact is that there is no destructive solution. HP filter works fine. But the problem is the LP filter. I also gave examples of what the problem is. There are a lot of comments on these two topics, but there is not a single solution to this problem... :o

KVRian
695 posts since 21 Feb, 2006 from FI

Post Sun Oct 10, 2021 6:10 am

SKyzZz wrote:
Sun Oct 10, 2021 4:43 am
The fact is that there is no destructive solution. HP filter works fine. But the problem is the LP filter. I also gave examples of what the problem is. There are a lot of comments on these two topics, but there is not a single solution to this problem... :o
IIRC, response you get for CURRENT plot means your coefficient order is reversed or mixed.
Did you try plot using Earlevel's grapher applet?

KVRer

Topic Starter

21 posts since 17 Jan, 2021

Post Sun Oct 10, 2021 6:50 am

juha_p wrote:
Sun Oct 10, 2021 6:10 am
SKyzZz wrote:
Sun Oct 10, 2021 4:43 am
The fact is that there is no destructive solution. HP filter works fine. But the problem is the LP filter. I also gave examples of what the problem is. There are a lot of comments on these two topics, but there is not a single solution to this problem... :o
IIRC, response you get for CURRENT plot means your coefficient order is reversed or mixed.
Did you try plot using Earlevel's grapher applet?

1.
Assume: Fc = 0.25 (normalized frequency, equivalant to ~11 KHz at 44100 sample rate)

2.
For 1st order LPF, use the mentioned:

b1 = exp(-2.0 * M_PI * Fc);
a0 = 1.0 – b1;

To receive:
b1 = 0.20787957635076193
a0 = 0.7921204236492381

3.
Copy paste these two params to the “filter frequency response grapher”:
http://www.earlevel.com/main/2016/12/08 ... e-grapher/

And one receives a weird result.

4.
Still on the “filter frequency response grapher”, change b1 to -b1
One receives the correct graph

5.
To follow that , the LPF calculation might simply be:
b1 = -exp(-2.0 * M_PI * Fc); // add minus
a0 = 1.0 + b1; //change minus to plus

According to the calculations, I adhere to this logic for LP:
b1 = -exp(-2.0 * 3.14 * Frequency);
a0 = 1.0 + b1;

KVRian
695 posts since 21 Feb, 2006 from FI

Post Sun Oct 10, 2021 10:26 am

SKyzZz wrote:
Sun Oct 10, 2021 6:50 am

1.
Assume: Fc = 0.25 (normalized frequency, equivalant to ~11 KHz at 44100 sample rate)

2.
For 1st order LPF, use the mentioned:

b1 = exp(-2.0 * M_PI * Fc);
a0 = 1.0 – b1;

To receive:
b1 = 0.20787957635076193
a0 = 0.7921204236492381

3.
Copy paste these two params to the “filter frequency response grapher”:
http://www.earlevel.com/main/2016/12/08 ... e-grapher/

And one receives a weird result.

4.
Still on the “filter frequency response grapher”, change b1 to -b1
One receives the correct graph

5.
To follow that , the LPF calculation might simply be:
b1 = -exp(-2.0 * M_PI * Fc); // add minus
a0 = 1.0 + b1; //change minus to plus

According to the calculations, I adhere to this logic for LP:
b1 = -exp(-2.0 * 3.14 * Frequency);
a0 = 1.0 + b1;
Would be more useful to quote your own implementation instead of someones behind the link in your opening post.

By same linked page, writer says that coefficients are calculated as (thought, not implemented as said there):
b1 = -exp(-2*π*fs/fc) and
a0 = 1 – abs(b1).
and, by grapher, these coefficients gives proper response for LPF.

FYI, there are other ways to implement 1st order LPF and HPF filters. Here's one common method:

Code: Select all

//LPF1
w0 = 2*pi*(fc/fs);
s = sin(w0);
c = cos(w0);
b0 =   s;
b1 =   s;
a0 =   c + s + 1.0;
a1 =   s - c - 1.0;

//HPF1
w0 = 2.0 * pi * fc/fs;
s = sin(w0);
c = cos(w0);
b0 =   c + 1;
b1 = -(c + 1);
a0 =   c + s + 1;
a1 =   s - c - 1;

KVRist
212 posts since 1 Apr, 2009 from Hannover, Germany

Post Sun Oct 10, 2021 10:28 am

This is a typical source of error because there's no hard and fast convention on how the a and b coefficients are named. Sometimes a are numerator, b are denominator coeffs, sometimes the other way 'round. In the same way, you'll sometimes see the sign of the denominator coeffs inverted. It's kind of a tabs vs. spaces thing and you need to always double check which article/book/paper uses which convention. Confusing and annoying, I know.

See here for Nigel's taste:
https://www.earlevel.com/main/2003/02/28/biquads/

He uses a for numerator and subtracts the feedback paths (so there's an extra minus sign). Many other sources add the feedback paths and that case the denominator coeffs need to be sign flipped.

KVRian
591 posts since 4 Jan, 2007

Post Sun Oct 10, 2021 1:05 pm

juha_p wrote:
Sun Oct 10, 2021 10:26 am
FYI, there are other ways to implement 1st order LPF and HPF filters. Here's one common method:

Code: Select all

//LPF1
w0 = 2*pi*(fc/fs);
s = sin(w0);
c = cos(w0);
b0 =   s;
b1 =   s;
a0 =   c + s + 1.0;
a1 =   s - c - 1.0;

//HPF1
w0 = 2.0 * pi * fc/fs;
s = sin(w0);
c = cos(w0);
b0 =   c + 1;
b1 = -(c + 1);
a0 =   c + s + 1;
a1 =   s - c - 1;
Another one is the TPT/ZDF onepoles on "The art of VA filter design". Just one coefficient and one state involved.

KVRer

Topic Starter

21 posts since 17 Jan, 2021

Post Sun Oct 10, 2021 2:35 pm

juha_p wrote:
Sun Oct 10, 2021 10:26 am
SKyzZz wrote:
Sun Oct 10, 2021 6:50 am


By same linked page, writer says that coefficients are calculated as (thought, not implemented as said there):
b1 = -exp(-2*π*fs/fc) and
a0 = 1 – abs(b1).
and, by grapher, these coefficients gives proper response for LPF.
I tried this solution and it's far from the standard, that's the point, that we let the same noise to measure the filter with the standard and they don't converge...

Code: Select all

b1 = exp(-2.0 * M_PI * mFreq);
a0 = 1.0 - abs(b1);
LP - 3kHz - 6db/oct.
Screenshot 2021-10-11 at 01.13.19.png

Code: Select all

          
b1 = -exp(-2.0 * M_PI * mFreq);
a0 = 1.0 - abs(b1);
LP - 3kHz - 6db/oct.
Screenshot 2021-10-11 at 01.27.21.png
That's why I asked for help, for me it is not yet clear how to correctly implement this task... :dog:
You do not have the required permissions to view the files attached to this post.

KVRer

Topic Starter

21 posts since 17 Jan, 2021

Post Sun Oct 10, 2021 2:38 pm

hugoderwolf wrote:
Sun Oct 10, 2021 10:28 am
This is a typical source of error because there's no hard and fast convention on how the a and b coefficients are named. Sometimes a are numerator, b are denominator coeffs, sometimes the other way 'round. In the same way, you'll sometimes see the sign of the denominator coeffs inverted. It's kind of a tabs vs. spaces thing and you need to always double check which article/book/paper uses which convention. Confusing and annoying, I know.

See here for Nigel's taste:
https://www.earlevel.com/main/2003/02/28/biquads/

He uses a for numerator and subtracts the feedback paths (so there's an extra minus sign). Many other sources add the feedback paths and that case the denominator coeffs need to be sign flipped.
Could you share the open-source code or docs or code or anything for this filter, if possible?

KVRian
695 posts since 21 Feb, 2006 from FI

Post Sun Oct 10, 2021 10:27 pm

SKyzZz wrote:
Sun Oct 10, 2021 2:35 pm

I tried this solution and it's far from the standard, that's the point, that we let the same noise to measure the filter with the standard and they don't converge...

...

That's why I asked for help, for me it is not yet clear how to correctly implement this task... :dog:
What you mean by standard ? ... Do you mean response found in the ETALON plot in your opening post?

This plot shows IIM/MZT vs BLT vs Analog type filters responses for one pole LPF (fc=3khz, fs=44.1kHz):
lpf1var1.png
You do not have the required permissions to view the files attached to this post.

KVRer

Topic Starter

21 posts since 17 Jan, 2021

Post Mon Oct 11, 2021 1:28 am

juha_p wrote:
Sun Oct 10, 2021 10:27 pm
SKyzZz wrote:
Sun Oct 10, 2021 2:35 pm

I tried this solution and it's far from the standard, that's the point, that we let the same noise to measure the filter with the standard and they don't converge...

...

That's why I asked for help, for me it is not yet clear how to correctly implement this task... :dog:
What you mean by standard ? ... Do you mean response found in the ETALON plot in your opening post?

This plot shows IIM/MZT vs BLT vs Analog type filters responses for one pole LPF (fc=3khz, fs=44.1kHz):

lpf1var1.png
Yes, I mean the ETALON!
 
For example 12db/oct. LP:

Code: Select all

      SRFilterIIR() {
        mType = EFilterType::BiquadLowpass;
        a0 = 1.0;
        a1 = a2 = b1 = b2 = 0.0;
        mFreq = 0.50;
        mQ = 0.70710678;
        mPeakGain = 0.0;
        for (int i = 0; i < MAXNUMCHANNELS; i++) {
          z1[i] = z2[i] = 0.0;
        }
        mSamplerate = 44100;
        mPoles = 2;
      }

Code: Select all

        case BiquadLowpass:
          K = tan(M_PI * mFreq);
          norm = 1 / (1 + K / mQ + K * K);
          a0 = K * K * norm;
          a1 = 2 * a0;
          a2 = a0;
          b1 = 2 * (K * K - 1) * norm;
          b2 = (1 - K / mQ + K * K) * norm;
          break;

KVRian
695 posts since 21 Feb, 2006 from FI

Post Mon Oct 11, 2021 1:48 am

Here's an example project which implements biquad filters from Robert Bristow-Johnson' s Audio EQ Cookbook paper. See those files starting with "BiQuad"... and "IIR".
Last edited by juha_p on Mon Oct 11, 2021 4:03 am, edited 1 time in total.

KVRer

Topic Starter

21 posts since 17 Jan, 2021

Post Mon Oct 11, 2021 1:56 am

juha_p wrote:
Mon Oct 11, 2021 1:48 am
Here's an example project which implements biquad filters from Robert Bristow-Johnson' s Audio EQ Cookbook paper. See those files starting with "BiQuad"... .
Thank you, it's very helpful :love:

User avatar
KVRist
242 posts since 24 Aug, 2014 from Moscow

Post Mon Oct 11, 2021 2:21 am

Please show your "process" method.

For http://www.earlevel.com/main/2016/12/08 ... e-grapher/ try this:

a coefficients (zeros):
0.7921204236492381
1.0

b coefficients (poles):
1.0
0.20787957635076193

Return to “DSP and Plug-in Development”