I now have over 1000 parameters in my synthesizer. That plus 8 wavetables that are saved in getStateInformation.
I find it very unnecessary and a total waste of CPU, that each and every time I touch and change a Slider that is defined via ;‘AudioProcessorValueTreeState::ParameterLayout params’ and attached to my PluginEditor, getStateInformation is called!
Is there any way to test in getStateInformation that the call was from the PluginEditor, or any other way to bypass these calls?
Yes that makes sense getStateInformation is not called from PluginEditor, that it just how I am able to detect the call as I have a visible test counter.
I am testing in latest version of Reaper 64 bit for Windows.
The only thing I can think off right of the bat, is setting a flag in parameterChanged, then test for that in getStateInformation, and then clear it if it’s set.
I don’t get your logic. What specific conditions would allow or prevent returning the information that the host is asking for? And what would you expect the host to do if you returned an empty state at some point? That doesn’t sound like what you would want to me. Host behavior is what it is. You could contact them and ask their thoughts on wasted cycles, especially for larger session data, but I don’t think there is anything we as plugin developers can really do about it.
parameterChanged is called when a parameter is changed from DAW FX Parameter or via PluginEditor, not when I close synth window or save preset or project via DAW.
When parameterChanged is called I set a flag. I assume as part if parameterChanged, in the end, getStateInformation is called. So I test for the flag and bypass if flag is set, then clear it.
It may not be the best thing to do, but it works in Reaper as intended, that is getStateInformation is still called when exiting synth window or saving preset or project via DAW.
True. I could provide the user with an option in my synth’s global settings to do this or not. So it’s a sight lag with full undo history, or no lag without undo history. Never a perfect world!
But what if you’re in a host that does not do that, and you change the slider value, then at some point you intentionally save the session? That design would cause it to skip writing the session data, and your session would not have the latest data in it when you re-open it later.
I do the below for up to 8 x 2048 float tables (loosing slight in precision), as I never took to the time to figure out how to do an append after copyXmlToBinary (*xml, destData);
std::string wavetable = "";
for (int i = 0; i < wtSize; i++)
{
wavetable = wavetable + std::to_string (unsigned short int
((synthParams.tgActiveWaveform[module][i] + 1.0f) * 32767)) + "|";
}
xml->setAttribute ("wavetable" + String (module), wavetable);
I think you misunderstand my scenario. If you are in a host that does NOT do that, then after changing the slider value, it will NOT reset that flag, because getStateInformation will not get called. But then if you Save the session, it WILL get called, and will skip saving because the flag is still set. It will then clear that flag, but the saved session data will not have your data in it, because you just skipped saving it.
Perhaps we are misunderstanding each other. Are you saying of DAW or PluginEditor changes a parameter, and parameterChanged is NOT called? Well if so, then the flag is never set.
If you break the contract with the host by making any assumptions and giving the host an invalid state, chances are high that you’ll run into trouble in some scenario. From the code you posted I’d suggest finding a more efficient scheme for encoding your wavetables. Is there any particular reason you’re not dumping them in the binary format they already are in, instead doing a huge number of memory allocations and number/string conversions?
In case you actually need a text representation (for XML or json), you could look into base64 encoding. JUCE has helper functions for that.
Yes there is a reason I am doing XML, as noted above. I tried the following, but it did nothing, as in nothing was saved;
... // Above XML is created for other than tables and parameters in
... // AudioProcessorValueTreeState::ParameterLayout
copyXmlToBinary (*xml, destData);
destData.append (synthParams.tgActiveWaveform, 8 * wtSize * sizeof (float));
Because when I read it back, data is not what it’s supposed to be. Actually I can’t remember now it’s been more than a year since I last tried. Perhaps I could never figure out how to read it in setStateInformation. However I am VERY keen to get it to work
What’s the type of synthParams.tgActiveWaveform? Is that a C array [8][wtSize], and is wtSize constant?
Can you share the code you use to read it back?
Have you stepped through the code and look at how the data is assembled and loaded step by step, using your IDEs debugging features?
I’d recommend designing a simple binary file format that gives you a little bit of safety here. E.g., storing a magic identifier, a version, then followed by the two sizes, followed by the actual data, and safeguarded by validating the size of the memory block as well.
Yes synthParams.tgActiveWaveform[8][2048], and yes wtSize is a constant = 2048. I don’t actually have a read code anymore. The more I try to remember why I never got it to work, is I think I never could figure out how to read back the data into the array.
In setStateInformation in the very top I have;
void MutineerAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
std::unique_ptr<juce::XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
if (xmlState != nullptr) {
if (xmlState->hasTagName (parameters.state.getType ())) {
...
...// And then each parameter is read from XML
...
I guess I somehow need to get my wavetable values from “data”.
And yes I will absolutely first append 8 and 2048 before the wavetable array in getStateInformation.