Problem with VST3 Bypass implementation

Continuing the discussion from VST3 Bypass:

In the current VST3 implementation, the special Bypass parameter is given an id which depends on the number of parameters exposed by the AudioProcessor.
put simply, it is

Bypass.id = processor.getNumParameters().

Let’s say that I have 10 parameters in my AudioProcessor, indices for them will be 0-9 and Bypass will have id = 10.

Now, if I release a new version of my plug-in which has 5 more parameters in the AudioProcessor, they will be numbered 0-14 and the Bypass will be given id = 15.

The problem is: if I saved a host session with the earlier version, automation for the bypass parameter has been recorded with id = 10 there.
When I install the newer version of the plug-in and reopen the same session, the automation recorded for id = 10 will not apply to the bypass anymore, because now the bypass has id = 15.

My proposed solution is to reserve a (very high!) constant ID to be given to the Bypass parameter in the VST3 wrapper, no matter how many parameters are there in the AudioProcessor.

I am aware that this implies breaking some of the automation that may have been created since when the Bypass parameter has been made automatable in VST3, but that was only 23 days ago according to GitHub and thus I think this is the lesser evil (in contrast with leaving behind this chronic problem)

Ok, I get that you had much to do lately because of 4.2 and the problems you had to fix after it has been released, but most of them seem to have been fixed now, could you please take pay attention to this?
Perhaps @fabian is the right person to have a look at this since it’s him who committed the VST3 bypass code?

Yes, this falls within Fabian’s area of expertise - I’m sure he’ll get onto it as soon as he’s back next week.

This problem unmasks a deeper, and more insidious issue: all of the JUCE plug-in format wrappers use the parameter index as the parameter ID.

While expedient for getting a plug up and running, there is a very good reason why all modern plug-in formats (RTAS, AU, VST3, AAX) use a distinct parameter ID instead of just relying on an index or name… both the index and name of a control are prone to change over the lifetime of a real-world plug-in. It is possible to store version information and do appropriate translation in session and preset save/restore, but it is NOT possible to do that for automation. The easiest design solution for automation to properly follow a particular parameter regardless of changes to that parameter’s name or index within the list is to have a separate identifier – the ID – which does not change no matter how the parameters are reordered or renamed in various versions of a plug. A FourCharCode (uint32) will work as an ID in AU, AAX, and VST3.

Unfortunately the quick workaround @yfede suggested (a high fixed ID for bypass) won’t work because the wrappers rely on the fact that the ID is the index and vice-versa. Assigning an ID/index beyond the number of controls will fail. AAX parks the Master Bypass at index 0, but doing so creates a confusing situation where the ID of parameter with index N is actually (N-1)… lots of fun to keep track of while debugging. Doing that isn’t a realistic option for addressing the bypass issue in VST3 anyway. You could check for the special-case bypass ID everywhere there is a ID-index conversion, but that is an ugly hack that doesn’t address the underlying problem.

A proper fix will require removing the assumption in the wrappers that the content of the ID depends on the index - so that each wrapper uses IDs instead of indices where supported by the format. To maintain compatibility with existing projects, the default IDs could be populated with the indices of the parameters. BUT THERE MUST BE A MECHANISM FOR SPECIFYING IDs TO OVERRIDE THESE AUTO-GENERATED ONES.

Once these changes are in place, VST3 bypass can be added at the end of the parameter list as it is now, and given a fixed ID such as ‘mbyp’

Not a quick fix, but a necessary one.

I’d suggest a pair of methods in AudioProcessor for handling the translation:
virtual const uint32 getParameterIDFromIndex (int index);
virtual const int getParameterIndexFromID (uint32 id);

And expanding the parameter class to include a uint32 id member.

Frank, thanks for the nice writeup of the problem and a proper solution. That is definitely my vote.

@frankfilipanits well yes, I am very aware of the limitations of the id = index simplification.

I agree very much with your proposed solution, which seems even more needed now that, by mean of the AudioProcessorParameter classes, the index is assigned implicitly to a parameter, based on the order of its addition to the AudioProcessor.
At least, when the getParameterXYZ(int index) methods were the only way to go, the parameter index that was being queried/referred was explicit, right there as an argument.

(somehow related to this, is this other discussion I had with jules: AudioProcessorParameter and their indices)

The only bit of your post that I don’t agree with is this:

the quick workaround @yfede suggested (a high fixed ID for bypass) won’t work because the wrappers rely on the fact that the ID is the index and vice-versa

That would be true if the parameter were to be exposed to the underlying AudioProcessor, but the VST3 bypass is not: it is entirely handled by the wrapper and that is the reason why it is given an index/id that is beyond the getNumParameters() of the AudioProcessor.

In such circumstance, that parameter is free to have an id that is different from its index, and thus my proposed workaround still seems valid to me:
set the id of the bypass to some very high, constant value, so that the indices/ids of the parameters exposed by the AudioProcessor are very far from it until the identity id==index is abandoned

@yfede I looked back through the code again and you may be right about a high fixed ID working as a result of being restricted to the wrapper. But the current state of the code sets off all kinds of red flags about relying on that to work, because there are many places where ID and index are used interchangeably.

Also, while that may provide a workaround for bypass, it doesn’t address other revisions to parameter indices.

TL;DR is that relying on the index as the ID (and vice versa) was not a good design choice for the wrappers and should be fixed properly, with an independent ID.

Yes, I also saw those problems recently when working in various JUCE plug-in wrappers. I’m surprised so many plug-ins seems to report their ID as indices. We will attempt to fix this on a release candidate/bleeding edge branch once 4.2.1 is tagged.

1 Like