From bf72ff0851b51b07830a831baf333465017a05ae Mon Sep 17 00:00:00 2001 From: jules Date: Thu, 13 Mar 2014 13:54:50 +0000 Subject: [PATCH] Added VST3->VST2 backwards compatibility, with a flag JUCE_VST3_CAN_REPLACE_VST2 to disable this if you want your VST2 + 3 versions to use different IDs and not be interchangable. --- .../VST3/juce_VST3_Wrapper.cpp | 153 ++++++++++++++++-- 1 file changed, 138 insertions(+), 15 deletions(-) diff --git a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp index 07f8bb212b..278a17a296 100644 --- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -34,6 +34,23 @@ #include "../utility/juce_IncludeModuleHeaders.h" #include "../../juce_audio_processors/format_types/juce_VST3Common.h" +#ifndef JUCE_VST3_CAN_REPLACE_VST2 + #define JUCE_VST3_CAN_REPLACE_VST2 1 +#endif + +#if JUCE_VST3_CAN_REPLACE_VST2 + #if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4514 4996) + #endif + + #include + + #if JUCE_MSVC + #pragma warning (pop) + #endif +#endif + #undef Point #undef Component @@ -750,10 +767,30 @@ public: return kResultOk; } - tresult PLUGIN_API setIoMode (Vst::IoMode) override { return kNotImplemented; } - tresult PLUGIN_API getRoutingInfo (Vst::RoutingInfo&, Vst::RoutingInfo&) override { return kNotImplemented; } + tresult PLUGIN_API setIoMode (Vst::IoMode) override { return kNotImplemented; } + tresult PLUGIN_API getRoutingInfo (Vst::RoutingInfo&, Vst::RoutingInfo&) override { return kNotImplemented; } + + #if JUCE_VST3_CAN_REPLACE_VST2 + void loadVST2CompatibleState (const char* data, int size) + { + const int headerLen = htonl (*(juce::int32*) (data + 4)); + const struct fxBank* bank = (const struct fxBank*) (data + (8 + headerLen)); + const int version = htonl (bank->version); + + jassert ('VstW' == htonl (*(juce::int32*) data)); + jassert (1 == htonl (*(juce::int32*) (data + 8))); // version should be 1 according to Steinberg's docs + jassert (cMagic == htonl (bank->chunkMagic)); + jassert (chunkBankMagic == htonl (bank->fxMagic)); + jassert (version == 1 || version == 2); + jassert (JucePlugin_VSTUniqueID == htonl (bank->fxID)); + + pluginInstance->setStateInformation (bank->content.data.chunk, + jmin ((int) (size - (bank->content.data.chunk - data)), + (int) htonl (bank->content.data.size))); + } + #endif - bool readFromMemoryStream (IBStream* state) const + bool readFromMemoryStream (IBStream* state) { FUnknownPtr s (state); @@ -767,14 +804,19 @@ public: if (s->getSize() >= 5 && memcmp (s->getData(), "VC2!E", 5) == 0) return false; + #if JUCE_VST3_CAN_REPLACE_VST2 + loadVST2CompatibleState (s->getData(), (int) s->getSize()); + #else pluginInstance->setStateInformation (s->getData(), (int) s->getSize()); + #endif + return true; } return false; } - bool readFromUnknownStream (IBStream* state) const + bool readFromUnknownStream (IBStream* state) { MemoryOutputStream allData; @@ -821,16 +863,58 @@ public: return kResultFalse; } + #if JUCE_VST3_CAN_REPLACE_VST2 + static tresult writeVST2Int (IBStream* state, int n) + { + juce::int32 t = (juce::int32) htonl (n); + return state->write (&t, 4); + } + + static tresult writeVST2Header (IBStream* state) + { + tresult status = writeVST2Int (state, 'VstW'); + + if (status == kResultOk) status = writeVST2Int (state, 8); // header size + if (status == kResultOk) status = writeVST2Int (state, 1); // version + if (status == kResultOk) status = writeVST2Int (state, 0); // bypass + + return status; + } + #endif + tresult PLUGIN_API getState (IBStream* state) override { - if (state != nullptr) - { - juce::MemoryBlock mem; - pluginInstance->getStateInformation (mem); - return state->write (mem.getData(), (Steinberg::int32) mem.getSize()); - } + if (state == nullptr) + return kInvalidArgument; - return kInvalidArgument; + juce::MemoryBlock mem; + pluginInstance->getStateInformation (mem); + + #if JUCE_VST3_CAN_REPLACE_VST2 + tresult status = writeVST2Header (state); + + if (status != kResultOk) + return status; + + const int bankBlockSize = 160; + struct fxBank bank; + + zerostruct (bank); + bank.chunkMagic = htonl (cMagic); + bank.byteSize = htonl (bankBlockSize - 8 + mem.getSize()); + bank.fxMagic = htonl (chunkBankMagic); + bank.version = htonl (2); + bank.fxID = htonl (JucePlugin_VSTUniqueID); + bank.fxVersion = htonl (JucePlugin_VersionCode); + bank.content.data.size = htonl (mem.getSize()); + + status = state->write (&bank, bankBlockSize); + + if (status != kResultOk) + return status; + #endif + + return state->write (mem.getData(), (Steinberg::int32) mem.getSize()); } //============================================================================== @@ -1241,11 +1325,50 @@ private: DECLARE_CLASS_IID (JuceAudioProcessor, 0x0101ABAB, 0xABCDEF01, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) DEF_CLASS_IID (JuceAudioProcessor) -DECLARE_CLASS_IID (JuceVST3Component, 0xABCDEF01, 0x9182FAEB, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) -DEF_CLASS_IID (JuceVST3Component) +#if JUCE_VST3_CAN_REPLACE_VST2 + // NB: Nasty old-fashioned code in here because it's copied from the Steinberg example code. + static FUID getFUIDForVST2ID (bool forControllerUID) + { + char uidString[33]; + + const int vstfxid = (('V' << 16) | ('S' << 8) | (forControllerUID ? 'E' : 'T')); + char vstfxidStr[7] = { 0 }; + sprintf (vstfxidStr, "%06X", vstfxid); + strcpy (uidString, vstfxidStr); + + char uidStr[9] = { 0 }; + sprintf (uidStr, "%08X", JucePlugin_VSTUniqueID); + strcat (uidString, uidStr); + + char nameidStr[3] = { 0 }; + const size_t len = strlen (JucePlugin_Name); + + for (size_t i = 0; i <= 8; ++i) + { + juce::uint8 c = i < len ? JucePlugin_Name[i] : 0; + + if (c >= 'A' && c <= 'Z') + c += 'a' - 'A'; -DECLARE_CLASS_IID (JuceVST3EditController, 0xABCDEF01, 0x1234ABCD, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) -DEF_CLASS_IID (JuceVST3EditController) + sprintf (nameidStr, "%02X", c); + strcat (uidString, nameidStr); + } + + FUID newOne; + newOne.fromString (uidString); + return newOne; + } + + 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 JUCE_MSVC #pragma warning (pop)