AU Parameter Order

Is there something I need to do to sort my parameters for AU plugins. For VST they are in the order I create them. For AU, they appear in a random order. Is there anything I can do about that or is it up to the host?

AU left, VST right. Reaper host.

2 Likes

Which host is this? I think the host is ordering via the ParameterID. JUCE uses hashes of the parameter name to create the ids - hence the random order. In any case, the host should not be ordering them by the IDs - for example: when using AUv3s OS X/iOS will also use a hash for the ParameterID.

This is the latest version of Reaper

Can confirm that the same ā€˜randomnessā€™ happens with AUs in Live 9.

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?