How to use the ValueTree to restore an application state?

I’m building my first mid-scale standalone application that should manage the application state through a ValueTree.

My approach currently looks like this (a bit simplified): I have some GUI Components that reflect the state of the underlying dsp processing chain and which can be used to control the processing. Each dsp processing block has a ValueTree that contains all its parameters as properties. The dsp processing blocks as well as the GUI are listeners to those trees, so modifying properties from any side of the tree leads to a perfect synchronization of the listeners.

Now my application base class owns a root value tree object. All processing block value trees are children of this root tree. Storing this root tree to xml works great, the xml generated perfectly reflects the hierarchical structure of the design and contains all current values. However I’m struggling on how to restore an application state at runtime.

I can load the stored xml and create a ValueTree from it through ValueTree::fromXml, however I can’t see how to simply apply those loaded settings to the current root tree and notify all listeners listening to the child trees that the whole application state gets updated at all ends. Simply assigning the loaded tree to the current root tree obviously doesn’t do the trick.

So what’s the usual way to achieve this?

I got it working with this function, that simply iterates over all properties and children in the tree loaded from xml and sets the properties if they exist in the destination tree. Besides from possibly blocking the message thread a bit long in case of a complex data structure is there any downside of this approach (or is there anything similar I have not found so far?)

void syncValueTreeNotifyListeners (const juce::ValueTree& source, juce::ValueTree& destination)
{
    const int numProperties = source.getNumProperties();
    for (int i = 0; i < numProperties; ++i)
    {
        auto propertyName = source.getPropertyName (i);

        if (destination.hasProperty (propertyName))
            destination.setProperty (propertyName, source.getProperty (propertyName), nullptr);
    }

    for (const auto& child : source)
    {
        auto childType = child.getType();
        auto childInDestination = destination.getChildWithName (childType);

        if (childInDestination.isValid())
            syncValueTreeNotifyListeners (child, childInDestination);
    }
}
3 Likes

not sure if this is the usual way, but I used that recently:
destinationValueTree.copyPropertiesAndChildrenFrom (ValueTree::fromXml (xmlElementToLoad), nullptr);

I my recent tests, copyPropertiesAndChildrenFrom() does what it says, but it doesn’t seem to call the listeners, which is a problem. The only solution that seemed to work for me was calling that syncValueTreeNotifyListeners()

I’d love to hear from someone on the Juce team about this.

2 Likes

Another option is to just do

source = destination;

which will trigger callbacks to valueTreeRedirected(), the only non-pure-virtual value-tree callback.

But I just ended up doing what you did, it just seemed simpler

Thank you for providing a solution to this problem! I was struggling to figure out how to update my GUI from an XML-loaded ValueTree until I found this post.

I made one slight change, which was needed since my ValueTree has more than one child with the same name. I replaced:

auto childType = child.getType();
auto childInDestination = destination.getChildWithName (childType);

with:

auto childInDestination = destination.getChild(source.indexOf(child));

which ensures that the each destination child is synced to its corresponding source child (and not synced to just the first one).

Hey Hey,

Thank you so much for that method. that indeed fixes it.
I am only wondering where are you calling this exactly. I am testing in ableton and when I load a previously saved project, the flow of initialisation in juce is as follows…

pluginEditor constructor() -> getState() -> setting the new ValueTree.

After my ableton project is loaded, and I open my plugin window, the pluginEditor constructor() is not called anymore. It is af it is already called on startup, but without actually opening the window. For this reason it is initialised with the default ValueTree instead of the loaded XML one.

When I close the window and reopen it, the pluginEditor constructor() is correctly called and this time uses the updated ValueTree.

So, yes, everything works except I have to close and reopen my plugin window :stuck_out_tongue:

Ideally the plugin editor constructor should be called after the getState() method, but this is unfortunately not the case.

Any tips?

Did you solve this?
I see 2 options.
One to make a sad button to get the current settings.
The other to make a public function similar to the constructor that is called inside the getState()

Anyone?

Hi @seanwayland,

The way I fixed it was to call a separate method inside the constructor of my editor called constructDynamicElements(). I then also call this method inside the setStateIndormation() method (I use an observer pattern to do this - but that doesn’t matter).

So now my flow goes -> open saved project in D.A.W -> editor constructor called -> calls constructDynamicElements() (with default/empty values) -> getStateInformation gets called -> constructDynamicElements() gets called a second time this time using data from the saved file.

I hope this flow might help you.

(EDIT: rereading your post, yes option 2 you are describing is what I did ;))

Thanks when you say “inside the constructor” do you mean a function inside the class constructor or just another public function defined in the editor class?
I assume you don’t mean a lambda like this

I had a quick try of adding a function yesterday but couldn’t quite figure out what the editor class was called trying to call it in the processor. I will fudge around again today thanks for your help Sir/Madam!

No I mean you simply have a public method in your editor class and you call it both at the end of its constructor and also after loading your data (e.g. valueTree)…

ProcessorEditor::ProcessorEditor(AudioProcessor& processor, ValueTree& valueTree)
: processor (processor),
valueTree (valueTree)
{
//initialise static stuff…
addAndMakeVisible(myTxtLabel);

//initialise dynamic stuff…
constructDynamicElements();
}

ProcessorEditor::constructDynamicElements()
{
auto txtFromValueTree = valueTree.atChild(“myTxt”); //retrieves default value if not set.
myTxtLabel.setText(txtFromValueTree);
}

inside the processor setStateinformation() method you call :

myEditor.valueTree = valueTree;
myEditor.constructDynamicElements();

Let me know if that makes sense.

Thanks so much I was thinking along similar lines.
I couldn’t figure out how to find the name of the Editor class to call it in the setStateinformation() method.

myEditor.valueTree = valueTree;

How would I find the value of myEditor?
Where does that class name come from?

Sean

Hmmm sorry I mistakenly assumed that the processor would have the editor as a member variable. But as I said I used an observer pattern to achieve this - using a third class/instance. But you might as well just try to try to add the editor as a member variable to the processor and just call the public method on it.

Anyway using my method:
So first of, you can find your editor class in the processor if you go and look for the method createEditor() (check the return value of that method) that is your editor!

So the way I did the communication is that I have a third class called DataModel. it is owned by the processor (as a member variable). In the constructor of the editor class I pass the processor as an argument, so inside the processor the createEditor() looks like this:

AudioProcessorEditor* MyAudioProcessor::createEditor()
{
return new MyAudioProcessorEditor (*this);
}

So now the editor can have access to the processor. We keep a reference to it as a member variable, and thus we can access its DataModel instance and we can add the editor as a listener to that object.

the editor constructor might look like this:

MyAudioProcessorEditor (MyAudioProcessor& processor)
: processor (processor)
{
processor.getDataModel().addListener(*this);
}

the editor has a reference to the processor as a member variable:


private:
MyAudioProcessor& processor;

I’m not sure if you are familliar with the observer/notifier pattern, but you should read up on it if it confuses you here…

so now in the setStateInformation() inside the processor I call:
dataModel.setValueTree(valueTree)

inside the DataModel class the method setValueTree() calls notifyObservers…

the Editor class inherits DataModel:Listener and implements the method which is called by the DataModel instance when calling setValueTree(). It might be called valueTreeHasBeenChanged() or something like that.

So now in the Editor class the implemented method calls our constructDynamicElements();
void valueTreeHasBeenChanged() override
{
constructDynamicElements();
}

Wow, all of this is probably overkill, but this is the way I did it. Like I said try to get a reference to the editor in your processor and call constructDynamicElements(0 directly on it, that’s probably way easier :smiley:

Great thanks for the lesson :slight_smile: I will go dig up the gang of four book!
Today I am trying to figure out why the parameter state is being stored to 2 decimal places only in the DAW. After that I can dig into this! Onwards and sideways!

I found a very simple solution.

I added a juce timer class as a member of my editor class

class WaylodelayUdAudioProcessorEditor  : public juce::AudioProcessorEditor, private juce::timer_clock:

I implemented the virtual class thingy as a private member of the editor class :

private:
    
    void timerCallback() override;

I started the timer in the editor constructor ( and stopped it in the destructor ).

Timer::startTimerHz(5);

In the editor class I implemented the virtual function and called the function that my sad button was using to update the sliders after a preset was called.

void WaylodelayUdAudioProcessorEditor::timerCallback()
//void timerCallback()
{
    setSliders();
}

Thanks everyone for their help I was so stoked to see that stupid button go away and the sliders to update to their positions when the presets were loaded in from the DAW.

:slight_smile:
:slight_smile:
:slight_smile:

I’m not sure if it is entirely the case here, but I would really discourage you to put code in a timerCallback() method, when you only need to call that code on an event-basis. In your case = on start up of the plugin or when a preset has been chosen.

You should rather only call the code when that event happens, otherwise you’re just waisting CPU power setting the sliders in the method ‘setSliders’ five times a second even when it is already set.

2 Likes