AAX unexpected state save/recall

Please give me a reality check here, if this sounds crazy or is somehow expected behavior…

I’ve got a plug-in, with parameters handled by an AudioProcessorValueTreeState, following all the guidelines from the APVTS tutorial.

In testing, I took out all the code from the Processor’s getStateInformation and setStateInformation methods. So there’s no APVTS copying or replacing happening there.

Despite this, Pro Tools is saving and recalling parameter values in sessions. And it also is happy to create and recall presets with its built-in preset management menu.

Reaper and Ableton do NOT save/recall plug-in state when I close/open sessions (which is is the expected behavior, with getStateInformation and setStateInformation gutted). Is Pro Tools doing something crazy, or am I overlooking something obvious?

(Yes, I suppose the next step is to dive into the docs from the AAX SDK, but really hoping for a reality check here before I go to that level…)

Is it actually setting the state or is it just the parameters which are being changed? Pro Tools still has access to the parameters even without those function calls so its probably just recalling the parameters it saved the first time you opened it.

Right, Pro Tools is probably just going in and changing the parameter values around, same as if it was sending automation data.

But if so, doesn’t that mean it’s circumventing the expected method, when it comes to saving and recalling parameter values? What if I need to do a translation of parameter values, from an older to a newer version of a plug-in? If I put those translation routines into setStateInformation, they’re not going to get called?

In any case, it isn’t just recalling the parameters it saved from the first time I opened it (I read about that weird behavior too). It is saving the parameter values, whatever they might have been changed to via the GUI, into a session.

do you have any control surface connected?
when a control surface connected, Pro Tools automatically adds some parameters to be automated.
while many other hosts wouldn’t need explicitly setting the automation per parameter.

another thing if I recall, if you’ve sent a parameter value notifying the host it would capture it…

in older JUCE trying to save without implementing the `set/getStateInformation`` would fail as PT won’t except 0 as valid data. so it might’ve changed from last time I’ve checked. (I’ll try to dig in later today)

Hi @ttg, thanks for the tips. No control surface connected, however…

Related: my current hope is that PT is only grabbing parameter values “directly” because getStateInformation isn’t returning valid data. So, if I want getStateInformation to effectively “do nothing”, then I actually have to make it return the parameters’ default values, rather than actually have it returning nothing.

I’ll try out that idea soon…

OK, tried that idea, and it still doesn’t work.

First, I re-implemented the getStateInformation method, direct from the APVTS tutorial.

Then I modified it slightly, to alter one of the parameter values, so I could test if this is where Pro Tools was actually pulling parameter values from. In the end, it looked like this:

void getStateInformation (MemoryBlock& destData) override
{
    auto state = parameters.copyState();
    std::unique_ptr<XmlElement> xml (state.createXml());
    DBG ("original params:\n" + xml->createDocument(String(), false, false));
    forEachXmlChildElementWithTagName (*xml, child, "PARAM")
	{
		if (child->getStringAttribute("id") == "gain") child->setAttribute("value", "0.666");
	}
	DBG ("modified params:\n" + xml->createDocument(String(), false, false));
    copyXmlToBinary (*xml, destData);
}

This produces the following debug window output, with the gain slider set to “1”, when selecting “Save Settings” in the Pro Tools “Preset” menu in the plug-in window:

original params:
<MyParams>
  <PARAM id="gain" value="1"/>
</MyParams>

modified params:
<MyParams>
  <PARAM id="gain" value="0.666"/>
</MyParams>

So here it should be clear that we’ve told Pro Tools to save the gain param at 0.666, instead of the “1” value that the slider was actually set at.

However, when recalling the preset, the slider goes back to “1”, instead of “0.666”.

That is unexpected behavior, right???

This is building with JUCE 5.4.3, and AAX SDK 2.3.1, and testing in the Pro Tools Development Build 12.8.2 on macOS 10.13.6.

@jules could you weigh in on this? (Or let me know if someone else is more the AAX-wrapper specialist there, who I could ask?)

It was my understanding that set/getStateInformation was supposed to be the interface for the host to set and get plug-in parameter values. Reaper and Ableton seem to be following that protocol. Pro Tools is doing something else, however. So, either my understanding was incomplete, or there is something buggy going on.

The current behavior is breaking something in my plug-in project, so I need to find a way around it. Before diving in to the inner workings of the AAX Wrapper and parsing Avid’s SDK docs, I’d like to just hear if JUCE is supposed to be handling this the way I thought it should. Thank you.

I’ve not yet dived into this, but JUCE plug-ins should behave (approximately) identically across the different formats. It may well be that something is amiss, but you’re the first person to report it.

Thanks, @t0m. Does the getStateInformation code I posted above seem like a decent, concise test of Pro Tools’ behavior?

Checking in about this issue - @t0m, is this something you’ll be testing? Would it help for me to add this as a “New Issue” over on Github? I’m unsure of the best protocol here… thanks.

That doesn’t seem unreasonable to me, hosts are free to read / write the parameter values and not require you to implement get/set state methods. That way unless you need some other information to be saved specifically it will work without you having to re-invent the wheel.

To be clear you haven’t told Pro Tools anything here, Pro Tools has no idea what information a plugin will pass it in a getStateInformation() call.

Have you confirmed that the same information is passed back to the setStateInformation() function? do the setParameter() calls happen before or after the setStateInformation()? if it’s after that’s presumably the problem for you, however I can’t see how there is much you can do about that other than maybe reach out to AVID and ask them to call the setStateInformation() (or what ever the equivalent is in the AAX SDK) after the parameter values have been set, so you can for whatever reason change them.

Out of interest what is the use case here? why do you not want Pro Tools to load the parameters in the way any user would reasonably expect them to load?

Thank you for the reply, Anthony. Your questions helped me figure out what was going on here.

Yes, I agree - after some reflection I came to the same conclusion, that providing NO data back to the host from getStateInformation might cause it to fall back to a “sensible” default behavior. So that’s why I then substituted that other code shown above for getStateInformation, where I do provide data back, but where I alter a parameter value.

OK, fair enough - you’re making the distinction that Pro Tools doesn’t parse the XML I pass it from getStateInformation. So, to correct my previous statement: it should be clear that I’ve passed Pro Tools a set of XML data with the gain param set at 0.666.

Good question - yes, I just checked that, and I can confirm that now. If I passed Pro Tools a param value of 0.666 in the XML data, it passes that value back to setStateInformation.

And therein lies the source of my confusion, I think. If, in setStateInformation, I don’t call AudioProcessorValueTreeState::replaceState, then Pro Tools still loads its own set of saved parameter values. Going back to your first point, about hosts reading/writing param values however they like, that makes a certain sense.

However, this breaks the mental model I had about Pro Tools’ preset files - that they would be storing ONE set of param values. In fact, they seem to be storing TWO sets of param values, their own snapshot, and whatever XML data you pass to the host. OK, fine, my mental model stands corrected on this matter!

This also answers your question, “do the setParameter() calls happen before or after the setStateInformation()?”. Thankfully, the setParameter calls must be happening before setStateInformation call, since if replaceState is called, then those XML-saved values are indeed the final values displayed in the plug-in.

The use case here is to prevent preset saving/loading, as a limitation on the demo version of a plug-in.

In essence, the part that tripped me up here is that in Pro Tools (unlike in the other hosts I tested), in order to have preset saving/loading “do nothing”, I cannot just let the code “do nothing”. Instead, when the plug-in is in demo mode, I will need to set the APVTS to default parameter values when setStateInformation is called. This will have the unfortunate side effect of wiping out a user’s current settings if they innocently try to load a preset file while in demo mode…

Thanks again for engaging with this issue.

I can see the source of confusion, I haven’t taken a deep look at the AAX SDK documentation but I suspect the get/set sate equivalent (get/setChunk I think) is simply for additional information not stored in the parameters. However by restoring the parameter values before calling setState they at least give the developer the option to override the default behaviour. In other words you have to opt-out of the behaviour rather than opt-in. Of course because you’re using JUCE and trying to build for other formats/DAWs the behaviour is unforgivingly and often subtly different, so if you want you’re code to be generic for all formats then you will end up overwriting the parameter information in this circumstance.

I see what your issue is though, I guess you want presets and sessions to save correctly but to do nothing when loaded?

You can either find a very tricky workaround (for example undo one parameter change for each parameter on a call to setStateInformation()) or just have some slightly different behaviour for Pro Tools only - i.e. parameters get defaulted when loading presets in demo mode. TBF users should expect the state of the plugin to change when trying to load a plugin and they should also be able to undo the action from Pro Tools. You could even make that the behaviour for all formats for consistency - most DAWs should let you undo the preset load (on that note I wonder if Pro Tools calls getStateInformation() before hand so it can restore the state on an undo?)

Looking at the AAX wrapper I wonder if stopping setChunk calling the base class would stop the parameter values loading?

Bear in mind, that the host has two ways of restoring the parameters:

  • calling setStateInformation
  • sending automation data

So if the parameters change but you didn’t do anything in setStateInformation, maybe the automation is set to “Read” or a controller is sending data?

…just a thought…

2 Likes

Actually, the idea is that, when running the plug-in in demo mode, that there would be no type of settings recall happening (no saving, no loading). No saving plug-in settings when closing a session, and no saving plug-in settings via Presets. And yes, also to “do nothing” when loaded.

Now that I understand Pro Tools’ behavior better, I can do this – although as mentioned above, it is at the price of reverting parameters to default values whenever a Preset load is attempted, rather than actually “doing nothing”.

Yes, they should definitely expect the state of the plug-in to change. And while I agree with the latter point, that’s unfortunately not how Pro Tools behaves. There is no host undo for preset loads in Pro Tools. Perhaps if I add my own undo manager, along with GUI controls for undo/redo, then users could at least do a “local” plug-in undo. (For the record, I haven’t implemented an undo manager yet in JUCE for the APVTS, so I’m just guessing about that.)

While that’s a good possibility to check, that’s not what was going on here.

The heart of the matter is confusion about Pro Tools’ dual sets of parameter save data. If you open a plug-in Preset (a .tfx file) in a text or hex editor, you can see this for yourself.

First in the file there’s some header stuff, and then a character sequence that spells out “Complete Controls State”. This is followed by a set of numerically indexed data, one for each parameter in the plug-in. Last in the file is any XML data passed to Pro Tools from getStateInformation. (If you don’t implement getStateInformation, then there’s no XML data in the preset file.)

Now, when you load a Preset file in Pro Tools, the host does two things in rapid sequence:

  1. Using its own snapshot of the plug-in’s parameters (the part stored as “Complete Controls State”), it sets the parameters to those values. This is not done via setStateInformation, but presumably by setParameter() calls. As I understand it, this is the same method that Pro Tools uses to send automation data to plug-ins.

  2. It then passes any saved XML to setStateInformation. If you’ve implemented that method, then you can overwrite the parameter values Pro Tools just pulled from its “Complete Controls State” data with whatever you like.

That, anyways, is my understanding from lots of testing and observing Pro Tools’ behavior.

I am a bit confused when looking at the JUCE AAX wrapper code, since in the SetChunk method the pluginInstance->setStateInformation call happens before all the parameters are iterated, calling SetParameterNormalizedValue for each one… but probably I just don’t have a complete understanding of what else is going on in that wrapper.

I think that’s just to fix a bug of some sort, I think that simply sets the parameter values to what they already are. The parameter values I suspect are actually changed when the base class setChunk is called, as the “juce” chunk is the last chunk to be set, so one of those other chunks is setting the parameters before hand, hence why I think if you don’t let the base class be called you might find you get the behaviour you expect / want - however I should warn you that another host may come along and decide to behave in a similar fashion to Pro Tools (Cubase maybe? possibly even Logic?), or someone could make a plugin to host your plugin that plays this game to get around your plugin not recalling presets.

Basically I think making the setState default the plugin is the safest option for all plugin formats. When loading a session it would appear to behave as you expect anyway, and when loading a preset it will wipe the settings but then what did the user expect?

Also I think adding in your own undo mechanism might be difficult for Pro Tools, it will be hard to detect when the transaction started, you’ll only know when it ended. I’m really surprised Pro Tools doesn’t allow an undo of this, I could have swore it did allow users to undo preset loads. Does it call getState before setState when loading a preset? If so why would it do that unless it wanted to store the state so it can be reverted?

Ah, I see now. Thank you for explaining.

Good point about safeguarding against unexpected behaviors from other DAWs. You never know, so overwriting the parameter state with default values does indeed seem to be the safest option for across-the-board consistency.

To follow up on this part: I just double-checked, and Pro Tools does not call the getState method when loading a preset.

Pro Tools is my primary DAW, and I’ve had to reload the entire session before after accidentally changing a plug-in’s settings. o_O

1 Like