Safe to add value tree listeners on any thread?

muliple threads reading the same value tree maybe only audio thread manipulating with a command fifo?
do we have to lock anything like the message thread?

It is not safe to add ValueTree listeners on any thread. In fact, none of the ValueTree methods are thread safe. While it is technically possible to put a lock around a ValueTree, in practice it turns out to be a real hassle, and a much better option is to enforce that you only ever interact with a ValueTree on the message thread, or at least on a single thread of execution (and certainly not the audio thread).

What is it that you are trying to synchronize between threads? If we know more, we might be able to suggest other ways of achieving your aims.

I have a copy of a synth voice I need to read from AVPTS on another gui thread. Right now I read from AVPTS with a listener on the audio thread and set atomic floats. But I have value tree manipulation on array for multi segment envelopes. How do we listen for changes to our params? Everyone just polls? I use this class from fabian for dynamic allocation of vectors from message thread to audio thread. I need a background thread that renders a synth voice for the gui. its to slow to put everything on the message thread

the rendering of that copy voice is too slow if that makes sense even with Call Async.



    void setMainMSEGChanged(mseg_vec mseg) override {
        
        farbot::RealtimeObject<mseg_vec, farbot::RealtimeObjectOptions::nonRealtimeMutatable>::ScopedAccess<farbot::ThreadType::nonRealtime> coeffs(audioProcessor.subLevelMSEG);
            *coeffs = mseg;
        
    }

I read from avpts in audio thread like this this is okay?

    void parameterChanged (const juce::String &parameterID, float newValue) override{
        if(auto * subsound = static_cast<SubSound*>(synth.getSound(0).get())) {
            if(parameterID == Params::kSubLevel) {
                subsound->setSubLevel(newValue);
            }else if (parameterID == Params::kSubPitchHi) {
                subsound->setSubPitchHi(newValue);
            }else if (parameterID == Params::kSubPitchLow) {
                subsound->setSubPitchLow(newValue);
            }else if (parameterID == Params::kSubPitchDecay) {
                subsound->setSubPitchDecay(newValue);
            }else if (parameterID == Params::kSubPitchVelocity) {
                subsound->setSubPitchVelocity(newValue);
            }
        }
        
    }

Don’t confuse ValueTree and APVTS. The APVTS is not a ValueTree, it has a ValueTree.
addParameterListener is very different from valueTree.addListener!

But indeed, polling i.e. read the value once at the beginning of your processBlock or subroutine is the way to go. This makes sure the audio is really independent from anything else like AsyncUpdater that posts on the message queue.

sometimes that polling method eats up a lot of cpu .getRawParameterValue but i guess thats fine know of anything faster?

juce::Identifiers instead of strings?

getRawParameterValue does indeed a string lookup which can be avoided.
Ideally make a raw pointer with the correct AudioParameter type and call get(). That has many advantages like getting it in the right range and the right type (i.e. for boolean etc.)

// make the parameter a member:
juce::AudioParameterFloat* gain = nullptr;

// in the constructor after the parameterTree setup initialise the pointer correctly:
gain = dynamic_cast<juce::AudioParameterFloat*>(apvts.getParameter("gain"));
jassert (gain); // makes sure the parameter exists and is of the correct type

// in processBlock or anywhere else
auto currentGain = gain->get();

This is as fast as it gets, it just reads the atomic and scales it according to the range of the underlying RangeBasedParameter.

JUCE is not an event driven system, so you cannot check if the value has changed other than just reading it.

nice thank you for the info. i think i can now deal with the background thread issue as well using fabians method to copy the “renderVoice” to the third thread