AU / AUv3 Wrappers are never saving the full state of a plugin

The wrappers always call AudioProcessor::getCurrentProgramStateInformation() even if asked to save the full state. That seems like a bug to me.

I know it’s ADC right now, but any chance we can get a quick fix on this. It’s a pretty big show stopper for us since our AU can’t load or save state.

The issue is the JUCE AU and AU3 wrappers never call AudioProcessor::getStateInformation() or AudioProcessor:: setStateInformation(), instead they call AudioProcessor::getCurrentProgramStateInformation() and AudioProcessor:: setCurrentProgramStateInformation().

I’m assuming this works for most people because the default implementation of getCurrentProgramStateInformation() and setCurrentProgramStateInformation() just call the other functions:

//==============================================================================
void AudioProcessor::getCurrentProgramStateInformation (juce::MemoryBlock& destData)
{
    getStateInformation (destData);
}

void AudioProcessor::setCurrentProgramStateInformation (const void* data, int sizeInBytes)
{
    setStateInformation (data, sizeInBytes);
}

But if you actually provide a implementation of these functions that differ the AU calls the wrong ones. (I don’t think AU has the concept of only storing the program state)

Changes should be:

diff --git a/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm b/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm
index d3a187f48..cc1116a88 100644
--- a/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm
+++ b/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm
@@ -702,7 +702,7 @@ ComponentResult SaveState (CFPropertyListRef* outData) override
         if (juceFilter != nullptr)
         {
             juce::MemoryBlock state;
-            juceFilter->getCurrentProgramStateInformation (state);
+            juceFilter->getStateInformation (state);
 
             if (state.getSize() > 0)
             {
@@ -749,7 +749,7 @@ ComponentResult RestoreState (CFPropertyListRef inData) override
                     const juce::uint8* const rawBytes = CFDataGetBytePtr (data);
 
                     if (numBytes > 0)
-                        juceFilter->setCurrentProgramStateInformation (rawBytes, numBytes);
+                        juceFilter->setStateInformation (rawBytes, numBytes);
                 }
             }
         }
diff --git a/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm b/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm
index 96e7ec6b9..a1b74bad9 100644
--- a/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm
+++ b/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm
@@ -566,7 +566,7 @@ void setCurrentPreset(AUAudioUnitPreset* preset) override
         }
 
         juce::MemoryBlock state;
-        getAudioProcessor().getCurrentProgramStateInformation (state);
+        getAudioProcessor().getStateInformation (state);
 
         if (state.getSize() > 0)
         {
@@ -612,7 +612,7 @@ void setFullState (NSDictionary<NSString*, id>* state) override
                 const juce::uint8* const rawBytes = reinterpret_cast< const juce::uint8* const> ([data bytes]);
 
                 if (numBytes > 0)
-                    getAudioProcessor().setCurrentProgramStateInformation (rawBytes, numBytes);
+                    getAudioProcessor().setStateInformation (rawBytes, numBytes);
             }
         }

I think that would be a good quick fix. However if I understand this document correctly AU does differentiate between a preset and the full state.

Thank you for reporting.

Here’s the quick fix:

It seems worthwhile making this change ASAP, but we’ll investigate more thoroughly once ADC is wrapped up.

Isn’t this going to break compatibility for existing AU plugins when they will try load data saved with setCurrentProgramStateInformation using getStateInformation ?

That depends a bit on your implementation of getCurrentProgramStateInformation() and setCurrentProgramStateInformation(). As G-Mmon mentioned the default implementation makes no difference between a preset and a full state.

But it might need a #define to enable legacy behavior for some plugins.

I’m trying to think through the consequences of this, but I’m not getting much help from the Apple docs.

There’s this thread from 10 years ago which prompted the getCurrentProgramStateInformation behaviour

so I suspect the quick fix might have broken more than just backwards compatibility. @Koull can you remember what the issue was? I’ll put together some more complex preset loading tests tomorrow.

AU apparently doesn’t have the concept of banks. They request basically the state of the plugin, whatever the developer thinks that is.

So I think it should be up to the developer what he wants for the AU wrapper to call. Let’s simply have a define so the developer can decide if he wants “set/getStateInformation” or “set/getProgramStateInformation” called. Always calling either doesn’t make anyone happy.

We are about to release Nexus3 on Friday and had to fork JUCE just so we could make sure the entire state is saved, and not only the current preset.

The preset is just that, the things that define the sound. But the state of Nexus3 includes much more. All settings for that particular instance. What page is currently selected, what preset is currently selected in the browser, what filters, search-term is currently set etc.

Imagine our surprise when we saved a project with Logic and upon recall, the sound played fine, but all settings for that instance were lost.

Thank you all for your input.

3 Likes

Thank you :smiley:

Hi Tom,

That was so long ago, I am not sure I remember exactly the issue.

Cheers

Kyriacos