RT read/write <> GUI read-only structure

Hello,

I’m using an XML specifications file that allows my plug-ins to dynamically infer an AudioProcessorValueTreeState::ParameterLayout for the processor, plus create a GUI in the editor, and also automatically make any attachments between Components and parameters. This works like a charm for plug-in parameters (exposed to the host).

But I’d like this flexible mechanism to also support “realtime properties”. They would be properties that belong to the processor. The processor could read/write them (from processBlock()). But the editor could only read them. And would not be visible to the host (they also don’t need to be stored/recalled).

Of course, I can achieve this manually (like using atomic variables in my processor and read them by polling in my editor), but my wish is to have something with the same benefits of an APVTS: named parameters, lambdas for string<>value conversion, etc.

Frankly, I though about using another APVTS but they have to be tied to a processor, and I’m less than sure about thread safety concerns…

Any suggestion or advice?

Thanks a lot.

I am interested in this idea as well.

I am in the midst of porting a project into JUCE, that used a proprietary library combined with a much earlier version of JUCE. For parameter management, this proprietary library had a group of classes that defined Parameters, a ParameterListener, a non-blocking, non-allocating ParameterQueue, etc… Each Parameter had a isAutomatable bool that controlled whether it would be visible to the host and automatable, or not, so user-controllable parameters were mixed right in with “realtime properties.” It was interesting to see a totally different approach to parameter management.

So in porting this project into modern JUCE, I am in a similar spot to what you describe. I’ve moved the user-controllable parameters to APVTS management, and then I’ve given the Processor a handful of atomics for exposing realtime properties for the Editor to poll. But it does feel a bit ad-hoc, and I’ve wondered about ways to consolidate organizing those properties (including, as you mention, using a second APVTS – but for the same reasons you mention, decided that wasn’t a great idea).

At the moment, I don’t have an great advice on a better solution, but I’m here for the conversation about it.

1 Like

For the non user-controllable parameters, have you thought about just adding them as properties to the APVTS state member? You’d have to enforce the read-only editor access yourself, but that would achieve some amount of consolidation.

Yes, that would be convenient, and that would consolidate the properties, but I thought that state member of APVTS is just a plain old ValueTree, which would make it not thread-safe.

1 Like

Also, they’d be stored along with the processor’s state, which is not needed (well, at least in my case).

Today I wrote a very simple RealtimePropertySet class which encapsulates a std::map<String, std::atomic<float>>. The processor (which owns it) can emplace properties from my XML file. I tried to update some values from processBlock() and was able to poll from the editor.

The editor also has an OwnedArray<RealtimePropertySliderAttachment> where RealtimePropertySliderAttachment is just a simple struct to bind a Slider* to the property ID.

This is just a naive test but I’ll keep going to see where this goes.

Most importantly, this work is heavily inspired by the Jukebox SDK from Reason Studios, which is now publicly available.

https://developer.reasonstudios.com

Right, storing these values is not needed. The use case I often hear for AudioProcessorValueTreeState::state is to use it store GUI window sizing/positioning – i.e. something you do want stored, but also something you don’t want to allow host automation access to.

I’m curious how you are handling access to the setter/getter methods of this class. As you stated above, you want the Processor to write these values, and the Editor to only read them, right?

Also, over in this thread, there has been discussion about not reading/writing atomics per-sample, and instead limiting access to atomics as a per-block operation, for performance/optimization reasons. So even if you encapsulated all the atomics within a RealtimePropertySet, you might still need a collection of floats to temporarily hold these values before writing them to the atomics at the end of the processBlock. (And those should in many cases be floats created “locally” on the stack, within the processBlock scope.) Just pointing out that attempts to keep all the variables needed for this idea in one tidy place starts getting a bit messy.

I’m curious, which part of the Jukebox SDK is inspiring this? I haven’t looked at that Reason stuff in years.

Here’s what is basically and currently happening inside processBlock() :

auto inputMeterValue = dsp.calculateInputMeterValue (buffer.getReadPointer (0), buffer.getReadPointer (1), buffer.getNumSamples());

// [rendering code]

rtps.setPropertyValue ("input_meter", inputMeterValue);

So, yes, I’m using a local variable but that’s not a problem to me. What I do care about is the editor’s ability to be self constructed and updated without a single line of code. So far so good: I do not even have a PluginEditor compilation unit.

Regarding the read/write access, both APVTS and RTPS are protected members of the processor class, which in turn provides a getRealtimePropertyValue (const String paramID) proxy method for the editor.

The Jukebox SDK part inspiring this is inside the Motherboard Object Model where you define custom_properties with specific ownership (GUI, Document, RTC, RT).

https://developer.reasonstudios.com/documentation/rack-extension-sdk/4.2.0/jukebox-concepts#_property_ownership

OK. Sounds pretty slick, having a self-constructing Editor!

One other thought would be to use an Identifier instead of a String for the property names. It could make the lookup faster when searching for a property in the std::map (I don’t know without testing though), and it would eliminate parameter name typos when coding (compiler wouldn’t complain about a string literal "imput_meter", but it would let you know that propertyIDs::imput_meter is not valid).

You’re absolutely right and that’s what I tested first, but for some reason (which I can’t even remember), I got errors trying doing so, especially when trying to create an Identifier from a property name extracted from my XML spec file. Probably something stupid I missed. I’ll have a look again.