Is there a trick to restoring the Toggle state (TextButton) from the APVTS (my sliders restore fine)?

I’m having an issue restoring my Toggle Buttons (TextButtons).

In getStateInformation() I am getting the APVTS tree and the sliders restore fine to their previous value.
But my Toggle(TExt) buttons don’t. They seem to all restore to true.
I did a debug DGB string in getStateInformation() and the values all report correctly.

apvts.replaceState(tree);
mRedToggleButton_value = apvts.getRawParameterValue("redToggleButtonParameterID")->load();
mGreenToggleButton_value = apvts.getRawParameterValue("greenToggleButtonParameterID")->load();
mBlueToggleButton_value = apvts.getRawParameterValue("blueToggleButtonParameterID")->load();
outputstr += (mRedToggleButton_value ? " Red=True" : " Red=False");
outputstr += (mBlueToggleButton_value ? " Blue=True" : " Blue=False");
outputstr += (mGreenToggleButton_value ? " Green=True" : " Green=False");            
DBG(outputstr);     //correct values shown

i.e. Blue is true, the others are false reported in the code above DBG string.
But then I query the values later and they are all true!

Is there a quirk with restoring toggle states of TextButtons in JUCE that I should know about?

(I notice that if I turn a knob on my plugin UI and close the DAW, it says “Save Yes/No” but if I turn a button on or off, and close the DAW, it doesn’t offer a “Save”.)

Extra info… My constructor in PlginProcessor.cpp snippet:
mRedToggleButton_value(false), mGreenToggleButton_value(false), mBlueToggleButton_value(false)
PluginEditor.cpp constructor snippet:
mRedToggleButton_buttonAttachment(audioProcessor.apvts, "redToggleButtonParameterID", mRedToggleButton), //same for blue and green

Which OS, plugin format, and DAW is this happening in?

Windows, vst3, Reaper.

Does the APVTS store only float values ? I’ve been assuming that it can store Booleans too…
mRedToggleButton_value = apvts.getRawParameterValue("redToggleButtonParameterID")->load();
So 1.0f, 0.0f for true, false…?

I am using mRedToggleButton.setToggleState(true, juce::NotificationType::dontSendNotification); But I am getting the feeling that its not actually changing the value in the APVTS… is this feeling correct?

I read other suggestions I should use to set the value, e.g. setValueNotifyingHost Is this the solution I should be looking at?

APVTS holds a bunch of RangedAudioParameters, which does use float values under the hood, and the different parameter types are all subclasses of that. (See the inheritance diagram for RangedAudioParameters to see how they relate.) But it shouldn’t be a problem to access that as a bool.

I’m a little unclear about what your code is doing and what you expect it to do. Is mRedToggleButton_value just a temporary value that you’re using to generate DBG test strings with, or is it a class member that you’re accessing over time? Is it a bool or a Value or something else?

Later, like when? After toggling the buttons onscreen?

No, the ButtonAttachment should handle the connection between the APVTS value and the Button component. You don’t need to involve setValueNotifyingHost.

I toggle the buttons on the UI and they toggle fine. I have three buttons, Red, Green, Blue. When I click the Red button the Green and Blue turn off. And the same behavior for Green and Blue when I click them.

When I close the DAW and reload the plugin, all the buttons are turned on (true), and all light up. So somehow the togglestate() of my button is either not being saved to the APVTS, or not being saved out to the plugin APVTS stream on exit. But I doubt the latter because my slider states work.

void myplugin::buttonClicked(juce::Button* button)
{

    if (button == &mBlueToggleButton)
    {
        mGreenToggleButton.setToggleState(false, juce::NotificationType::dontSendNotification);
        mRedToggleButton.setToggleState(false, juce::NotificationType::dontSendNotification);
        mBlueToggleButton.setToggleState(true, juce::NotificationType::dontSendNotification); 
    }
    else if (button == &mGreenToggleButton)
    {
        mBlueToggleButton.setToggleState(false, juce::NotificationType::dontSendNotification);
        mRedToggleButton.setToggleState(false, juce::NotificationType::dontSendNotification);
        mGreenToggleButton.setToggleState(true, juce::NotificationType::dontSendNotification); 

    }
    else if (button == &mRedToggleButton)
    {
        mBlueToggleButton.setToggleState(false, juce::NotificationType::dontSendNotification);
        mGreenToggleButton.setToggleState(false, juce::NotificationType::dontSendNotification);
        mRedToggleButton.setToggleState(true, juce::NotificationType::dontSendNotification); 
    }

(the above works to toggle my buttons on the UI)
.h

    juce::TextButton mBlueToggleButton;
    juce::TextButton mGreenToggleButton;
    juce::TextButton mRedToggleButton;

    juce::AudioProcessorValueTreeState::ButtonAttachment mBlueToggleButton_buttonAttachment;
    juce::AudioProcessorValueTreeState::ButtonAttachment mRedToggleButton_buttonAttachment;
    juce::AudioProcessorValueTreeState::ButtonAttachment mGreenToggleButton_buttonAttachment;

In my Editor constructor:

    mRedToggleButton_buttonAttachment(audioProcessor.apvts, "redToggleButtonParameterID", mRedToggleButton),   
    mGreenToggleButton_buttonAttachment(audioProcessor.apvts, "greenToggleButtonParameterID", mGreenToggleButton),   
    mBlueToggleButton_buttonAttachment(audioProcessor.apvts, "blueToggleButtonParameterID", mBlueToggleButton),   

Also, a perhaps significant thing is that , if I alter a slider, when I quit, the DAW asks me to save. But when I alter button toggle states the DAW does not ask me to save. So, in theory, when I change toggle states on the buttons I don’t think the changes are being effected in the APVTS.

This isn’t part of the answer to your problem, but your if/else pattern, can be reduced to just three lines.

mGreenToggleButton.setToggleState(button == &mGreenToggleButton, juce::NotificationType::dontSendNotification);
mRedToggleButton.setToggleState(button == &mRedToggleButton, juce::NotificationType::dontSendNotification);
mBlueToggleButton.setToggleState(button == &mBlueToggleButton, juce::NotificationType::dontSendNotification);
1 Like

Ah, OK, I think I see the problem.

However, first, another bit of advice…

Now that I see you’re trying to do a set of buttons where only one at a time is activated, it makes me wonder if there should really be three different parameters. Is it really just describing three states of a single parameter? If so, better to express that as an AudioParameterChoice (or AudioParameterInt), rather than using 3 different parameters for it.

Not only will that approach result in clearer and more maintainable code, it will also not break once you load this in a DAW and expose its parameters to automation. Because, while it’s possible in your Editor to reinforce this group of 3 buttons having only one at a time in its “true” state, what would happen in a DAW’s automation lanes if the user set two of them to true at the same time?

Also, rather than use that verbose branching code in your myplugin::buttonClicked method, consider using Button::setRadioGroupId, which can do the same thing for you just by calling it on each button (using the same group ID).

Anyways… I suspect the issue here is that that JUCE Buttons are not “toggleable” by default. So your attachment class (connecting the button and APVTS) is probably fine, it’s just that when you call e.g. mGreenToggleButton.setToggleState, it doesn’t do anything.

So the solution to that would be to call setToggleable (true) on each button in the Editor’s ctor. And you may need to call setClickingTogglesState (true) as well, if you take my advice to replace your myplugin::buttonClicked method with the use of Button::setRadioGroupId.

–solution below–

The toggle button issue is solved.

When I implemented addListener() and took over the buttonclicked() method, this meant I overrode the code that updated the APVTS, so the listener was not updating the APVTS.

So I had to manually update the APVTS, by putting some code in buttonclicked() : audioProcessor.updateRedBlueGreenToggles(0.0f, 0.0f, 1.0f);

Then in my audioProcessor, manually update the APVTS:

void myPluginAudioProcessor::updateRedBlueGreenToggles(float redValue, float greenValue, float blueValue)
{
    apvts.getParameter("redToggleButtonParameterID")->setValueNotifyingHost(redValue);
    apvts.getParameter("greenToggleButtonParameterID")->setValueNotifyingHost(greenValue);
    apvts.getParameter("blueToggleButtonParameterID")->setValueNotifyingHost(blueValue);
}

I am assuming NotifyingHost is OK. It seems to work - I exit the plugin and come back in and all is restored correctly.
(I’ll change the 0.0 1.0f to true false later, and may look at the AudioParameterChoice)

That’s actually not what happens. The addListener adds to a ListenerList, which will send the event to all listeners in a not guaranteed order. And the ButtonAttachment also listens to that ListenerList.
Your listener must have had a different adverse side effect.

1 Like