AU Parameter Order

I’m afraid there is no easy fix for this. DAWs shouldn’t be sorting the parameters by their ids. We would need to add an “order” parameter to the JUCE parameter class and let JUCE generate hashes from the ids while also respecting the order parameter. This would be quite an involved change…

Well, in some sense, also sorting them in alphabetical order is purely arbitrary, and the resulting list may make no sense at all for plug-ins that don’t have a “flat” list of parameters, but have perhaps them divided in “sections”.

Because of that, some DAWs may be assuming that ids have been assigned by plug-in developers so that some meaning is conveyed by sorting by it (for example, all ids of parameters in section B of my GUI may come after the ids of those shown in section A, so that when listed by the DAW they will be neatly separated in two lists).

What is really strange for me is that the same DAW sorts them differently depending on the plug-in format. I would have expected them to be consistent (either using the id/index, or alphabetical order).
Perhaps the documentation of either format explicitly states something regarding parameter sorting?

AU bridges (like for AUv3 or newer Apple AUs) will generate the param IDs by hashing the IDs of the original AU. So many AUs will appear to have random order anyway.

What I meant is, that I am puzzled by the fact that REAPER chooses to sort them differently: it sorts them alphabetically for the VST, but by their id for the AU.

It is the same DAW, so I would have expected its developers to decide for one of the two (either alphabetic or by id) and stick to it for both VST and AU, and all the other formats that are supported.

By seeing that they chose two different way of sorting them, I wondered whether perhaps the VST explicitly specifies somewhere in the documentation, that the DAWs should sort the params alphabetically regardless of their ids (or something like that).

I don’t think REAPER sorts the VST params, they seem in the order I created them. Only the AU sorts.

Neither id nor name is the appropriate way for a host DAW to order its list of parameters, because both are content dependent. The correct way is to list them in the order they were reported to the host, unless the format provides a mechanism for specifying order (ie. AAX page tables)

To my knowledge AU does not have a similar mechanism, but it is difficult to claim the observed issue is bad behavior on the part of these hosts because in addition to Reaper and Live 9, AU Lab and Logic (which is the canonical AU DAW) list JUCE plug parameters ordered by their IDs.

The question is, do non-JUCE AUs exhibit this same behavior, or is there is something inherent in the JUCE AU wrapper that presents the parameters to the AU API in order of the ParamID instead of the order they are “created”?

Any updates on this? Is it possible to fix without breaking backwards compatibility?

1 Like

I strongly suspect this is the case.
In a plugin I have whose params are not inheriting AudioProcessorParameter the parameters order is the same in AU and VST. But in plugins using AudioProcessorParameterWithID, the order is different in the AU indeed.

In a plugin I have whose params are not inheriting AudioProcessorParameter the parameters order is the same in AU and VST. But in plugins using AudioProcessorParameterWithID, the order is different in the AU indeed.

This is because the JUCE AU Wrapper calls AUScopeElement::UseIndexedParameters (numParams) when you use legacy parameters. Apparently the Apple audiounit code stores the parameter list in a std::map when using paramID instead of indexes. And its GetParameterList implementation is:

AUElement::GetParameterList(AudioUnitParameterID *outList)
{
	if(mUseIndexedParameters)
	{
		UInt32 nparams = static_cast<UInt32>(mIndexedParameters.size());
		for (UInt32 i = 0; i < nparams; i++ )
			*outList++ = (AudioUnitParameterID)i;
	}
	else
	{
		for (ParameterMap::iterator i = mParameters.begin(); i != mParameters.end(); ++i)
			*outList++ = (*i).first;
	}

So they are listed in the std::map order , that is by ascending paramID

To be honest, this sucks. Now that I have switched my plugin to the new AudioProcessorParametersWithID parameters, I have a few hundred parameters that are displayed in a random order in the AU plugin hosts (Logic , Reaper etc). This does not feel like progress.

Here is a fix for the AU parameter order issue when using parameter IDs . It does not change the paramID , so nothing should break. However, it requires patching the apple AUScopeElement.cpp and AUScopeElement.h files …

--- a/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUScopeElement.cpp
+++ b/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUScopeElement.cpp
@@ -175,6 +175,9 @@
                                                                mAudioUnit->GetLoggingString(), (int)paramID);
 #endif
                        } else {
+#if AU_PARAMID_ORDER_BUG_PATCH
+                               mParametersOrdered[(int)mParameters.size()] = paramID;
+#endif // AU_PARAMID_ORDER_BUG_PATCH
                                // create new entry in map for the paramID (only happens first time)
                                ParameterMapEvent event(inValue);
                                mParameters[paramID] = event;
@@ -218,6 +221,9 @@
                                                                mAudioUnit->GetLoggingString(), (int)paramID);
 #endif
                        } else {
+#if AU_PARAMID_ORDER_BUG_PATCH
+                               mParametersOrdered[(int)mParameters.size()] = paramID;
+#endif // AU_PARAMID_ORDER_BUG_PATCH
                                // create new entry in map for the paramID (only happens first time)
                                ParameterMapEvent event(inEvent, inSliceOffsetInBuffer, inSliceDurationFrames);
                                mParameters[paramID] = event;
@@ -247,8 +253,13 @@
        }
        else
        {
+#ifdef AU_PARAMID_ORDER_BUG_PATCH
+               for (std::map<int, AudioUnitParameterID>::iterator i = mParametersOrdered.begin(); i != mParametersOrdered.end(); ++i)
+                       *outList++ = (*i).second;
+#else
                for (ParameterMap::iterator i = mParameters.begin(); i != mParameters.end(); ++i)
                        *outList++ = (*i).first;
+#endif // AU_PARAMID_ORDER_BUG_PATCH
        }
 }

diff --git a/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUScopeElement.h b/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUScopeElement.h
--- a/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUScopeElement.h
+++ b/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUScopeElement.h
@@ -270,7 +270,9 @@
        AUBase *                                                mAudioUnit;
 /*! @var mParameters */
        ParameterMap                                    mParameters;
-
+#ifdef AU_PARAMID_ORDER_BUG_PATCH
+       std::map<int, AudioUnitParameterID> mParametersOrdered;
+#endif // AU_PARAMID_ORDER_BUG_PATCH
 /*! @var mUseIndexedParameters */
        bool                                                    mUseIndexedParameters;
 /*! @var mIndexedParameters */

(the indentation of the patch sucks because the indentation of the original file is utterly broken)

3 Likes

Can I bump this? Would be good, if that could be addressed.
There is a solution from @jpo, what needs to be done to get it fixed on develop?
I understand the reluctance to patch apple’s code… I just need it solved.
@fabian maybe?

Or let me rephrase: is there a drawback using JUCE_FORCE_USE_LEGACY_PARAM_IDS=1 ?
I haven’t shipped yet, so I don’t mind which format.

Another finding:
When using the hashed IDs, in Final Cut Pro (10.3.4 i.e. latest) the keyframes cannot be displayed as graph. They only show as dots, that you can move along the time, but you need an extra step to change the value in the HUD sliders.

Vs. if I use the legacy parameters, I can select a parameter and expand to the graph view:

The unfold right of the audio animation toggles the graph view. This switch was not present, when I used the hashed parameters. If you want to try that, not that you have to select a single parameter. The “All” mode will only show dots in time.

When you work on that (also maybe in connection with Parameter groups), please make sure, that you don’t kill that feature, as it works currently only in the JUCE_FORCE_USE_LEGACY_PARAM_IDS mode.

@fabian, @t0m (FR: ParameterGroups for AudioUnits (and VST))

Also, may I raise the question again, what is the benefit to use hashes as IDs, vs. an numerical increasing index? Since the hash produces random results, it is no better than appending new parameters at the end…
(this is not to bugger you, but to understand and make an educated decision, if I use the legacy mode or keep digging)

There is no way to fix this within JUCE and maintain backwards compatibility.

However, we can improve things going forward. One of the things we’re looking at is the introduction of parameter groups. With groups in place we can take the most significant 9 bits of the hash, zero the first (Studio One doesn’t like negative parameter IDs), and fill the next 8 with a uint8 which automatically increases within the group. This means that the first 256 members of each group will appear in the group in the order in which the parameters were added to the AudioProcessor.

We can also expose a macro to disable this new behaviour, so that backwards compatibility will be maintained.

3 Likes

Thanks for checking this thoughtfully.

I understand that the FinalCut differs from the standard AU host implementation quite a bit, and the limitation, that numbering the IDs as 1, 2, 3… makes the graphs work, vs. using arbitrary IDs (like hashes) make the graphs disappear is quite a bummer.

For that reason it would be important for this special project, that I can keep the legacy IDs, even if that means to not have groups in this case.

Sorry to be a bit selfish here :wink:

We’ll probably need to add a special ID-generating function to the AudioParameter class, to help with this: We are removing AudioProcessor-based parameter management

2 Likes

Can someone clarify what I’m seeing?

I’m updating my plugin to v2.0 and added several more parameters. I’ve added the new ones at the end of the creation order and so far, it seems that VST is okay. But when I load a v2 plugin into a Logic project that had parameter automation, it seems that the mappings are different…and I can’t seem to make any sense of it.

Does the hashing have something to do with the number of parameters?

I’ve tried using JUCE_FORCE_USE_LEGACY_PARAM_IDS and JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE but don’t see any difference.

And oddly, when I view my plugin in Logic’s Control view instead of my own UI, the list of parameters is definitely in some odd order. Same thing in Gig Performer. But in Gig Performer, doing the same mapping with VSTs works fine and as expected.

But then getting back to my issue…why would the order change when I’m just adding some parameters to the end of the list?

We had the same issue with AU parameter ordering with JUCE, but automation in Logic has its own quirks that make it more confusing/difficult… their automation menu always sorts alphabetically (example is their Multipressor plugin):

There also seems to be a Logic X bug in which parameters show up in the automation menu based on how many parameters your plugin has:

hello, just a bump to say that i just encountered this on a fresh clone, following the new best practice from demos (not having made a plugin with JUCE in many years.)

for what it’s worth, both of the solutions above worked for me (JUCE_FORCE_USE_LEGACY_PARAM_IDS, or hacking AUElement accessors.)

but the default behavior is really unfortunate for newcomers to the framework; making an AU with generic interface is, i’d guess, a very common first task.

It’s even more complicated than that if you want consistency with AUv3:

hi everyone,

where do I put this exactly in the audioplugin code and with what syntax?

JUCE_FORCE_USE_LEGACY_PARAM_IDS