-1
$\begingroup$

I want a 40 dB/decade roll-off two-pole filter with resonance that does not peak when the cut off frequency is changed. I also want resonance to remain at a constant volume unless it is updated. @DanBoschen has got a basic blueprint which I am trying to follow but so far I don't completely understand everything.

This is the code so far, It has a problem that is preventing it from working The waveform will trough at 0. I have triple checked the algebra, (I'm not the best at algebra yet)

What is wrong with my code?

New code:

public class DynamicTwoPoleFilter implements ISimpleFilter {
    
    // instance variables
    private double cutoffFrequency;
    private double resonance;
    private double sampleRate;
    private double cutoffAmount;
    private double resonanceAmount;
    private double time;
    private double k;
    private double alpha;
    private double beta;
    private double gama;
    private double delta;

    // constructor
    public DynamicTwoPoleFilter(double cutoffFrequency, double sampleRate, double resonance) {
        this.cutoffFrequency = cutoffFrequency;
        this.sampleRate = sampleRate;
        this.resonance = resonance;
        cutoffAmount = Math.sqrt(2) * Math.PI * this.cutoffFrequency;
        time = 1 / sampleRate;
        k = -2 * Math.PI * cutoffFrequency / 
                Math.tan(-2 * Math.PI * cutoffFrequency * time / 2);
        resonanceAmount = Math.pow(0.5, (24 + resonance * 0.25 * 128) / 16);
        delta = (Math.pow(k, 2) + 2 * cutoffAmount * k + 2 
                * Math.pow(cutoffAmount, 2));
        alpha = 2 * Math.pow(cutoffAmount, 2) / delta;
        beta = (4 * Math.pow(cutoffAmount, 2) - 2 * Math.pow(k, 2)) / delta;
        gama = (Math.pow(k, 2) - 2 * cutoffAmount * k + 2 
                * Math.pow(cutoffAmount, 2)) / delta;
    }

    // low pass filter
    @Override
    public double lowPass(double point) {
        
        double a = Math.pow(point, -1);
        double b = Math.pow(point, -2);
        
        return alpha * ((1 + 2 * a + b)/(1 + beta * a + gama * b));
    }

    // high pass filter
    @Override
    public double highPass(double point) {
        return point - lowPass(point);
    }

    @Override
    public void setCutoff(double cutoffFrequency) {
        this.cutoffFrequency = cutoffFrequency;
        cutoffAmount = Math.sqrt(2) * Math.PI * this.cutoffFrequency;
        time = 1 / sampleRate;
        k = (2 * Math.PI * cutoffAmount) / 
                Math.tan((2 * Math.PI * cutoffAmount * time) / 2);
        delta = (Math.pow(k, 2) + 2 * cutoffAmount * k + 2 
                * Math.pow(cutoffAmount, 2));
        alpha = 2 * Math.pow(cutoffAmount, 2) / delta;
        beta = (4 * Math.pow(cutoffAmount, 2) - 2 * Math.pow(k, 2)) / delta;
        gama = (Math.pow(k, 2) - 2 * cutoffAmount * k + 2 
                * Math.pow(cutoffAmount, 2)) / delta;
    }
    
}
```
$\endgroup$
15
  • 3
    $\begingroup$ Greetings Edward! There is a lot of code here to read and debug which we don't typically do on this site, certainly not 4 1/2 pages of code as shown (3 to 4 lines max!). Your question would be greatly improved if you can extract your signal processing question more concisely with math and and graphics- Especially helpful would be if you could define what you mean by "modulate", what exact result you are expecting and the result you are getting and more specifically where you are having difficulty. $\endgroup$ May 7, 2021 at 12:04
  • $\begingroup$ "Filter modulator" is not an established signal processing term. If you could start by describing what you mean by that -- perhaps even tell us what you're really doing with a "filter modulator" -- then the road to sensible answers will be much shorter. $\endgroup$
    – TimWescott
    May 7, 2021 at 15:11
  • $\begingroup$ I have tried making the code more clear and removed some unnecessary code. What actually is wrong is the setCutoff code in both methods. $\endgroup$ May 7, 2021 at 21:21
  • $\begingroup$ @DanBoschen While OP's code is excessive, "3 to 4 lines max" is overkill, we can surely handle more. $\endgroup$ May 8, 2021 at 7:42
  • $\begingroup$ The setCutoff(double cutoffFrequency) is the code I need help with in TwoPoleFilter and ITFilter. The dynamic cutoff frequency works fine with Sinc and single pole filters, but I need a two pole with resonance filter that can be dynamically updated for my program. $\endgroup$ May 8, 2021 at 8:37

2 Answers 2

0
$\begingroup$

Given the requirement of a 2 pole 40 dB/decade roll-off with peaking that doesn't change as the cut-off frequency is changed, A simple approach would be to map the analog two-pole filter given by:

$$H(s) = \frac{a^2}{s^2+ 2as+2a^2}$$

Where $a$ is related to the cutoff frequency using the relationship:

$$a = \sqrt{2}\pi f_c $$

(This results in two complex conjugate poles at $p = -a \pm j a$)

The digital filter is determined using the bilinear transform, with warping of the pole location by replacing the following with $s$ in $H(s)$ as follows:

$$s \leftarrow K \frac{z-1}{z+1}$$

With

$$ K = \frac{-2\pi f_c}{tan\bigg(\frac{-2\pi f_c T}{2}\bigg)}$$

Where $f_c$ is the desired cutoff frequency in Hz,

and $T$ as the sample duration or inverse of the sampling rate in Hz:

$$T = \frac{1}{f_s}$$

Leading to:

$$H(z) = \alpha \frac{1+2z^{^-1}+z^{-2}}{1+\beta z^{^-1}+\gamma z^{-2}}$$

With

$$\alpha = \bigg(\frac{ 2a^2}{K^2+2aK+2a^2}\bigg)$$

$$\beta = \frac{4a^2-2K^2}{K^2+2aK+2a^2}$$

$$\gamma = \frac{K^2-2aK+2a^2}{K^2+2aK+2a^2}$$

One possible implementation block diagram of H(z) using the Transposed Direct-Form II structure is shown below.

Implementation

For example, the following shows the comparative frequency of $H(s)$ and $H(z)$ with a sampling rate of 1 KHz and a cut-off frequency of 50 Hz:

50 Hz cutoff

And with a cutoff of 100 Hz, where we see the stop band will have a zero at Nyquist (500 Hz) but the cut-off frequency will still track closely due to the pre-warping:

100 Hz cutoff

$\endgroup$
7
  • $\begingroup$ Is p the cutoff frequency? $\endgroup$ May 11, 2021 at 5:01
  • $\begingroup$ Also, using the above Two Pole filter as an example, Which formulas should be executed in the constructor, which should be executed in the setCutoff(cutoff) method and which should be set in the lowPass(point) method? $\endgroup$ May 11, 2021 at 5:57
  • $\begingroup$ @EdwardEddy67716 I updated it with complex poles for a cleaner cutoff ($f_c$ is the -3dB cutoff frequency). Do you know how to interpret the block diagram? That is the filter implementation that you can translate to your code. $\endgroup$ May 11, 2021 at 6:08
  • $\begingroup$ I don't really know how to interpret the block diagram to tell the truth. Do the triangles mean multiply? $\endgroup$ May 11, 2021 at 6:16
  • $\begingroup$ No worries- that is why I asked, yes the triangles are gains, and the $z^{-1}$ means a delay of one clock cycle. So the output would be your input multiplied by $\alpha$ + twice the last input multiplied by $\alpha$ + the input before that multiplied by $\alpha$ + the last output multiplied by $-\beta$ + the output before that multiplied by $-\gamma$ $\endgroup$ May 11, 2021 at 6:18
0
$\begingroup$

I figured out that in my original code, all I had to do was clamp values zero and one to never be above 1 and below -1. so it ends up that I did not need this filter.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.