From 2d31153d99f6a03b5a353fa8ae6b63370a0bd742 Mon Sep 17 00:00:00 2001 From: reuk Date: Thu, 27 Apr 2023 18:26:01 +0100 Subject: [PATCH] VST3 Client: Implement IPluginCompatibility --- .../juce_audio_plugin_client_VST3.cpp | 147 +++++++++++++----- .../utilities/juce_VST3ClientExtensions.h | 11 ++ 2 files changed, 119 insertions(+), 39 deletions(-) diff --git a/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp b/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp index 687023fe24..51559c682c 100644 --- a/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp +++ b/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp @@ -106,6 +106,21 @@ JUCE_BEGIN_NO_SANITIZE ("vptr") namespace juce { +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4310) +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wall") + +#if JUCE_VST3_CAN_REPLACE_VST2 + static Steinberg::FUID getFUIDForVST2ID (bool forControllerUID) + { + Steinberg::TUID uuid; + detail::PluginUtilities::getUUIDForVST2ID (forControllerUID, (uint8*) uuid); + return Steinberg::FUID (uuid); + } +#endif + +JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + using namespace Steinberg; //============================================================================== @@ -563,7 +578,7 @@ public: bool isUsingManagedParameters() const noexcept { return juceParameters.isUsingManagedParameters(); } //============================================================================== - static const FUID iid; + inline static const FUID iid { TUID INLINE_UID (0x0101ABAB, 0xABCDEF01, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) }; private: //============================================================================== @@ -729,7 +744,12 @@ public: } //============================================================================== - static const FUID iid; + + #if JUCE_VST3_CAN_REPLACE_VST2 + inline static const FUID iid = getFUIDForVST2ID (true); + #else + inline static const FUID iid { TUID INLINE_UID (0xABCDEF01, 0x1234ABCD, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) }; + #endif //============================================================================== JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Winconsistent-missing-override") @@ -2389,7 +2409,8 @@ private: { return createARAFactory(); } - static const FUID iid; + + inline static const FUID iid { TUID INLINE_UID (0xABCDEF01, 0xA1B2C3D4, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) }; private: //============================================================================== @@ -2460,7 +2481,11 @@ public: AudioProcessor& getPluginInstance() const noexcept { return *pluginInstance; } //============================================================================== - static const FUID iid; + #if JUCE_VST3_CAN_REPLACE_VST2 + inline static const FUID iid = getFUIDForVST2ID (false); + #else + inline static const FUID iid { TUID INLINE_UID (0xABCDEF01, 0x9182FAEB, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) }; + #endif JUCE_DECLARE_VST3_COM_REF_METHODS @@ -3806,46 +3831,12 @@ private: std::atomic isMidiOutputBusEnabled { true }; #endif - static const char* kJucePrivateDataIdentifier; + inline static constexpr const char* kJucePrivateDataIdentifier = "JUCEPrivateData"; CriticalSection flStudioDIYSpecificationEnforcementMutex; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVST3Component) }; -const char* JuceVST3Component::kJucePrivateDataIdentifier = "JUCEPrivateData"; - -//============================================================================== -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4310) -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wall") - -DECLARE_CLASS_IID (JuceAudioProcessor, 0x0101ABAB, 0xABCDEF01, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) -DEF_CLASS_IID (JuceAudioProcessor) - -#if JUCE_VST3_CAN_REPLACE_VST2 - static FUID getFUIDForVST2ID (bool forControllerUID) - { - TUID uuid; - detail::PluginUtilities::getUUIDForVST2ID (forControllerUID, (uint8*) uuid); - return FUID (uuid); - } - const Steinberg::FUID JuceVST3Component ::iid (getFUIDForVST2ID (false)); - const Steinberg::FUID JuceVST3EditController::iid (getFUIDForVST2ID (true)); -#else - DECLARE_CLASS_IID (JuceVST3EditController, 0xABCDEF01, 0x1234ABCD, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) - DEF_CLASS_IID (JuceVST3EditController) - - DECLARE_CLASS_IID (JuceVST3Component, 0xABCDEF01, 0x9182FAEB, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) - DEF_CLASS_IID (JuceVST3Component) -#endif - -#if JucePlugin_Enable_ARA - DECLARE_CLASS_IID (JuceARAFactory, 0xABCDEF01, 0xA1B2C3D4, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) - DEF_CLASS_IID (JuceARAFactory) -#endif - -JUCE_END_IGNORE_WARNINGS_MSVC -JUCE_END_IGNORE_WARNINGS_GCC_LIKE - //============================================================================== bool initModule(); bool initModule() @@ -3953,6 +3944,70 @@ bool shutdownModule() } #endif +// See https://steinbergmedia.github.io/vst3_dev_portal/pages/FAQ/Compatibility+with+VST+2.x+or+VST+1.html +class JucePluginCompatibility : public IPluginCompatibility +{ +public: + virtual ~JucePluginCompatibility() = default; + + JUCE_DECLARE_VST3_COM_REF_METHODS + + tresult PLUGIN_API getCompatibilityJSON (IBStream* stream) override + { + auto filter = createPluginFilterOfType (AudioProcessor::WrapperType::wrapperType_VST3); + auto* extensions = dynamic_cast (filter.get()); + + if (extensions == nullptr || extensions->getCompatibleClasses().empty()) + return kResultFalse; + + DynamicObject::Ptr object { new DynamicObject }; + + // New iid is the ID of our Audio Effect class + object->setProperty ("New", String (VST3::UID (JuceVST3Component::iid).toString())); + object->setProperty ("Old", [&] + { + Array oldArray; + + for (const auto& uid : extensions->getCompatibleClasses()) + { + // All UIDs returned from getCompatibleClasses should be 32 characters long + jassert (uid.length() == 32); + + // All UIDs returned from getCompatibleClasses should be in hex notation + jassert (uid.containsOnly ("ABCDEF0123456789")); + + oldArray.add (uid); + } + + return oldArray; + }()); + + MemoryOutputStream memory; + JSON::writeToStream (memory, var { Array { object.get() } }); + return stream->write (memory.getMemoryBlock().getData(), (Steinberg::int32) memory.getDataSize()); + } + + tresult PLUGIN_API queryInterface (const TUID targetIID, void** obj) override + { + const auto result = testForMultiple (*this, + targetIID, + UniqueBase{}, + UniqueBase{}); + + if (result.isOk()) + return result.extract (obj); + + jassertfalse; // Something new? + *obj = nullptr; + return kNotImplemented; + } + + inline static const FUID iid { TUID INLINE_UID (0xABCDEF01, 0xC0DEF00D, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) }; + +private: + std::atomic refCount { 1 }; +}; + //============================================================================== /** This typedef represents VST3's createInstance() function signature */ using CreateFunction = FUnknown* (*)(Vst::IHostApplication*); @@ -4126,6 +4181,16 @@ private: #endif #endif + static const PClassInfo2 compatibilityClass { JucePluginCompatibility::iid, + PClassInfo::kManyInstances, + kPluginCompatibilityClass, + JucePlugin_Name, + 0, + "", + JucePlugin_Manufacturer, + JucePlugin_VersionString, + kVstVersionString }; + static const PClassInfo2 componentClass { JuceVST3Component::iid, PClassInfo::kManyInstances, kVstAudioEffectClass, @@ -4159,6 +4224,10 @@ private: static const ClassEntry classEntries[] { + ClassEntry { compatibilityClass, [] (Vst::IHostApplication*) -> Steinberg::FUnknown* + { + return new JucePluginCompatibility; + } }, ClassEntry { componentClass, [] (Vst::IHostApplication* h) -> Steinberg::FUnknown* { return static_cast (new JuceVST3Component (h)); diff --git a/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h b/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h index 9d70828f34..70a5fdfd7c 100644 --- a/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h +++ b/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h @@ -94,6 +94,17 @@ struct VST3ClientExtensions All other input buses will always be designated kAux. */ virtual bool getPluginHasMainInput() const { return true; } + + /** This function should return the UIDs of any compatible VST2 plug-ins. + + Each item in the vector should be a 32-character string consisting only + of the characters 0-9 and A-F. + + This information will be used to implement the IPluginCompatibility + interface. Hosts can use this interface to determine whether this VST3 + is capable of replacing a given VST2. + */ + virtual std::vector getCompatibleClasses() const { return {}; } }; } // namespace juce