So you load presets using XML...but then what?

So I’m currently loading XML presets using the following function:

void MyAudioProcessor::setStateFromFile(File newFile)
{
    XmlElement* xmlState = XmlDocument::parse (newFile);
    if (xmlState != 0 && xmlState->hasTagName (parameters.state.getType()))
    {
        parameters.replaceState (ValueTree::fromXml (*xmlState));
    }
    
    delete xmlState;
}

The problem is that once the replaceState() call happens, I then need to set all of my synth parameters based on the new data we just loaded. Now, my processor is also a AudioProcessorValueTreeState::Listener so if I could just get parameterChanged() to be called for all of my parameters, that would solve my problem, but alas replaceState() does not seem to trigger the parameterChanged() listeners.

My solution is to insert this code into the function above:

for(int i=0;i<parameters.state.getNumChildren();i++) {
    ValueTree vt = parameters.state.getChild(i);
    if(vt.getType() == Identifier("PARAM")) {
        String parameterId = vt.getProperty("id");
        float *parameterValue = parameters.getRawParameterValue(parameterId);
        parameterChanged(parameterId, *parameterValue);
    }
}

But this code is ugly.

So my questions are as follows:

  1. How are people setting their plugin settings after loading parameters via XML and calling replaceState? Is there some magic other way?
  2. Is there some way to get replaceState to trigger parameterChanged messages?
  3. If not, how can I parameterChanged messages for all parameters at once?
  4. Or, assuming all of the work is correct…is there some clever way to iterate over all the parameters in a AudioProcessorValueTreeState without doing what I did above?

EDIT: See note from @t0m below, this is not the way to do it :wink:

I do it like this:

void AudioProcessor::loadPresetXML(const String xmlString)
{
    ScopedPointer<XmlElement> xmlState (XmlDocument::parse(xmlString));

    Identifier idtype = apvts.state.getType();
    if (xmlState->hasTagName(idtype)) {
        apvts.state = ValueTree::fromXml (*xmlState);
    }
}

/// AudioProcessor.h has this:

AudioProcessorValueTreeState apvts;

obviously just need a bit of adaptation to take a File instead of a string, but this Just Works™️ without any need for any other hoops to be jumped through.

I take a string so that I can either load from a file (using file.loadFileAsString() for user presets) or I have a bunch of built in presets defined using:

R"XML(<?xml version="1.0" encoding="UTF-8"?><myPlugin><PARAM id="depth" value="5e1"/>...etc...</myPlugin>)XML"

that I can send to the same method.

1 Like

Thank you so much. That’s perfect! So basically if you just set the state:

parameters.state = ValueTree::fromXml (*xmlState);

the way you are doing instead of replacing the state as I had been doing:

parameters.replaceState (ValueTree::fromXml (*xmlState));

it calls all of the listeners.

Problem solved.

The first method is not thread safe so you shouldn’t use that in places like setStateInformation which could be called on the audio thread.

All replaceState does is replace parameters.state with the ValueTree::fromXml (*xmlState) in the second line of the implementation, so I’m a little surprised that changes anything.

1 Like

Oh, really? Didn’t realize that about the thread safety. I’m also not sure why setting the state directly (instead of using replaceState) should change anything.

    ScopedLock lock (valueTreeChanging);

I guess that line in replaceState is what makes the difference, also time to fix my code using that instead. I can’t remember where I learned the replacing state directly from, perhaps old JUCE examples?