I have a weird issue that I can’t quite track down. My hope is that someone around here can provide me with some advice on how to move on.
I have an AudioProcessor that is registered as a listener on its AudioProcessorValueTreeState. I want to modify some parameter value from the GUI and get a callback in the AudioProcessor.
In the GUI, I have a collection of buttons that modify a Value and set it to specific values when pressed. I connect this value to the AudioProcessorValueTreeState like this:
When I press one of the buttons, I can track that the underlying Value changes. In fact, as it now refers to data in the AudioProcessorValueTreeState, I can actually see ValueTreePropertyValueSource::setValue() being called. What is weird is that my plugin processor doesn’t receive a call to parameterChanged() when it runs in certain hosts (other hosts or the standalone version work fine). I stepped through the code with the debugger in various ways, but there is so many callbacks happening in result to the call to ValueTreePropertyValueSource::setValue() that it is almost impossible to find out where the call to my AudioProcessor gets lost.
What I do know is that, when the callback arrives, it arrives as a consequence to a call to AudioProcessorValueTreeState::valueTreePropertyChanged. This call doesn’t happen when I test it in the problematic host, so my callback gets lost somewhere before that.
Now I’m wondering what strategy I can use to narrow this down, especially since it only happens in a certain host. I would be grateful for any hints.
Here’s the conclusion: A Value retrieved from the AudioProcessorValueTreeState gets detached from the tree if the content is replaced by a call to AudioProcessorValueTreeState::replaceState(). As this is the recommended way of loading a preset, I think that this must be a bug.
The long story
The ValueTree seems to consist of several subtrees, one for each VST parameter. I temporarily modified ValueTreePropertyValueSource::setValue() from this
It seems as if the part of the tree that is responsible for this specific parameter got detached from the ValueTree but I’m not doing anything specific with that in my plugin code.
After some more debugging I noticed that the fault only occurs somewhere after my constructors are done.
I added auto xmlStr = tree.getRoot().toXmlString(); to all other callbacks in the ValueTreePropertyValueSource and I noticed that when I recall a preset with
I get a call to ValueTreePropertyValueSource::valueTreeParentChanged(). When I observe the xmlStr in this callback, it shows the detached tree. This means that the connection between the tree and the Value breaks when the tree state is replaced.
Here’s a temporary fix that seems to work for our product. Instead of treeState.getParameterAsValue(ID)
you would write APVTSValueAdapter::getParameterAsValue(treeState, ID).
I would still prefer a true solution to this.
Here’s an update. The Problem could potentially be fixed with a change in the APVTS itself:
void AudioProcessorValueTreeState::replaceState (const ValueTree& newState)
ScopedLock lock (valueTreeChanging);
// This was the original line:
// state = newState;
if (undoManager != nullptr)
It depends on how the listeners are attached to the APVTS. We had issues when we attached as a Value::Listener to the actual Value objects inside the value tree. When you call replaceState you swap out the actual value tree objects. You listeners will then be refering to Value objects that are no longer a part of the value tree and the value tree will consist of new Value objects that your listeners know nothing about. The suggested change fixes this issue, because copyPropertiesAndChildrenFrom leaves the existing objects in place and copies their actual values. Thus, the Listeners still refer to the Value objects in the tree and continue to receive updates when the values in the tree change.
If you attach your listeners as a RangedAudioParameter::Listener (or similar) this will probably not solve the issue, but you could still try it just in case. In any case, some additional digging and debugging should help you narrow down why things don’t work as expected. We never had problems with RangedAudioParameter::Listener but we also moved to a custom parameter system a while back… My info may not be completely up to date.