Alternative to accessing ValueTree in processBlock?


#1

There’s lots of discussion here saying NOT to read or write to/from ValueTree’s in the DSP loop.

BUT, what are the alternatives?

for example, if i want to process MIDI input, and update the state of a parameter in my AudioParametersValueTreeState, how is the best way to do that?


#2

There are a few questions that could do with answering around the AudioParametersValueTreeState…

For this though.

Could you keep an AudioProcessorParameter pointer as a member variable in your processor ?

Then you could initialize/assign to that member pointer via the returned value from AudioProcessorValueTreeState::createAndAddParameter() so that you have a direct reference to the parameter held in the value tree via your member pointer.

Then you could use that member pointer in the processBlock and call:

myParamPointer->setValue(someValue);

That way your not interacting with the underlying ValueTree in the audio callback.

I’ve not actually tried this though and this may be well off the mark but could be worth a go ?

It’d be great to get a few answers to this stuff from the juce team including the whole asynch updater thing when sending a notification to a GUI attachment in response to parameter changes.

EDIT: Obviously only valid if your not talking about some derived parameter class you’ve implemented yourself and added manually to the underlying ValueTree. But if your talking about a parameter you added through createAndAddParameter() I think it should work ?


#3

I always guessed, but now I looked it up:
The parameters are not stored in the valuetree! The AudioProcessorValueTreeState combines a ValueTree and the list of parameters to make it easy to serialise the state of a plugin/processor. The parameter ends up in an OwnedArray in the processor.
Here is the code:

This makes perfect sense, if you remember, that the actual value of a parameter is not part of the plugin state, because it is depending on the host’s automation.
So there is no need to add a separate pointer to the parameter.

But for all values in the myValueTreeState.state you need to access them from outside the process block.
You can do e.g. as a separate Atomic anything; and update it from a ValueTreeListener… But I usually use them for non time critical stuff, like display settings etc. So there you are outside the audio thread…


AudioProcessorValueTreeState with branched ValueTree
#4

@daniel

Yeah, so would you just use AudioProcessor::setParameterNotifyingHost or something instead you mean ?

You still need to use the above or somehow get a reference to the parameter to actually set the value in the processBlock right ?

Whether you get a reference from the processor’s OwnedArray of params or whatever ?

Only issue is the AudioProcessorValueTreeState::Parameter class overrides setValue in a way which results in trigerring an asynch updater. Which I didn’t think was thread-safe to do on the callback. So I’m a little confused by all this.


#5

First I would avoid calling a setValue or setValueNotifyingHost from the processing callback. I cannot prove this, but it feels wrong to me.

Yes, but getting the parameter object via AudioProcessorValueTreeState::getParameter(StringRef paramID) does not involve any ValueTree operations, so the assumption using the parameter object would result in memory allocation was wrong.
As you say, it is a lookup in the processor’s OwnedArray returning a pointer, so no harm done IMHO.

You are not supposed to call setValue at all. As I understand the workflow is:

  1. something in the gui is changed -> call setValueNotifyingHost()
  2. the host saves that value and decides, if it is a general value or a value for it’s automation, depending on what the user selected (read/write/latch/touch…)
  3. the host calls setValue on your processor to perform the actual change of the parameter

If you call setValue yourself you override the automation of the host.

What you need to keep in mind: the setValue does not necessarily happen on a specific thread, so I usually read all values I need in processBlock to const floats at the beginning of the callback, that way the values should be as consistent as possible. I don’t know, if the host automation will respect the callbackLock.


#6

Yeah that was sort of my assumption.

Ahh yep I had missed that method! Thanks! It was more assuming that it would be incorrect to read any value via the AudioProcessorValueTreeState::state member. Which would be the case if adding some derived parameter class or object directly to the state. I made a real botch of that reply!

So essentially is the ValueTree for the parameters there primarily just for loading and saving state ?

Awesome. Thanks Daniel. Sorry I did know that…should have re-read docs before posting! I’ve never actually set a parameter value from the callback myself. Basically should this never be done ?

I think the way I’m handling things at the moment is fine. For any parameter that is read per process block (like a gain scaling or something) I would do the same as you suggested and read the value into a const float via getRawParameterValue or getParameter etc.

For parameter values that result in some further calculation when changed (like a filter cutoff changing and calculating coefficients) I usually implement some listener or lambda callback based approach which updates some dsp object or performs calculations in response to a parameter change.

Thanks for clarifying these points (been bugging me for a while).

Just to check then, is it common/best practice to never update parameters from the audio callback ?

(I need another thorough read through the parameter docs to get my head around the host behaviour I thinks…)

Thanks again for the advice.


#7

[quote=“daniel, post:5, topic:19298”]

  1. something in the gui is changed -> call setValueNotifyingHost()
  2. the host saves that value and decides, if it is a general value or a value for it’s automation, depending on what the user selected (read/write/latch/touch…)
  3. the host calls setValue on your processor to perform the actual change of the parameter
    [/quote]Interestingly (or annoyingly), setValueNotifyingHost() is hardcoded to call setValue() directly after. In the AudioProcessor, you can override the sendParamChangeMessageToListeners() to only deliver the notification of the value to the host, thereby avoiding potential feedback loops.

#8

Thanks @Mayae, I wasn’t aware of that. That’s indeed interesting. I wonder what it’s good for. The processor will not do anything without a host, so why should the wrapper forward the message risking to disagree with the host’s setValue call.

I think so, but I would be interested in a general advice too, as I test mostly on one host only.