How can I successfully save plugin component state?

I am developing a plugin on Windows (VS22) that I want to eventually be distributed as VST3, AU, AAX, etc, and I followed this tutorial on saving/loading plugin state. Here is my code for saving/loading state, copied pretty much directly from the tutorial:

void BluesTechAudioProcessor::getStateInformation (juce::MemoryBlock& destData) {
    DBG("saving state");
    auto state = parameters.copyState();
    std::unique_ptr<juce::XmlElement> xml(state.createXml());
    copyXmlToBinary(*xml, destData);
}

void BluesTechAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
    DBG("RESTORING state");
    std::unique_ptr<juce::XmlElement> xmlState(getXmlFromBinary(data, sizeInBytes));
    if (xmlState.get() != nullptr) {
        if (xmlState->hasTagName(parameters.state.getType()))
            parameters.replaceState(juce::ValueTree::fromXml(*xmlState));
    }
}

When I open the plugin in FL Studio, it works, but when I close the project and re-open it, I get a pop-up message that “There was an error setting the state of the VST3 plugin. The component state wasn’t restored.”

Did I miss something in the tutorial? Or is there a different way to save/load the plugin state? I’m a little unclear on where the plugin state gets saved (is it in a file somewhere? or in a memory block inside the VST3?), but I’d love to get this issue figured out.

Thanks so much for your help!

Update: I noticed that when I run it using the “Debug” button in Visual Studio, it actually does save and restore the parameters, but it crashes when I try to run it in FL Studio.

The host is completely in charge of where/how it stores session data. Plugins simply read from or write to the memory that is provided to them by the host app. There is nothing in any of the APIs that specifies how/where such data is stored, or even if it exists at all.

Thanks so much, that makes sense. So then if I am storing the plugin state in the memory block that is given by the DAW (juce::MemoryBlock& destData), do you have any idea why it wouldn’t work with the code I’ve provided?

Not offhand, but our chunk data doesn’t include only the parameters xml. We add a lot of other stuff as well. Maybe something to do with how you’re initializing the unique_ptr there? We have similar code, but it uses assignment to the local variable, not putting it in a constructor parameter list. Not sure on the technicalities of that class, though, just an observation. This is our code for that line in setStateInformation:
std::unique_ptr<juce::XmlElement> pRootXml = getXmlFromBinary(pBuffer, sizeInBytes);

Okay, thanks so much. I changed it to be an assignment rather than a constructer param, but the same thing happens.

I’m now noticing that I can look at FL Studio’s debugging log, where it gives more specifics. It says that I have a read access violation exception, and gives me a memory address. The challenge is, when I debug the JUCE plugin in Visual Studio, I never get that exception. So I’m not sure exactly where it’s happening.

Do you know if there is a way to check what is trying to be read at that address? For example, if I put that address in the “memory” window in Visual Studio while debugging would it be the same address? It gives me both the address in the .vst3 executable (which I assume is the address of the instruction) and the address that is trying to be read. Is there any way to relate those to variables in my C++ code, so as to know which lines of code are causing the exception?

Thanks again for all your help!

UPDATE: I’ve figured out that it is in calling parameters.replaceState() that the read access violation happens. It works fine when running the standalone project with the Visual Studio Debug button, but the vst3 causes the exception when the DAW loads a new project. Any ideas why this wouldn’t work?

Okay, I’ve fixed the issue.

Turns out my mistake was initializing my plugin DSP objects in the prepareToPlay() function, which I hadn’t realize gets called multiple times. In there, I had initialized IR buffers for different FIR filters, and I think the memory allocations there messed something up. I moved those initializations into the PluginProcessor’s constructor, and it works now.

Thanks!

1 Like