Skip to content

Optimization - create a "Equalizer band parameter group" factory method to generate and process an array of "Equalizer band parameter group" #84

Description

@nathanjhood

Chore

Describe the chore

I have successfully moved the Biquads DSP into one of my favourite C++ constructs - the auto-ranged 'for' loop - meaning that each audio block is now processed by a Biquad array, before the next block comes in.

This is a huge improvement over the previous method, where the same block was being passed from Biquad to Biquad per sample-tick, which was creating classic audio block artefacts (crackling/noise, focused in the high end of the audio spectrum).

Interesting side-note: in developing the above, JUCE's 'declare non-copyable with leak detector' macro really shone a light on the exact syntax to make sure we're not making any copies of the processing array by mistake... this macro might be very handy for a generic Var class that we might validate non-copying on the audio thread DSP as a unit test in the future? hmmm...

Now, I am keen to do the same array-iteration/range-based-for-loop thing for the parameters, which are now all tidily grouped together, given the current build's project version number for compatibility control (very nice feature!) and all sit under a global parameter group, which is also versioned nicely.

The tricky part is the construction and initialization of an unknown number of parameter groups, containing as-yet unspecified parameters. Consider the labels and unique ID's which all need to be known at construction/initialization, but accept string classes.

After giving this some preliminary consideration, I am thinking that the best thing to do might be:

  • Define an object, which is a juce audio parameter group, containing one frequency float, one gain float, one resonance float, one type choice, and one bypass boolean
  • Define a factory function for constructing one of the above object on-call
  • When calling 'createParameterLayout', pass in the size_t of BiquadArray, which we define globally, and use it to construct an array (or container class of some sort) of size BiquadArray, with each element in the array being a call to the EQ band parameter group factory method
  • If my logic is sound (aside from being abstract), then we should be able use this global size_t BiquadArray to drive the generation of two array types; one containing the DSP processors, and one containing a matching group of parameters.

One current benefit/issue (concurrently) in the current implementation is that the entire list of params is being updated on every new audio block... this means that parameters should be updatable simultaneously, perhaps over Midi or channel data of some sort (also think CLAP, VCV Rack, etc). This is also a drain, as the entire thing is written in a big ugly list, that all gets processed one at a time, per call to update().

It will be much better to use some sort of looping construct to processes each param group 'for each' DSP processor, but ideally, while maintaining this 'non-blocking' principle, where a user should in theory be able to update two un-related parameters from two different equalizer bands at the same time, and not have one block the other. If blocking between params and/or param groups cannot be avoided, then it must be managed with logic and reason (haw haw).

This doesn't really need to be a blazing fast CPU fest at all... but those repetitive lists of params and updates is quite an eyesore for a language as rich as C++, and most of the time, using these more modern constructs leads to much safer and more performant code (plus the things you learn in doing so).

Additional context

I created a size_t of 4 which is the number of Biquads to hold in the array. I feel that this var might actually sit nicely at a more global level, where it can be referenced as a single source of truth for how many "equalizer bands" our plugin should manage; both DSP array, and the number of parameter groups to be generated.

This size_t must be a compile-time constant, probably via a constexpr, since we are not intending to attempt dynamic creation and deletion of N number of bands at runtime. If we were ever to go down the route of only generating the visual elements for the active number of bands, then that should not be dynamically constucting and deconstructing any DSP classes, nor parameter groups. I advise creating groups in pow2 numbers, with an ideal limit being 16 bands (think 16 MIDI channels). The requirement in such a case would be to have 16 bands - and their corresponding parameters - constructed on plugin instantiation, and then the "show/hide" of inactive bands would only pertain to graphical rendering, such as in our Editor unit - which is currently not implemented at all.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions