Best Practice for getting APVTS parameter values in Editor

I have been exploring the new (i.e. Juce 5.4) AudioProcessorValueTreeState, following the steps outlined in the Saving and loading your plug-in state tutorial. (So NOT using the createAndAddParameter method, but instead using the new AudioProcessorValueTreeState constructor with a ParameterLayout.) Got the basics working in a demo, and then started trying to implement an APVTS in an older plug-in that hadn’t used a value tree at all (was previously using the addParameter method).

What I’m wondering about now is the best practice for accessing the parameter values of the APVTS from within the PluginEditor. (In this case it happens to be from within the paint method.) There are at least three ways I can see to grab parameter values from there:

  1. valueTreeState.getParameter("foo")->getValue
  2. *processor.fooParam
  3. fooSlider.getValue()

(This is assuming that the params are set up like in the aforementioned APVTS tutorial. That is, fooParam is a Processor member set up as a pointer using getRawParameterValue, and fooSlider is an Editor member connected to its corresponding APVTS parameter via a SliderAttachment.)

So which of these three is the best way to grab a parameter value from within the Editor? “Best” as in ease of implementation, but also “best” as in following the intended JUCE design pattern.

If you’re reading along closely, you’ll realize there’s a couple oopsies in options 1 and 2.

For #1, that would actually return the normalized 0-1 value rather than the “full-range representation”, so you’d also have to use RangedAudioParameter::convertFrom0to1 to make that conversion.

For #2, the Editor can’t access fooParam if it’s set up as a private member of the Processor, as shown in the tutorial. Only works if fooParam is made public. But apparently those “RawParameterValues” are the most efficient way to access a value, since it’s the recommended way to grab parameter values from within the processBlock.

#3 seems the most immediate way, since Sliders are already Editor members. But Sliders are tied to parameter values via the Attachment classes, the full nature of which I don’t really understand, so I’m not sure if there are some caveats there. Plus getting data values via GUI objects seems weird, rather than going straight to the source. Also, what if you had some parameters in your tree without corresponding GUI components at all?

Thanks for considering this issue. C++ is not my native language so please pardon any coding blunders I may have made within.

2 Likes

No replies yet on this post. I’m wondering if I made the question too broad. So, in an attempt to focus it, let me ask this:

Say I’ve got a plug-in set up following the design pattern from this tutorial.

Is there anything wrong with making the float* gainParameter = nullptr; members of the Processor be public rather than private? This way the Editor could also use those same pointers to get parameter values from the AudioProcessorValueTreeState.

Or, since I’m using attachment classes, I could get those same parameter values from within the Editor with gainSlider.getValue().

The latter approach maintains the encapsulation of the Processor class better, but it still seems a bit goofy to be grabbing parameter values via a GUI object (rather than more directly from the AudioProcessorValueTreeState itself). There are of course other possible approaches, but I’m trying to keep this focused…

I think there were no replies, because no one is confident enough to catch every possible issue.

The problem is, there is the Editor, which is updated in the message thread, so not too problematic. Then there is the audio thread inside processBlock. Here you can already create problems. But the worst is, when the host sends automation data. Most hosts synchronise this with the audio thread, but afaik ProTools will use a separate thread to update automation data.

What I personally do is, I rely on the APVTS::XyzAttachment classes to connect the sliders and comboboxes to the individual parameters.

In the processor, for scalar values I set a float* in the constructor. According to the documentation this pointer is safe to be used inside processBlock.
If it is a parameter, say for a dsp::IIR::Coefficients, I use the parameterListener, that will trigger the creation of new Coefficients, since the dsp module is designed to be safe, when the coefficients change not synchronous with the audio thread.

I am looking forward to additions…

3 Likes

Also the “best” way would be rather subjective… Personally I use the GUI component values in my plugin editors instead of grabbing the direct values from getRawParameterValue() or even accessing the tree parameters via tree.getParameter()->getValue().

Here I would say it’s “best” because it’s more predictable as to what value you can expect to see relative to the UI state… also there’s the whole topic of how JUCE’s parameters don’t provide thread synchronisation (warning, rather complex discussion!).

If you’re using raw parameter values from your editor they may likely be at a different vaule from the GUI state, since the GUI component values are only updated every so often from the APVTS and parameter attachment objects. These component value updates happen on the message thread so your code reading slider, button, etc. values will only happen either before they get a value update or after.

The poster is asking how to mirror a parameter in the GUI that apparently also already has a Slider with an Attachment inserted. So it’s bit of a special case that doesn’t appear that often. I don’t suppose there’s any optimal solution for that, just do something that works…

Thank you all for your replies.

To respond to @TonyAtHarrison’s post: Agreed, the “best” way is subjective. As I mentioned in my first post, I was thinking “best” in two senses: “as in ease of implementation, but also ‘best’ as in following the intended JUCE design pattern.”

Definitely gainSlider.getValue() has ease of use. But I didn’t know if there was some concept in the JUCE design pattern that would tell you to always trust the tree and not its derivative elements (e.g. attached sliders).

But you bring up an interesting point that grabbing values from tree-attached GUI components will keep those values in sync with the GUI. That’s an important practical consideration here, since I’m using these values in the Editor’s paint method, so I (obviously) want all the parts of the GUI to be in sync with each other.