Proper template declaration for dsp classes


#1

For some time I have been using my own template version of the IIRFilter class so that I can pass floats or doubles to my processBlock(). Recently, I have begun to move over to the new dsp classes and wish to accomplish the same thing, which I thought would be easy since the dsp classes are already template classes. But I am having trouble figuring out the right way to declare the initial member variables.

My code is as follows;

// PluginProcessor.h

class PluginAudioProcessor  : public AudioProcessor
{
public:

    PluginAudioProcessor();

    bool supportsDoublePrecisionProcessing() const override { return true; }

    void processBlock(AudioBuffer<float>& buffer, MidiBuffer& ) override
    {
    	dsp::AudioBlock<float> block(buffer);
	process(dsp::ProcessContextReplacing<float>(block));
    }

    void processBlock(AudioBuffer<double>& buffer, MidiBuffer& ) override
    {
	dsp::AudioBlock<double> block(buffer);
	process(dsp::ProcessContextReplacing<double>(block));
    }

private:

    template<typename T>
    void process(dsp::ProcessContextReplacing<T>);

    using GainProcessor = dsp::Gain<double>;
    using FilterProcessor = dsp::ProcessorDuplicator<dsp::IIR::Filter<double>, dsp::IIR::Coefficients<double>>;

    dsp::ProcessorChain<GainProcessor, FilterProcessor> chain;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginAudioProcessor)
}

//==============================================================================
// PluginProcessor.cpp

template<typename T>
void PluginAudioProcessor::process(dsp::ProcessContextReplacing<T> context)
{
    ScopedNoDenormals noDenormals;

    const float peak = (*pluginState.getRawParameterValue(Peak));
    const float peakHz = ((*pluginState.getRawParameterValue(PeakHz)) * 1000.0f);//convert to kilohertz
    const float peakQ = (*pluginState.getRawParameterValue(PeakQ));

    chain.get<0>().setGainLinear(*pluginState.getRawParameterValue(Trim));

    *chain.get<1>().state = *dsp::IIR::Coefficients<T>::makePeakFilter(getSampleRate(), peakHz, peakQ, peak);
    chain.process(context);
}

I need GainProcessor and FilterProcessor to be templates. But I have not figured out a way to declare them as such.

Any help is appreciated, or perhaps I need a different approach?


#2

I think you wanted to write:

template <typename T>
using GainProcessor = dsp::Gain<T>;

template <typename T>
using FilterProcessor = dsp::ProcessorDuplicator<dsp::IIR::Filter<T>, dsp::IIR::Coefficients<T>>;

#3

Thank you. That is what one would think should work, but it does not compile. At least, it does not compile in VS2017! The error is “only static data member templates are allowed”.

I know there must be a way to do this…


#4

Could you copy-paste the complete compiler error? Depending on which line is producing that error, this could mean different things.


#5

Below is the error from VS2017;

1>c:\aaxprojects2\Plugin\source\PluginProcessor.h(111): error C3376: 'PluginProcessor::chain': only static data member templates are allowed (compiling source file ..\..\Source\PluginEditor.cpp)
1>c:\aaxprojects2\Plugin\source\PluginProcessor.h(111): error C3376: 'PluginProcessor::chain': only static data member templates are allowed (compiling source file ..\..\Source\PluginProcessor.cpp)
1>..\..\Source\PluginProcessor.cpp(32): error C3245: 'PluginProcessor::chain': use of a variable template requires template argument list
1>c:\aaxprojects2\Plugin\source\PluginProcessor.h(111): note: see declaration of 'PluginProcessor::chain'

I am just not seeing how to form the syntax…your help is greatly appreciated!


#6

:warning: Disclaimer: I am not familiar with these classes, so I might say something stupid. My solutions are only “correct” from the C++ POV, but there are maybe not correct for JUCE, or for what you want to achieve.

chain cannot be of type dsp::ProcessorChain<GainProcessor, FilterProcessor>, because GainProcessor and FilterProcessor are not types. GainProcessor<double>, GainProcessor<float>, FilterProcessor<double> and FilterProcessor<float> are types.

So the following would be valid:

dsp::ProcessorChain<GainProcessor<double>, FilterProcessor<double>> chain;

but I guess it’s not what you want.

If you want chain to be either a dsp::ProcessorChain<GainProcessor<double>, FilterProcessor<double>> or a dsp::ProcessorChain<GainProcessor<float>, FilterProcessor<float>>, then I guess you have to make PluginAudioProcessor a class template.

template <typename T>
class PluginAudioProcessor : public AudioProcessor
{
public:
    PluginAudioProcessor();

    bool supportsDoublePrecisionProcessing() const override { return true; }

    void processBlock(AudioBuffer<T>& buffer, MidiBuffer& ) override
    {
        dsp::AudioBlock<T> block(buffer);
        process(dsp::ProcessContextReplacing<T>(block));
    }

private:
    void process(dsp::ProcessContextReplacing<T>);

    using GainProcessor = dsp::Gain<T>;
    using FilterProcessor = dsp::ProcessorDuplicator<dsp::IIR::Filter<T>, dsp::IIR::Coefficients<T>>;

    dsp::ProcessorChain<GainProcessor, FilterProcessor> chain;
};

#7

Thank you. But that doesn’t work because we need the float/double specializations for the template.

And this is why I posted here. The answer seems to be obvious, but it is not!

I have tried everything I can think of. So, I am at the point of needing help from those more knowledgeable.


#8

we need the float/double specializations for the template.

Of course! Stupid me :sweat_smile:

Last idea: having two members (floatChain and doubleChain) and then passing the right member to process().

// PluginProcessor.h

class PluginAudioProcessor  : public AudioProcessor
{
public:

    PluginAudioProcessor();

    bool supportsDoublePrecisionProcessing() const override { return true; }

    void processBlock(AudioBuffer<float>& buffer, MidiBuffer& ) override
    {
        dsp::AudioBlock<float> block(buffer);
        process(dsp::ProcessContextReplacing<float>(block), floatChain);
    }

    void processBlock(AudioBuffer<double>& buffer, MidiBuffer& ) override
    {
        dsp::AudioBlock<double> block(buffer);
        process(dsp::ProcessContextReplacing<double>(block), doubleChain);
    }

private:
    template <typename T>
    using GainProcessor = dsp::Gain<T>;
    template <typename T>
    using FilterProcessor = dsp::ProcessorDuplicator<dsp::IIR::Filter<T>, dsp::IIR::Coefficients<T>>;
    template <typename T>
    using ProcessorChain = dsp::ProcessorChain<GainProcessor<T>, FilterProcessor<T>>;

    template <typename T>
    void process(dsp::ProcessContextReplacing<T>, ProcessorChain<T>& chain);

    ProcessorChain<double> doubleChain;
    ProcessorChain<float> floatChain;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginAudioProcessor)
}

//==============================================================================
// PluginProcessor.cpp

template<typename T>
void PluginAudioProcessor::process(dsp::ProcessContextReplacing<T> context, ProcessorChain<T>& chain)
{
    ScopedNoDenormals noDenormals;

    const float peak = (*pluginState.getRawParameterValue(Peak));
    const float peakHz = ((*pluginState.getRawParameterValue(PeakHz)) * 1000.0f);//convert to kilohertz
    const float peakQ = (*pluginState.getRawParameterValue(PeakQ));

    chain.get<0>().setGainLinear(*pluginState.getRawParameterValue(Trim));

    *chain.get<1>().state = *dsp::IIR::Coefficients<T>::makePeakFilter(getSampleRate(), peakHz, peakQ, peak);
    chain.process(context);
}

#9

Great! That is working! Thank you for your help!

I still have a nagging suspicion that there may be another way to do it,… a whole different approach.

But for now, it compiles and runs.

Thank you!


#10

@McMartin’s code looks good to me.


#11

Thank you. I wanted to make sure I was using best practices.

I think it would be nice to add similar capability to the demos for future reference.