Code design for better abstraction of parameters

I have nodes (juce::AudioProcessor), and I need to process the audio based on the parameters for each node. for each parameter can be a constant value, an AudioBuffer, vector, or a midiMessage. while processing nodes, won’t there be way too many if statements ? Modulating a parameter from the result of other things is pretty common, I just want to know what is a good way of handling this situation.

Often when writing DSP we have the possibility that a parameter can be either changing or ‘static’ (same all the time).
If that parameter represents something like a filter frequency, then the transformation of that parameter to some filter coefficients might be quite expensive (in terms of the amount of CPU). and it doesn’t make sense to calculate those coefs again and again in the case when the parameter is static.
So the common approach is to make two versions of your process function, one where the parameter is being modulated, and a ‘lighter’ version when the parameter is not changing.
This sounds pretty manageable until you consider the case of a plugin with many such parameters. for example if you had two parameters, with cases for any combination of ‘changing’ / ‘static’. You would need to write 4 process functions (to have optimal efficiency). With 4 parameters there are 16 permutations, with 5, 32 permutations. i.e. you are dealing with a ‘combinatorial explosion’ which can easily become unmanageable.
The solution is to use ‘code-generation’, by using templates for example.

In GMPI, we write a process function something like…

template< int isModulatingPitch, int isModulatingResonance>|
void subProcess( int sampleFrames )|
{
    if constexpr (isModulatingPitch)
    {
       // perform some expensive calculation here
    }

and we can use that template to instantiate many optimized variations of the function without manually duplicating a lot of code. The compiler will completely remove any constexpr section that are not enabled, so you can acheive a very efficient function.
Rather than having some complex series of if statements to choose the correct function, GMPI stores a member-function-pointer that points to the ‘current’ process function. i.e. once the plugin has decided which instansiation of the template to use, you store a pointer to that function. Then the ‘main’ process function can delegate to the appropriate optimised function without a lot of overhead.
I hope that makes sense.

3 Likes

This is exactly what I was asking for, Thank you.

1 Like