Update AudioProcessorValueTreeState from process block

Just making a little kick drum VST, and have set it all up with the lovely new AudioProcessorValueTreeState class, and have connected my UI with AudioProcessorValueTreeState::SliderAttachment classes.

…And that all works nice and well. State of the sliders is saved in the host, and recalled fine when reloading.

But i’m getting stuck with this:

I want to update the ValueTreeState and also the pitch slider, whenever a midi note is entered.
Here’s what my processBlock looks like:

MidiMessage msg;
int ignore;

for (MidiBuffer::Iterator it (midiMessages); it.getNextEvent (msg, ignore);)
{
    if (msg.isNoteOn())
    {
        mainValueTree->state.setProperty ("Kick_pitch", msg.getNoteNumber() / 127.0f, nullptr);
        kickSynth.trigger();
    }
}

the kickSynth.trigger() function is working fine, so it seems highly unlikely i have an error in midi handling… but i was really hoping that calling setProperty on the tree would just “work” in this case, and unfortunately it doesn’t.

as i said, i have set up my sliderAttachments correctly, as the getStateInformation and setStateInformation functions in my processor are working fine.

so why isn’t the setProperty function updating my slidersAttachments?

Unfortunately the Attachments work only for parameters. They are not designed for arbitrary properties, afaik.
In this case I think you need to add a ValueTree::Listener on the public accessible state of the AudioProcessorValueTreeState.
There you get the callback from the property change and call Slider::setValue() by hand.
HTH

ok, i’ll do that then.

it’d be good if the attachments could also handle that though!

You are right. I took the challenge, try this, does that work for you?

ValueTreeSliderAttachment.h.txt (2.4 KB)

Use it like:

slider = new Slider();
slider2 = new Slider();

tree = ValueTree ("TestTree");
tree.setProperty ("number", 5.0, nullptr);

addAndMakeVisible (slider);
addAndMakeVisible (slider2);

attachment = new ValueTreeSliderAttachment (tree, slider, "number");
attachment2 = new ValueTreeSliderAttachment (tree, slider2, "number");

with:

ScopedPointer<Slider> slider;
ScopedPointer<Slider> slider2;
ValueTree tree;

ScopedPointer<ValueTreeSliderAttachment> attachment;
ScopedPointer<ValueTreeSliderAttachment> attachment2;
1 Like

hmm…nearly there.

state of the sliders seems to save fine, and yes, they do update when i change the value tree. awesome.

only thing i have to sort out now, is that my synth was an AudioProcessorValueTreeState::Listener, so i guess i’ll have to change that and make it listen directly to the value tree too, yeah?

Your synth can be both, a AudioProcessorValueTreeState::Listener AND a ValueTree::Listener. The callbacks are named differently, so no problem doing:

class MySynth : public AudioProcessorValueTreeState::Listener, public ValueTree::Listener {
    // [...]
    public:
        void parameterChanged (...) override;
        void valueTreePropertyChanged (...) override;
};

Just depend’s if the value is a parameter (i.e. automatisable by the host), or if it’s stored somewhere else in the tree.

ahh…i should have asked!

i just assumed they’d cross-talk.

but i just tried with both, and it seems perfect!!!

thanks again! :grin:

would there be any problem to include this code directly into the AudioProcessorValueTreeState::SliderAttachment class?

as far as i can see, if they don’t give each other issues, and they both use basically the same arguments , then this would be a pretty cool and handy addition

Just added the attachment class to github as module

And added two more attachment classes, one for ComboBox (working) and one for a RadioButtonGroup (in progress). If someone has a clever idea how to design the lookup between the radio buttons and the ValueTree, let me know.

Now I can cleanup my code as well a bit…

I think it should be kept separate. Every AudioProcessorValueTreeState is a ValueTree, but not vice versa. So why would one restrict the users from using the attachment on “normal” ValueTrees?

@jules: if you want to add that stuff, please go ahead. But you probably will rewrite most of it anyways :wink:

2 Likes

+1
I like the idea of having something like the button, combobox, and slider attachments working the same way for all valuetrees out of the box.

Never do any ValueTree (or other similar high-level work) in your audio thread!!

Also, why are you even trying to process the midi messages directly - our Synthesiser class would probably do this for you, as well as handling numerous nasty edge-cases that you won’t yet have banged your head against (but you will do!)…

1 Like

@jules when you say don’t do any ValueTree work would it is safe to be reading a value from the state in the process block ?

Am I right in thinking it would just be bad practice to set a Value, particularly when a listener is attached to that value and the asynch update tiggering (or whatever is used internally) would be potentially blocking behaviour ?

Cheers

No, don’t read a Value or ValueTree in your audio functions. It could involve allocation, and the message thread could be changing it while you’re reading it.

1 Like

So this holds also for just reading… good to know, thanks!

So if you’ve got your plugin Document/Preset/State in a ValueTree and need to read some value(s) from it in the audio thread, what should you do @jules ? Is that what CachedValue<> is for?

Yep, that’s the main reason we wrote CachedValue.

1 Like

Does this also apply for the AudioProcessorValueTreeState getRawParameterValue function?

float* AudioProcessorValueTreeState::getRawParameterValue ( StringRef parameterID ) const noexcept 

  Returns a pointer to a floating point representation of a particular parameter which a 
  realtime process can read to find out its current value.

Is it safe to retrieve these values in the processBlock function as is done in the new AudioProcessorValueTreeState tutorial?

void processBlock (AudioSampleBuffer& buffer, MidiBuffer&) override
{
    const float phase = *parameters.getRawParameterValue ("invertPhase") < 0.5f ? 1.0f : -1.0f;
    const float currentGain = *parameters.getRawParameterValue ("gain") * phase;
    if (currentGain == previousGain)
    {
        buffer.applyGain (currentGain);
    }
    else
    {
        buffer.applyGainRamp (0, buffer.getNumSamples(), previousGain, currentGain);
        previousGain = currentGain;
    }
}

so, CachedValue<> can be used to safely read AND write values to/from the ValueTree from the processBlock function ???

2 Likes

Hi @gordcragg

Getting raw parameter values should be safe as it doesn’t actually read from the internal ValueTree object.

When you pass in the paramID the AudioProcessorValueTreeState class/object gets the parameter from it’s owning processor via the usual OwnedArray of audio parameters which the AudioProcessorValueTreeState ::createAndAddParameter() method adds to under the hood.

So yeah calling *parameters.getRawParameterValue ("gain") * phase; on the callback thread should be safe.

Josh

Isn’t it safe to o this, then?

void MIDI_Int_AudioProcessor::parameterChanged(const String& paramID, float newValue)
{
	if ( paramID == "pidMinPlayRange" )
	{
		auto max = vtsParams.getParameterAsValue( "pidMaxPlayRange" );
		max.setValue( jmax<float>( newValue , max.getValue() ) );
	}
	if ( paramID == "pidMaxPlayRange" )
	{
		auto min = vtsParams.getParameterAsValue( "pidMinPlayRange" );
		min.setValue( jmin<float>( newValue , min.getValue() ) );
	}
}