I have as AU/VST plugin, that loads a midi file and plays it in a loop. My main purpose is to run this in Apple Mainstage. This already works properly. Now I want to save several parameters of the plugin, so if it is open again, the values will be “restored”.
This already works for integer or float values if I save the Concert in Mainstage but I don’t know, how to save the path to the midi file. I miss s.th. like AudioParameterString. I read in one or two threads, that s.th. can be transformed in floats and then restored again but to be honest, I do not understand this at all.
May you please give me a hint on how to do that or may you provide me a sample of how to save a file path using AudioProcessorValueTreeState. Or do I follow the wrong approach?
You probably wouldn’t have this as an actual parameter, since it’s not something you can really automate anyway.
Since you’re using AudioProcessorValueTreeState you could simply use its ValueTree member, state. You can even use getPropertyAsValue() to get a Value object to easily read/write a property on a ValueTree.
Essentially you use your ValueTree state (or add a new child in it) and then you can take a property from it to get a Value object. Then when you set the Value to your string, it properly updates the underlying ValueTree property.
When you save your plugin’s state these kind of non-parameter ValueTrees still get written into the XML. I think the Value from getPropertyAsValue() will reflect the correct value as well when loading XML, but haven’t tried it out yet.
Quick example of what I mean in accessing the property:
Value midiFilePath = parameters.state.getPropertyAsValue("MIDI_FILEPATH", nullptr, true);
midiFilePath.setValue("~/foobar.midi"); // Writing the XML after this should show the "foobar.midi"
but reading the value is just the first step because I also have to load the file and read it for further processing. What is the right place to do this? I tried preparePlay() but this did not work.
Usually I see people talk about loading files on specialized threads, although that’s more in the case of something like an IR Reverb… But anyway, the idea there is that you shouldn’t be handling any I/O on the audio thread, so using a special thread for loading the file then atomically passing the data pointer off to the audio thread keeps your plugin/application running smoothly.
You haven’t once mentioned getStateInformation and setStateInformation…? I am assuming you have something implemented in those. Can you show the implementations?
Right. It looks like nothing is handling the file name coming from the state? For things like that you need to manually handle updating the internal state in setStateInformation. (Only the plugin numerical parameters are handled “automagically”.) So, get the file name string from the tree, make a file object out of it and call your loadMIDIFile method.
I also set some breakpoints to check if the getStateInformation and setStateInformation functions are called. If I save the “project” in the Juce AudioPluginHost, the getStateInformation is called.
Even though I would expect it vice versa from the naming point of view, it looks like getStateInformation saves the parameters and properties.
That the information is correctly put to the property before is checked in loadMIDIFile:
midiFilePath.setValue(fileMIDI.getFullPathName()); // set file path in parameters
std::cout << "In load midi file: " << midiFilePath.getValue().toString() << std::endl;
This prints the path to the debugging console.
But if I stop the debugging and start it again, the output on the console from preparePlay() is empty and when loadMIDIFile is called, it runs into an error, because there is no file loaded.
I am trying to implement what @TonyAtHarrison suggested to save a file path as a parameter. The parameter is being recalled when the DAW is open (i.e. urlParameter.getValue() returns to file path). However, when I reopen the DAW, urlParameter.getValue() returns nothing. Any suggestions?
Like was already discussed in the thread, something like strings/file paths can’t be handled as plugin parameters to begin with. (I am a bit confused how did you get your urlParameter to work at all if it is actually a plugin parameter…?) They need to be handled as custom data that you somehow deal with in the getStateInformation and setStateInformation calls of your plugin processor class. If you are already using AudioProcessorValueTreeState, that is going to be fairly easy, you can just store the path as a custom property in the ValueTree member (“state”) of the AudioProcessorValueTreeState.
However, when I save and close the DAW and open the DAW again
DBG(urlParameter.getValue().toString());
returns nothing.
My understanding is that if getStateInformation and setStateInformation calls are working when I close and open the plugin, then the parameter should be recalled when I also close and open the DAW. But this is where my problem lies. Any suggestions?
What do you mean by “closing and opening” the plugin? Closing and reopening the plugin GUI? That usually doesn’t destroy and recreate the whole plugin, it usually just destroys and recreates the GUI editor part of the plugin. The data in the plugin’s AudioProcessor part would stay intact.
How have you implemented your getStateInformation and setStateInformation methods?
Yes. I mean closing and opening the GUI. (I checked on another project that doesn’t have parameters, and you are correct. Closing and opening the GUI does not destroy the values)
Here are my getStateInformation and setStateInformation methods:
void GraphicalIrLoaderAudioProcessor::getStateInformation (MemoryBlock& destData)
{
auto state = parameters.copyState();
std::unique_ptr xml(state.createXml());
copyXmlToBinary(*xml, destData);
}
replaceState replaces the whole tree state and disconnects the Values that were previously attached to the tree. (In effect, without the referTo call, your urlParameter would still point to the data that was there before the setStateInformation call.)
I made a save following these tips, but there is one problem. How do I tell the DAW that a parameter has been changed? When you turn the knob, Reaper says that the project has been changed. And with saving the file path, this does not happen.
Saving the file path I did like this:
AudioProcessor::AudioProcessor()
{
// for file path save
state.state.addChild ({ "fileState", {}}, -1, nullptr);
checkValue();
}
void AudioProcessor::checkValue() // your void
{
filePathValue.referTo(state.state.getChildWithName ("fileState").getPropertyAsValue ("file", nullptr));
}
void AudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
if (auto xmlState = getXmlFromBinary (data, sizeInBytes))
{
state.replaceState (ValueTree::fromXml (*xmlState));
// for file path save
checkValue();
filePath = filePathValue.getValue().toString();
openFile(); // your void
}
}
The method for that is processor.updateHostDisplay(). It usually works for me but I read from others that they had problems in specific hosts…
There is a new parameter, unfortunately I don’t see a parameter changed flag, only parameterInforChanged, I would try that one: