DSP: Designing a FIR Lowpass Filter / Calculating Coefficients?


#1

Hey all,

I have a couple use cases in a plug-in I’m writing where having a FIR lowpass filter would either be handy or is necessary.

I’ve found a couple examples of FIR implementations in C, but they’re all relying on coefficients that are pre-calculated. This won’t work for me, as I need a filter that can perform on bufferSize # of samples, with a particular cutoffFrequency that’s in relation to a sampleRate.

How can I go about calculating FIR coefficients with those three parameters in mind? I’ve been stumped by this, so your help is much appreciated!

One tentative solution I’ve come up with is to populate a buffer with a rectangle signal, then to perform an FFT on it to get a Sinc, and then to convolve that Sinc with my signal-to-be-filtered. The thing is, I’m uncertain how to create a rectangle that will communicate to the proper bandwidth for my filter given the above three parameter specifications. I’m also uncertain of if this is the most efficient method of calculating the coefficients or not.

Thanks for any help!


#2

You can create the impulse response according to this wikipedia article. Additionally, you might want to apply a window (hann etc.) to the impulse response.


#3

If I understand what your’re saying, I think the cutoff frequency of a given FIR filter should be invariant to changes in buffersize.

The implementation of convolution or FFT might require a specific buffer size, however.

In contrast, FIR filter frequency response for a specific set of coefficients would vary inversely with the sampling frequency, so resampling or interpolation may be required to achieve a satisfactory response at varying Fs.


#4

Don’t start from a rectangle in the frequency domain, you won’t have enough control on the cutoff frequency since this control will be depending on the buffer size. The right way to do this is to get the sinc expression, and to use it directly in the time domain, with your impulse response centered in the middle of the buffer. Then, you can apply a window on it and you get your impulse response.


#5

If the sinc is centered in the middle of the buffer, is the convolution still performed with both initially starting at zero? As in the first value is

y[0] = x[0]*fir[0] + x[1] * fir[1] + … + x[buffSize-1] * fir[buffSize-1]
presuming x and fir are the same size. next value being
y[1] = x[1]*fir[0] + x[2] * fir[1] + … + x[buffsize - 1] * fir[buffsize-2]
etc.

Or does the convolution perform from a different starting point? I guess I’m confused in that I’d figured centering the Sinc in the fir buffer would get you a different output.

Instead of worrying about how to do the time-domain convolution I’ve also been trying to figure out how to do a frequency multiplication with JUCE’s FFT class, but I’m stumped there too- Multiply results of FFT class? (convolution)


#6

Convolution doesn’t work this way, have a look at the book in dspguide.com, you’ll have most of the missing information there :wink:


#7

Been a while since I’ve taken a DSP course, brain’s a bit frazzled- but thanks for letting me know. My understanding of discrete convolution was multiply all overlaps and sum to get a particular output value, then shift one signal over in time by 1, repeat with the same calculation+shift until there’s no overlap.

That aside… is this the proper way of calculating a lowpass sinc, or am I missing something? Just trying to determine if my garbage audio output is coming from erroneous convolution, erroneous filter creation or both…

AudioSampleBuffer* FIRFilter::makeLowpass(int numberOfTaps, double cutoffFreq, double sampleRate)
{
    windowedFilter.setSize(1, numberOfTaps);
    float* filterArray = windowedFilter.getWritePointer(0);
    
    //Construct sinc centered in middle of buffer
    filterArray[0] = 0;
    for(int n = 1; n < numberOfTaps; n++)
    {
        const double angle = (n - numberOfTaps/2) * double_Pi * cutoffFreq/sampleRate;
        double sinc = 1;
        if(angle > 0)
        {
            sinc = sin(angle)/angle;
        }
        sinc *= 2.0 * cutoffFreq/sampleRate; //normalization
        const double hammingWindow = 0.54 - 0.46 * cos(n * double_Pi / numberOfTaps);
        filterArray[n] = sinc * hammingWindow;
    }
    
    return &windowedFilter;
}

If this is how to correctly design the filter, is the step from here simply convolving it with my signal to therefore filter it?