Save/Recall non Parameter state info

#1

I have AudioProcessorValueTreeState for the regular plugin paramters which works as expected. I’m wanting to add various data to the saved info (at this stage just some strings).
This is the example I found and am trying to work with:here
in PluginProcessor.h I have:

public:
..
Value nonParmStringVal;
static Identifier myNonParmStringID;
Value anotherVal;
static Identifier anotherStringID;

In PluginProcessor.cpp

> Identifier AudioPluginTemplateAudioProcessor::myNonParmStringID("mynonparmstring");
> Identifier AudioPluginTemplateAudioProcessor::anotherStringID("anotherstring");
> //==============================================================================
> AudioPluginTemplateAudioProcessor::AudioPluginTemplateAudioProcessor() : parameters(*this, nullptr, Identifier("APVTSTutorial"),
> 	{
> 		std::make_unique<AudioParameterFloat>("gain",            // parameter ID
> 											   "Gain",            // parameter name
> 											   0.0f,              // minimum value
> 											   1.0f,              // maximum value
> 											   0.5f),             // default value
> 		std::make_unique<AudioParameterBool>("invertPhase",      // parameter ID
> 											  "Invert Phase",     // parameter name
> 											  false)              // default value
> 	})
{
	phaseParameter = parameters.getRawParameterValue("invertPhase");
	gainParameter = parameters.getRawParameterValue("gain");

	parameters.state.setProperty(myNonParmStringID, var("init"), nullptr);
	nonParmStringVal.referTo(parameters.state.getPropertyAsValue(myNonParmStringID, nullptr));

	parameters.state.setProperty(anotherStringID, var("init2"), nullptr);
	anotherVal.referTo(parameters.state.getPropertyAsValue(anotherStringID, nullptr));
}

The getstate and setstate have xml file output for diagnostic purposes:

 void AudioPluginTemplateAudioProcessor::getStateInformation (MemoryBlock& destData)
    {												//to->DAW
    	File file("D:/toDAW.xml");
    	auto state = parameters.copyState();
    	std::unique_ptr<XmlElement> xml(state.createXml());
    	copyXmlToBinary(*xml, destData);
    	if (xml->writeToFile(file, "")) DBG("toDAW written");
    	else DBG("toDAW not written");
    }
void AudioPluginTemplateAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{												//from<-DAW
	File file("D:/fromDAW.xml");
	std::unique_ptr<XmlElement> xmlState(getXmlFromBinary(data, sizeInBytes));
	if (xmlState.get() != nullptr)
		if (xmlState->hasTagName(parameters.state.getType())) {
			parameters.replaceState(ValueTree::fromXml(*xmlState));
			if (xmlState->writeToFile(file, "")) DBG("fromDAW written");
			else DBG("fromDAW not written");
		}
}

Finally loading and saving a DAW session results in the following XML files:
“toDAW.xml”:
<?xml version="1.0" encoding="UTF-8"?>

<APVTSTutorial mynonparmstring="init" anotherstring="init2">
  <PARAM id="gain" value="5e-1"/>
  <PARAM id="invertPhase" value="0"/>
</APVTSTutorial>

and “fromDAW.xml”:
<?xml version="1.0" encoding="UTF-8"?>

<APVTSTutorial>
  <PARAM id="gain" value="4.599999785423279e-1"/>
  <PARAM id="invertPhase" value="1"/>
</APVTSTutorial>

I don’t know much about xml but in the “toDAW.xml” the attribute value pairs seem weird to in sequence without delimiters like that.
Also the attributes aren’t appearing in the output file so something basic is missing…
I just haven’t seen any working examples of what must be basic stuff in the overall scheme of things, so I’d really appreciate any input,
thanks

0 Likes

#2

That’s actually how XML works. There are no delimiters between attribute/value pairs (except the space).

That is surprising. The host is (or should be) agnostic to the data in that blob, it doesn’t need to be XML. I once used JSON and had no problem there. It is simply stored away, often base64 encoded.

To narrow it down, can you try to export the returned tree first, before you call replaceState()? I mean directly after the if (xmlState.get() != nullptr). Or you can even write out data, sizeInBytes, to see, what the host is telling you…

BTW, when I added information to the state, I usually added a child node for that. Maybe something doesn’t like attributes in the state tree root…

Good luck

0 Likes

#3

Thanks. I’ve set the properties in a child node: (in the processor constructor)

static Identifier myDataNodeType("NONPARMDATA");			// pre-create an Identifier
	ValueTree child(myDataNodeType);							//create node
	child.setProperty(myNonParmStringID, var("init"), nullptr); //add a pair
	child.setProperty(anotherStringID, var("init2"), nullptr);  //add another
	parameters.state.addChild(child, 0, nullptr);               //add node to valuetree

	nonParmStringVal.referTo(parameters.state.getPropertyAsValue(myNonParmStringID, nullptr));
	anotherVal.referTo(parameters.state.getPropertyAsValue(anotherStringID, nullptr));

This results in:

<APVTSTutorial>
  <NONPARMDATA mynonparmstring="init" anotherstring="init2"/>
  <PARAM id="gain" value="5e-1"/>
  <PARAM id="invertPhase" value="0"/>
</APVTSTutorial>

This is what gets generated in getStateInformation(). I understand this first call by the DAW is to receive all the default values from the plugin? So far so good. My understanding (is/was) that the call

nonParmStringVal.referTo(parameters.state.getPropertyAsValue(myNonParmStringID, nullptr));

connects the value to the property in the tree so that when the value changes the property’s value in the tree changes?
When I add the line at the end of the constructor:
nonParmStringVal.setValue("changed");
The XML becomes:

<APVTSTutorial mynonparmstring="changed">
  <NONPARMDATA mynonparmstring="init" anotherstring="init2"/>
  <PARAM id="gain" value="5e-1"/>
  <PARAM id="invertPhase" value="0"/>
</APVTSTutorial>

It’s created another property value pair on the root, not what I was expecting…
I should also mention that in every case so far when I hit save in the DAW the following happens to the XML output from getStateInformation() (i.e all added properties are gone…)

<APVTSTutorial>
  <PARAM id="gain" value="4.49999988079071e-1"/>
  <PARAM id="invertPhase" value="1"/>
</APVTSTutorial>

I’m obviously neglecting something quite basic. BTW I wonder if the example I followed still even works…
Thanks for any hints

0 Likes

#4

This makes the nonParmStringVal refering to the property myNonParmStringID in the
parameters.state, which is the root node.
If you want to refer to the property in the child tree, you should change it to:

nonParmStringVal.referTo (child.getPropertyAsValue (myNonParmStringID, nullptr));

I hope that helps.

0 Likes

#5

Thanks for the reply. I was about to post back with some progress along those lines:
nonParmStringVal.referTo(parameters.state.getChild(0).getPropertyAsValue(myNonParmStringID, nullptr));
works equivalently.
So now the desired data structure is formed in the processor constructor as expected.
Following the constructor the DAW host calls getStateInformation() the xml output is then

<APVTSTutorial>
  <NONPARMDATA mynonparmstring="HI" anotherstring="changed2"/>
  <PARAM id="gain" value="5e-1"/>
  <PARAM id="invertPhase" value="0"/>
</APVTSTutorial>

as generated in the constructor.
The DAW host (REAPER,btw) then calls setStateInformation () but the xml is now:

<APVTSTutorial>
  <PARAM id="gain" value="4.49999988079071e-1"/>
  <PARAM id="invertPhase" value="1"/>
</APVTSTutorial>`

This reflects the parameters values in the stored session but as can be seen the new data is lost.
I’ll also mention that attempts to change the value of nonParmStringVal by calling a processor method from the editor don’t work…[edit] meaning by the time a textcontrol changes the string the ValueTree is back to it’s reduced form shown above…

0 Likes

#6

Declaring the ValueTree child as a private member of the processor class seems to have fixed the disappearing node issue.
I’ll post the complete example when I get a component listener working with the ValueTree.

0 Likes

#7

Ok!
I’ve been wrestling with this issue I just worked out.
The Value that I had referTo() an element in the parameter.state ValueTree was no longer referring after setStateInformation() was called by the host:

void AudioPluginTemplateAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{												//from<-DAW
	std::unique_ptr<XmlElement> xmlState(getXmlFromBinary(data, sizeInBytes));
	if (xmlState.get() != nullptr)
		if (xmlState->hasTagName(parameters.state.getType())) 
        {
			parameters.replaceState(ValueTree::fromXml(*xmlState));
		 nonParmStringVal.referTo(parameters.state.getChild(0).getPropertyAsValue(myNonParmStringID, nullptr));		
		}
}

It seems that replaceState() breaks the previous referTo(), (which makes sense), so the referTo() has to be called again in this function…

1 Like