How are people handling AudioProcessorValueTreeState::LIstener callbacks?

Hi all,

Just a quick question…sorta.

How are folks handling the parameterChanged() callback inside AudioProcessorValueTreeState::Listener ?

I’m working on a project and decided I’d use the new parameter classes as until now I’ve just had my own subclass which takes in a lambda in the constructor that acts as a callback function whenever my derived parameter class’s setValue() method is called.

I have some classes (processing units etc. that are members in my audio processor) that ideally I don’t want to be inheriting from AudioProcessorValueTreeState::Listener as I’d like the classes to function outside of JUCE based projects.

So at the moment the way I can see things working is to add the processor itself as a listener which is registered to each of the value tree parameters one by one and then implement the parameterChanged() method/callback in my audio processor.

This feels kinda messy though. It’ll mean a big old if else or switch type block to check the ID of the parameter changed and then call the relevant processing code.

My possible ideas were to have something like:

std::map<String, std::function<void(float)>> parameterCallbacks;

This would be a basic key value setup in which the key is the parameterID and the std::function is a callback/lambda function to be called when the parameter with the relevantID is changed. That avoids the big messy conditional block checking against String ID’s in the parameterChanged() callback in my AudioProcessor.

The parameterChanged() callback can just find the callback with the parameterID key and call the std::function object in response to the change. No big if else checking block.

It all feels a little less than ideal though. I feel like I’m missing something in the way these classes are intended to be used. The various GUI attachments are really useful and save a lot of boiler plate so I would like to make this work if possible. At the moment it feels a little smelly…

Anyone have any advice ?



NOTE: This sort of relates to my other post:

This doesn’t sound wrong to me if you need to do some extra processing when the parameters change. However, the idea behind AudioProcessorValueTreeState is that it stores the values for you. So if you are simply copying ValueTree/Parameter values to some member variables in your callback then that’s not really necessary - in fact you are storing the values in two places which seems wrong to me.

You should be getting the values straight out of the AudioProcessorValueTreeState at the beginning of your audio callback and - depending on the parameter - copy them to a local variable (to avoid tearing).

Also, note that you do not need to use AudioProcessorValueTreeState::Listener as your callback method. As the values are stored in a ValueTree you can also add a Value::Listener to the individual values.

But I’ve also been thinking about adding a way to add simple lambdas as listeners in the Value class. That would be useful.

Hi @fabian ,

It’s not so much that I’m saving the parameter values in two places.

More that it saves the computation every time in the processBlock(). So for example if I have a parmeter for a filter’s cutoff frequency I would just call myFilter.setCutoff(param.getValue()) in the listener callback and not at the start of every process block.

Does that approach not sound quite right to you ? I thought it made more sense but I’m still working my head around the best way to handle the various parameter use cases.

EDIT: Are you saying this as an appropriate approach for parameter changes which result in calculations like filter coefficient calculations etc ? For raw values like a gain param that will be applied to the process buffer reading the parameter in the processBlock makes sense. I think that is what your trying to say!

This doesn’t sound wrong to me if you need to do some extra processing when the parameters change.

Being able to add a lambda as a listener would be a great feature!

Cheers for taking the time to read through this one.