I’m sorry for the length of the post ahead. Unfortunately the subject is complex and I wanted to make it as clear as possible.
So, I have finally done some testing and reached some conclusions.
My suggested patch is provided by this pull request:
Essentially, it revolves around three distinct changes.
I’ll list them here, and below the list explain why I think they will not break any existing session saved with plug-ins compiled with the previous code.
-
Initialize the vstEffect.pluginVersion in the constructor with the value of JucePlugin_VersionCode (or JucePlugin_VSTChunkStructureVersion if that one is used) like this:
#ifdef JucePlugin_VSTChunkStructureVersion
vstEffect.plugInVersion = JucePlugin_VSTChunkStructureVersion;
#else
vstEffect.plugInVersion = JucePlugin_VersionCode;
#endif
without any “mangling” by the “conversion funcion” which was used before.
-
On the other hand, the value returned by handleGetManufacturerVersion() should still be passed through the conversion function, like this:
pointer_sized_int handleGetManufacturerVersion (VstOpCodeArguments)
{
return mangleHexVersionForDisplay (JucePlugin_VersionCode);
}
I have renamed such function mangleHexVersionForDisplay() for reasons given below.
-
The body of that convertion function is essentially the following (stripping comments):
static inline int32 mangleHexVersionForDisplay (const unsigned int hexVersion)
{
#if JUCE_VST_RETURN_HEX_VERSION_NUMBER_DIRECTLY
return (int32) hexVersion;
#else
const PluginHostType hostType;
if (hostType.isSteinberg ())
{
const int32 major = (hexVersion >> 16) & 0xff;
const int32 minor = (hexVersion >> 8) & 0xff;
const int32 bugfix = hexVersion & 0xff;
if (major < 1)
return major * 1000 + minor * 100 + bugfix * 10;
if (major > 100)
return major * 10000000 + minor * 100000 + bugfix * 1000;
return hexVersion;
}
return hexVersion;
#endif // JUCE_VST_RETURN_HEX_VERSION_NUMBER_DIRECTLY
}
which basically means: just return the hexVersion intact, unless we are in a Steinberg DAW: in that special case, do some manipulation on it in some corner cases (major < 1 or > 100) in order for the version number to display correctly, because Steinberg DAWs need that, as shown in my post above.
Now I’m going to answer some possible doubts and concerns about the proposed change, but first, let’s take a moment to consider that this change only affects the VST2 wrapper code.
DAWs that only use other formats (Logic, Pro Tools) will not be affected by this change at all. And that’s good, because they already have their idiosincracies.
Q: This change implies that the value returned by handleGetManufacturerVersion() could differ from what is set in vstEffect.plugInVersion upon initialization. Can this cause problems?
A: No, that is not expected to cause problems, because:
-
This situation can already happen even with the current code, when one uses a JucePlugin_VSTChunkStructureVersion that’s different from JucePlugin_VersionCode, and doesn’t seem to have caused any problem so far
-
If one uses only JucePlugin_VersionCode, then the two values will only differ inside Steinberg DAWs. I have done some tests with them, and the plug-ins never showed any malfunction.
Q: My plug-in was compiled with JUCE_VST_RETURN_HEX_VERSION_NUMBER_DIRECTLY = 1, will something change for me?
A: no, nothing will change for you: the behavior of that macro is retained so that if you define it to 1, you are guaranteed that your hex code is passed intact rather than manipulated. It’s just that after this change, there are less places where the manipulation could occour.
Q: My plug-in was compiled without setting JUCE_VST_RETURN_HEX_VERSION_NUMBER_DIRECTLY, what will change for me?
A: Everything will work as expected, and this is why I believe so:
Now, let’s consider the behavior of the convertHexVersionToDecimal () function as it is now:
static inline int32 convertHexVersionToDecimal (const unsigned int hexVersion)
{
return (int32) (((hexVersion >> 24) & 0xff) * 1000
+ ((hexVersion >> 16) & 0xff) * 100
+ ((hexVersion >> 8) & 0xff) * 10
+ (hexVersion & 0xff));
}
It is easily seen that this function returns a value that is:
- equal to
hexVersion, when hexVersion is between 0 and 255
- less than
hexVersion, when hexVersion is 256 or above.
This means that initializing vstEffect.plugInVersion directly with the version hex code, rather than passing it through the conversion funcion (change #1), will always result in a vstEffect.plugInVersion equal or greater than the one obtained with the previous code.
According to my tests (table below), an increment in the value of vstEffect.plugInVersion will never prevent the plug-in from receiving the data saved in the session.
I have tested it only with the mentioned DAWs because that’s what I could have easy access to.
As you can see, only Studio One and Cubase seem to care about that value, and they refuse to pass the data only when plug-ins report a lower value. When the value is equal or higher, everything works as normal.
The other two major DAWs that I have tested with, Ableton Live and Cakewalk SONAR, simply ignore that value, so there’s no problem for those either.
Please also note that the comparison performed is purely arithmetic: if Cubase took into account the version number encoded by the version code, the tests 6 and 7 would have had the opposite outcome.
These tests also show that what’s returned by handleGetManufacturerVersion() is not taken into account in any of those hosts.
Studio One 3 does not even call that function, and Cubase only does so during the scanning of the plug-ins, probably to obtain the version number to be displayed, according to the rules of previous posts.
In consideration of this, letting handleGetManufacturerVersion() return the version code unmodified in most cases, with the sole exception of Steinberg hosts (behavior implemented in mangleHexVersionForDisplay()) seems quite safe to me.
Additional special cases for other DAWs could be added to that funcion if that proves necessary, and I believe that putting this change on a separate branch for experimentation and testing is the safest and most desirable choice, also to bring those special cases to the surface if they exist.
EDIT: I have intentionally left out of this post the discussions about:
- whether it’s a good idea to move the entire body of
mangleHexVersionForDisplay() inside handleGetManufacturerVersion(), because that’s the only remaining sport where it is called from
- how to warn the developer if the
JucePlugin_VersionCode that he is using is breaking some of the constraint that would lead Cubase to misinterpret the version number to display.
I’ll leave these (especially the second one) for later.