Any examples of using AudioProcessorValueTreeState for handling parameter changes?


#1

Hello JUCErs,

I couldn’t find an example of using AudioProcessorValueTreeState. Do you know of an open-source plugin I can look into?

In the “audio plugin demo”, only AudioProcessorParameter is used and processing code directly uses the params without any mirroring of the state (no update of custom objects on every change of parameter - just use latest values) and no locks are used, which doesn’t seem thread-safe (AudioParameterFloat, for example, doesn’t keep an atomic value or a lock)… is it?

I am reading the forum for some time now and found a couple of mentions of the AudioProcessorValueTreeState, but no working examples to follow.

What I want to achieve:

  1. No complete UI (or processing) state updates - just the changed value (no timer updates).
  2. Thread-safety for read & write to params, but lock-free on the Audio thread.

I guess the APVTS would only help with task #1?

Any help is much appreciated.


Parameter won't update in DAW from GUI
#2

You can do this & its pretty straightforward. Not aware of any example projects (there may be some, I have not looked), but some good examples are shown here -

and here -


#3

First one is an example of something that doesn’t work. Second one I’ll give a try.

Thanks.


#4

Here is a simple example. Not perfect, but it works here.

In processor.h —
UndoManager undoManager; AudioProcessorValueTreeState params;

//Define the parameter's id and name
#define P_ID_OSC1_KYTRKD "KYTRKD"
#define P_NM_OSC1_KYTRKD "Key Tracked"

In processor.cpp —
//Instantiate the AudioProcessorValueTreeState and UndoManager
TestAudioProcessor::TestAudioProcessor()
: undoManager(3000, 30), params(*this, &undoManager)

//make a range
NormalisableRange<float> rngZeroToOne (0.0f, 1.0f, 0.0001f);

//create the parameter in the constructor with the name, id, and range
params.createAndAddParameter (P_ID_OSC1_KYTRKD, P_NM_OSC1_KYTRKD, P_NM_OSC1_KYTRKD, rngZeroToOne, rngZeroToOne.snapToLegalValue(0.0f), nullptr, nullptr);

in Editor.h —
ScopedPointer<AudioProcessorValueTreeState::ButtonAttachment> keyTrackToggleAttachment;
ScopedPointer<ToggleButton> keyTrackToggle;

In Editor.cpp —
//in constructor
keyTrackToggleAttachment = new AudioProcessorValueTreeState::ButtonAttachment (*p.getParamValueTree(), P_ID_OSC1_KYTRKD, *keyTrackToggle);

Good luck!


MaxMsp/Gen Param -> Slider linking
#5

I took a day off and created one, look here:

Have Fun!


#6

Awesome example, @daniel . Thank you.

A few questions:

  • What if my plugin is a graph of processors and some of them are a blackbox to me - then I can’t just use AudioProcessorValueTreeState for the nodes I don’t have control of internal state (their processBlock would read private vars which need to be set externally on change). In this case I need to mirror the state back to the blackbox processor on every update - do I add a listener for every param of that processor?
  • I guess since changes are coming just one way (UI->Processor) this is guaranteed to be lock-free, right? But what do I need to do if I need to make an update in the opposite direction - is there a way to update a param and somehow instruct it not to propagate the change to listeners?
  • One unrelated to the issue of the topic question: is there a need to set ScopedPointers to nullptr in the destructor of the class?

Cheers.


#7

I think so, but maybe somebody else knows a more elegant way…

I didn’t see a possibility to disable a listener temporarily. If you need to customize that, I would suggest to add a flag in the listener callback.
I also experimented around with different update mechanisms, you can try an AsyncUpdater, but for me the best was to use the Attachment classes for gui knobs/buttons and use a Timer for readonly displays (like meters and stuff), which simply repaint snapshots. Just make sure, you have minimal reading code, e.g. I usually have an Atomic with the maximum level filled in the processBlock, which my meter component reads for displaying.

No, you don’t need that. It is only necessary, if you want to define in which order the objects are destructed (in my case the AudioProcessorValueTreeState holds a pointer to the UndoManager, so I destroy it before the tree).
But I think the tree wouldn’t fail if the order was the other way round, so it doesn’t matter anyways


#8

Thanks a lot @daniel! The awesomeness is strong with this one!


#9

Thanks Daniel for that example.
just a small thing : I think that something like that is probably missing in FftapeDelayAudioProcessor (?)

mState->addParameterListener (paramGain, this);
mState->addParameterListener (paramTime, this);
mState->addParameterListener (paramFeedback, this);

#10

Hi @lalala,
you are right. I missed that when changing from reading each time in processBlock to using the listener method.
When hearing it again I wish to spend some time with the DSP code again to make it resample etc…

I added it now. Thanks for noting.


#11

Adding my thanks for your example @daniel - helped me add some GUI-linked parameters to my AudioProcessor with ease.


#12

Thanks for the example this was very helpful!!!


#13

Thanks everybody, I am glad you all liked the minimal AudioProcessorValueTreeState example. Actually I did another example with proper GUI and using the DSP module, also Open Source: