Example of how to use the FilterDesign stuff? (dsp module)

I’ve started to have a go at using the dsp module, but I’m having a lot of trouble. I want to use a high order low pass filter, preferably a butterworth. I’ve been following the example project here: https://github.com/WeAreROLI/JUCE/tree/master/examples/DSP%20module%20plugin%20demo/Source

I’m finding this example slightly confusing itself, I’m not familiar with the ‘ProcessorDuplicator’ or the ‘ProcessContextReplacing’ stuff. I declared my filter as done in that example, but it seems it’s initialized expecting a “dsp::IIR::Coefficients”, e.g. in the example it’s initialized like:

lowPassFilter (dsp::IIR::Coefficients::makeFirstOrderLowPass (48000.0, 20000.f))

However, I suppose I want to use designIIRLowpassHighOrderButterworthMethod() from here. Problem is, this returns an array of IIR::Coefficients rather than a single one, I’ve no idea how I’m supposed to use this to initialize my filter.

Any example usage of setting my filter to be of these FilterDesign types would be very useful. Also why is only low pass filter designs available?

Cheers.

1 Like

If it returns a list of coefficients then it’s meant to be used as a cascading filter. This means you need to create several IIRFilters in series for each Coefficient element in the array.

Ah right, I was hoping there would be a generalized class that implements this stuff internally.

1 Like

So…

With a class member:

OwnedArray<dsp::IIR::Filter<float>>    m_lowPassArray;
    m_lowPassArray.clear();

    Array<dsp::IIR::Coefficients<float>> chebFilterCoefficients = dsp::FilterDesign<float>::designIIRLowpassHighOrderChebyshev2Method ((float)(m_dGourtzelSampleRate / 2.0), dSamplerate, fTransitionFactor, -0.1f, -299.0f);
    
    for (auto coeff : chebFilterCoefficients)
        m_lowPassArray.add (new dsp::IIR::Filter<float> (coeff));

it complains when coeff is destroyed…

    virtual ~ReferenceCountedObject()
    {
        // it's dangerous to delete an object that's still referenced by something else!
        jassert (getReferenceCount() == 0);
    }

You have an Array of ReferenceCountedObjects (dsp::IIR::Coefficients)… shouldn’t that be a ReferenceCountedArray ?

Cheers,

Rail

To fix this issue… here’s my changes:

https://www.dropbox.com/s/qamlr1kb182llxw/juce_dsp.zip?dl=0

This changes Array to ReferenceCountedArray in FilterDesign and updates the OverSampling code for the change.

Rail

Thank you for reporting!

https://github.com/WeAreROLI/JUCE/commit/1396d7cfc5233ba15c3f52f5fbcf62f5b9ee7e95

Damn… I think I made a copy paste error in the Oversampling2TimesPolyphaseIIR method:

    Oversampling2TimesPolyphaseIIR (size_t numChans,
                                    SampleType normalizedTransitionWidthUp,
                                    SampleType stopbandAttenuationdBUp,
                                    SampleType normalizedTransitionWidthDown,
                                    SampleType stopbandAttenuationdBDown)
        : OversamplingEngine<SampleType> (numChans, 2)
    {
        auto structureUp = dsp::FilterDesign<SampleType>::designIIRLowpassHalfBandPolyphaseAllpassMethod (normalizedTransitionWidthUp, stopbandAttenuationdBUp);
        dsp::IIR::Coefficients<SampleType> coeffsUp = getCoefficients (structureUp);
        latency = static_cast<SampleType> (-(coeffsUp.getPhaseForFrequency (0.0001, 1.0)) / (0.0001 * MathConstants<double>::twoPi));

        auto structureDown = dsp::FilterDesign<SampleType>::designIIRLowpassHalfBandPolyphaseAllpassMethod (normalizedTransitionWidthDown, stopbandAttenuationdBDown);
        dsp::IIR::Coefficients<SampleType> coeffsDown = getCoefficients (structureDown);
        latency += static_cast<SampleType> (-(coeffsDown.getPhaseForFrequency (0.0001, 1.0)) / (0.0001 * MathConstants<double>::twoPi));

        for (auto i = 0; i < structureUp.directPath.size(); ++i)
            coefficientsUp.add (structureUp.directPath.getObjectPointer (i)->coefficients[0]);

        for (auto i = 1; i < structureUp.delayedPath.size(); ++i)
            coefficientsUp.add (structureUp.delayedPath.getObjectPointer (i)->coefficients[0]);

        for (auto i = 0; i < structureDown.directPath.size(); ++i)
            coefficientsDown.add (structureDown.directPath.getObjectPointer (i)->coefficients[0]);

        for (auto i = 1; i < structureDown.delayedPath.size(); ++i)
            coefficientsDown.add (structureDown.delayedPath.getObjectPointer (i)->coefficients[0]);

        v1Up.setSize   (static_cast<int> (this->numChannels), coefficientsUp.size());
        v1Down.setSize (static_cast<int> (this->numChannels), coefficientsDown.size());
        delayDown.resize (static_cast<int> (this->numChannels));
    }

Please check it.

Thanks,

Rail

Ah, yes. I really thought our CI would pick something like that up. Time to write some more tests I guess.