I’m currently working on a project that uses a JUCE wrapper to replace a VST SDK-based plugin (and several other wrappers). With a matching Manufacturer Code and Plugin Code, I can get the VST2 and AU versions of the JUCE plugin to replace the existing plugin in DAW projects.
But for VST3, the UID that the host uses to identify plugins is partially hard-coded in JUCE:
This seems to do the trick (in my particular situation), but I’d appreciate some input from someone more experienced. So far only tested in Reaper (win and macOS).
In juce_VST3ModuleInfo.h:
[[maybe_unused]] static VST3Interface::Id getVST3InterfaceId (VST3Interface::Type interfaceType)
{
#if JUCE_VST3_CAN_REPLACE_VST2
if (interfaceType == VST3Interface::Type::controller || interfaceType == VST3Interface::Type::component)
return VST3Interface::vst2PluginId (JucePlugin_VSTUniqueID, JucePlugin_Name, interfaceType);
#endif
#if defined(CUSTOM_UID)
if (interfaceType == VST3Interface::Type::component)
{
auto res = VST3Interface::Id();
constexpr std::array<unsigned, 4> UID = CUSTOM_UID;
for (int i = 0; i < 16; ++i)
{
const unsigned pos = i / 4;
const unsigned off = (3 - (i % 4)) * 8; // big-endian
res[i] = static_cast<std::byte>((UID[pos] >> off) & 0xFF);
}
#if JUCE_WINDOWS
std::swap(res[0], res[3]);
std::swap(res[1], res[2]);
std::swap(res[4], res[5]);
std::swap(res[6], res[7]);
#endif
return res;
}
#endif
return VST3Interface::jucePluginId (JucePlugin_ManufacturerCode, JucePlugin_PluginCode, interfaceType);
}
In your project’s preprocessor definitions (example): CUSTOM_UID={0x01234567,0x89ABCDEF,0x78965412,0x19732846}
I must be doing something wrong, because this doesn’t work for me. I replaced the custom UID macro with this: JUCE_VST3_COMPATIBLE_CLASSES=VST3Interface::hexStringToId(“0123456789ABCDEF7896541219732846”)
but Reaper doesn’t recognize it as the same plugin. It might also be a DAW issue, but that’s all the more reason to actually use the old UID.
Another (definitely not me doing things wrong) issue is that this approach doesn’t work if the user decides to go to a previous (pre-JUCE) version. Even though it would be a PITA to modify JUCE on every machine with every JUCE update, I’d still prefer to do that than introduce easily avoidable incompatibility.
EDIT: I guess the hexStringToId() approach needs some/all of the byte shuffling done when creating the UID string, is that correct? Still, I think my second point stands.
Yes you would need to check what the actual value is of the VST3 you want to replace.
Looking back at the implementation you’ve suggested I think if we add something like this we would expect the CUSTOM_UID to be the correct UID, no need to shuffle bytes. That should also make managing the change in JUCE for yourself that little bit easier.
Having it be the final UID without shuffling bytes is fair enough I guess.
I keep trying to figure out the hexStringToId() approach, but it just doesn’t want to work. I got to a point where my original implementation and hexStringToId() return the same byte array, but Reaper still doesn’t replace the instance with the new version.
I’m not sure how to check whether or not it works in the test host. I did just try it in Cubase and it doesn’t work there either, so I’m really confused…
EDIT: I just double checked and the byte arrays produced by my code and hexStringToId() are definitely the same, so I really have no idea what’s going on. Is there something more needed for this to work? I assumed not overriding VST3ClientExtensions::getCompatibleParameterIds() would just result in state being messed up, am I wrong?