AudioProcessorValueTreeState createXml() with texts instead of values?


#1

Hello experts, I need your advice.

In my project I am using an AudioProcessorValueTreeState to store my parameters and chunks as it is described in the tutorial (Tutorial: Saving and loading your plug-in state).
For the creation of preset files I am using state.createXml() which gives me something like <PARAM id="m_uLFOWave_LFO1" value="2.00000000000000000000"/>. For the parameter if I have defined a valueToTextFunction and textToValueFunction that map the 2.f to “SINE” and vice versa.

Now I would like to create an XML instead that uses the texts and not the values. So for the example above I would need to get <PARAM id="m_uLFOWave_LFO1" text="SINE"/>. With this I hope to create preset files that are more robust to changes (so e.g. I could change the internal representation of sine to 3 in future without invalidating the preset files).

Is there an easy and elegant way of creating such an XML from my current ValueTree and retrieving it back later to a tree?

All the best,
Tobias


#2

ValueTree property can be a string.
Try it out.


#3

Hi there. Thanks for the answer. I should have mentioned that I am using the component attachments, e.g. AudioProcessorValueTreeState::SliderAttachment, ButtonAttachment and ComboBoxAttachment (in the example with “SINE” above). And I am defining the parameters with AudioProcessorValueTreeState::createAndAddParameter. From what I see all this only allows for float properties and not string. Do I miss something?


#4

The way I understand this, the answer is probably not what you want to hear. I think of the parameters as floats, and the vt allows a sort of alias which is a string.

It seems like what you are trying to do can be done by changing the valueToTextFunction for param = 3. to “SINE”.

But you probably would still need to reorder things a bit as at the point both 2. and 3. have a valueToTextFunction to translate these to “SINE”.

Then again, I may not be understanding what you are asking…


#5

@AnoesisAudio suggested the expected design. it should be well enough.
(the plug-in state isn’t something the user sees most time).
In addition, and information you provide would also help focusing any answer.

You should remember few things:

  • parameters are shared with the host. they eventually numeric values to the host. modes/stepped (only modern JUCE) or continuous.
  • They’re exposed to your host, that means the user can automate them. meaning the range/steps for the parameter would make automation data broken if you change your steps/range afterwards.

If you still want to save a different representation you’d simply take your APVTS copy it to a new ValueTree and encode/decode it.
But you would only do that on saving/loading state calls (to keep using the Attachments).


#6

Yes, totally aware of that and this is why I want to keep my parameters as floats and have them managed by the attachments (including the change gestures to notify the host).

This is exactly what I am looking for. Could you be more specific on this one? How do I encode / decode my APVTS so that it contains the string representation of the float parameters? Do I have to code that manually by looping over all the parameters and calling the getText() / getValueForText() methods and create new tree attributes accordingly? Or is there a more elegant way of doing this?


#7
    auto copyOfYourAPVTS = params_.getValueTreeCopy();
    copyOfYourAPVTS.setProperty(
               <identifier_name_for_string_version>,
               methodToReturnTxtVal(copyOfYourAPVTSstateToSave.getProperty(<identifier_name_for_numeric_version>)), nullptr);
    
    // optional, delete the numeric property.
    copyOfYourAPVTS.removeProperty(<identifier_name_for_numeric_version>, nullptr);

    // same flow as with JUCE tutorials...
    std::unique_ptr<XmlElement> xml (copyOfYourAPVTS.createXml());
    copyXmlToBinary (*xml, destData);

#8

Wonderful. Thank you for pointing me in the right direction. Here is what I have now and what works for me:

auto copied_state = m_parameterState.copyState();
if (externalRepresentation) {
	for (int parameterIndex = 0, numparam = getNumParameters(); parameterIndex < numparam; parameterIndex++) {
		AudioProcessorParameterWithID* param = (AudioProcessorParameterWithID*)getParameters()[parameterIndex];
		ValueTree childTree = copied_state.getChildWithProperty("id", param->paramID);
		//add new text property
		childTree.setProperty("text", param->getCurrentValueAsText(), nullptr);			
		//delete the float property
		childTree.removeProperty("value", nullptr);
	}
}