I followed this tutorial https://www.youtube.com/watch?v=YwAtWuGA4Cg to implement a preset manager, all fine.
However, when I open a saved preset file (xml), then there are (at the top) some audio parameters from another tutorial (!) I had followed previously, parameters from another juce project.
How come?? I am puzzled.
Could something like this happen in get/setStateInformation?
Yep, after commenting out the code in get/setStateInformation and saving the preset, these “strange” parameters were gone. This is what I currently have:
void WaveFaktoryAudioProcessor::getStateInformation (juce::MemoryBlock& destData)
{
// You should use this method to store your parameters in the memory block.
// You could do that either as raw data, or use the XML or ValueTree classes
// as intermediaries to make it easy to save and load complex data.//const auto state = apvst.copyState(); //const auto xml(state.createXml()); //copyXmlToBinary(*xml, destData);
}
void WaveFaktoryAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
// You should use this method to restore your parameters from this memory block,
// whose contents will have been created by the getStateInformation() call.//const auto xmlState(getXmlFromBinary(data, sizeInBytes)); //if (xmlState == nullptr) return; //const auto newtree = juce::ValueTree::fromXml(*xmlState); //apvst.replaceState(newtree);
}
Any thoughts?
EDIT after re-reading original post
If you’re seeing old state, then the preset manager is loading an XML file from the original project. If you take a look here you’ll see that it loads a file from your common documents directory. Each audio processor will be in a folder for the company name, and have a file for the project name. Make sure you update these details in your Projucer file (you can also check these in JuceHeader.h but they get will get overwritten by Projucer).
const File PresetManager::defaultDirectory{ File::getSpecialLocation(
File::SpecialLocationType::commonDocumentsDirectory)
.getChildFile(ProjectInfo::companyName)
.getChildFile(ProjectInfo::projectName)
If you want to keep the same details, then you could also edit (or delete) that XML file to remove the old junk.
OK, thanks!
I think the juce “design feature” I am struggling with is that after changing a parameter name in the valuetree, during development of the code, I still keep the old name.
After changing “VOL” to “Volume”, I end up with both in the saved xml file:
<PARAM id="VOL" value="0.0"/>
<PARAM id="Volume"/>
This also occurs when running the plugin as a standalone app.
Is there a way to bypass this behaviour?
I am only creating & saving the valuetree,
no parameter attachments yet, no reading of xml files.
You could try pruning out old parameters each time you get/set state information. For example:
void YourAudioProcessor::getStateInformation (MemoryBlock& destData)
{
const auto stateTree = processorState.copyState();
std::unique_ptr<juce::XmlElement> xmlState (stateTree.createXml());
pruneParameterXml (xmlState.get());
copyXmlToBinary (*xmlState, destData);
}
void YourAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
std::unique_ptr<juce::XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
pruneParameterXml (xmlState.get());
if (xmlState.get() != nullptr)
{
if (xmlState->hasTagName (processorState.state.getType()))
{
processorState.replaceState (juce::ValueTree::fromXml (*xmlState));
}
}
}
void YourAudioProcessor::pruneParameterXml (XmlElement* xml) const
{
if (xml->hasTagName (processorState.state.getType()))
{
XmlElement* parameterXml = xml->getFirstChildElement();
Array<XmlElement*> deleteList;
while (parameterXml != nullptr)
{
const auto id = parameterXml->getStringAttribute ("id");
if (!processorState.getParameter (id))
{
// Not one of our current parameters, so delete it later (if we delete now, then getNextElement() won't work!)
deleteList.add (parameterXml);
}
parameterXml = parameterXml->getNextElement();
}
// Delete obsolete parameters
for (const auto param : deleteList)
{
xml->removeChildElement (param, true);
}
}
}
First thing make sure that each project you make has its own state identifier name, that you pass to the ValueTree constructor.
Then delete the old saved state of your standalone app (on macOS for example there is a .settings file at /Users/yourUsername/Library/Application Support), and open an instance of your application.
If you are reading presets at the startup, disable that functionality before doing that and delete all the old presets that have the undesired parameters. After the first start, close the app, enable again the preset loader and recompile.
This should be fine!
andrwej said:
You could try pruning out old parameters each time you get/set state information.
Good one!
this gets rid of older parameters and keeps the AudioProcessorValueTreeState clean.
however, it still writes <PARAM id="Volume"/>
only, eg without its value.
Something else in set/getStateInformation is still polluting my AudioProcessorValueTreeState,
because after completely commenting out both methods, all is fine with the saved xml file.