From 041da08474120b18a287dc5fcee4eba82b6dc8e3 Mon Sep 17 00:00:00 2001 From: reuk Date: Tue, 30 Mar 2021 18:32:33 +0100 Subject: [PATCH] VST3: Add a new PluginDescription::uniqueId field --- BREAKING-CHANGES.txt | 28 +++++++++++++++++++ .../Source/Plugins/InternalPlugins.cpp | 23 +++++++-------- extras/Build/CMake/JUCEUtils.cmake | 3 +- .../StartPage/jucer_NewProjectWizard.cpp | 1 - .../juce_AudioUnitPluginFormat.mm | 8 +++--- .../format_types/juce_LADSPAPluginFormat.cpp | 8 +++--- .../format_types/juce_VST3PluginFormat.cpp | 23 ++++++++------- .../format_types/juce_VSTPluginFormat.cpp | 11 ++++---- .../juce_audio_processors.h | 9 ------ .../processors/juce_AudioProcessorGraph.cpp | 3 +- .../processors/juce_PluginDescription.cpp | 18 ++++++++---- .../processors/juce_PluginDescription.h | 26 +++++++++++++++-- 12 files changed, 106 insertions(+), 55 deletions(-) diff --git a/BREAKING-CHANGES.txt b/BREAKING-CHANGES.txt index d0e6f2d570..2268fe9e9e 100644 --- a/BREAKING-CHANGES.txt +++ b/BREAKING-CHANGES.txt @@ -1,6 +1,34 @@ JUCE breaking changes ===================== +Develop +======= + +Change +------ +PluginDescription::uid has been deprecated and replaced with a new 'uniqueId' +data member. + +Possible Issues +--------------- +Code using the old data member will need to be updated in order to compile. + +Workaround +---------- +Code that used to use 'uid' to identify plugins should switch to using +'uniqueId', with some caveats - see "Rationale" for details. + +Rationale +--------- +The 'uniqueId' member has the benefit of being consistent for +a given VST3 across Windows, macOS, and Linux. However, the value of the +uniqueId may differ from the value of the old uid on some platforms. The value +of the old 'uid' member can now be found in the 'deprecatedUid' member, which +should allow clients to implement logic such as checking a saved uid against +the new uniqueId, and falling back to the deprecatedUid. This should allow +hosts to gracefully upgrade from the old uid values to the new values. + + Version 6.0.8 ============= diff --git a/extras/AudioPluginHost/Source/Plugins/InternalPlugins.cpp b/extras/AudioPluginHost/Source/Plugins/InternalPlugins.cpp index 4f79b8921b..83ad8ca121 100644 --- a/extras/AudioPluginHost/Source/Plugins/InternalPlugins.cpp +++ b/extras/AudioPluginHost/Source/Plugins/InternalPlugins.cpp @@ -113,17 +113,18 @@ private: PluginDescription descr; - descr.name = identifier; - descr.descriptiveName = identifier; - descr.pluginFormatName = InternalPluginFormat::getIdentifier(); - descr.category = (registerAsGenerator ? (acceptsMidi ? "Synth" : "Generator") : "Effect"); - descr.manufacturerName = "JUCE"; - descr.version = ProjectInfo::versionString; - descr.fileOrIdentifier = identifier; - descr.uid = identifier.hashCode(); - descr.isInstrument = (acceptsMidi && registerAsGenerator); - descr.numInputChannels = ins; - descr.numOutputChannels = outs; + descr.name = identifier; + descr.descriptiveName = identifier; + descr.pluginFormatName = InternalPluginFormat::getIdentifier(); + descr.category = (registerAsGenerator ? (acceptsMidi ? "Synth" : "Generator") : "Effect"); + descr.manufacturerName = "JUCE"; + descr.version = ProjectInfo::versionString; + descr.fileOrIdentifier = identifier; + descr.isInstrument = (acceptsMidi && registerAsGenerator); + descr.numInputChannels = ins; + descr.numOutputChannels = outs; + + descr.uniqueId = descr.deprecatedUid = identifier.hashCode(); return descr; } diff --git a/extras/Build/CMake/JUCEUtils.cmake b/extras/Build/CMake/JUCEUtils.cmake index f6138927e5..673dddb239 100644 --- a/extras/Build/CMake/JUCEUtils.cmake +++ b/extras/Build/CMake/JUCEUtils.cmake @@ -2274,8 +2274,7 @@ function(juce_add_pip header) target_compile_definitions(${JUCE_PIP_NAME} PRIVATE ${pip_moduleflags} PUBLIC - JUCE_VST3_CAN_REPLACE_VST2=0 - JUCE_VST3_HOST_CROSS_PLATFORM_UID=1) + JUCE_VST3_CAN_REPLACE_VST2=0) _juce_get_pip_targets(${JUCE_PIP_NAME} pip_targets) diff --git a/extras/Projucer/Source/Application/StartPage/jucer_NewProjectWizard.cpp b/extras/Projucer/Source/Application/StartPage/jucer_NewProjectWizard.cpp index b886637c3b..e611966779 100644 --- a/extras/Projucer/Source/Application/StartPage/jucer_NewProjectWizard.cpp +++ b/extras/Projucer/Source/Application/StartPage/jucer_NewProjectWizard.cpp @@ -63,7 +63,6 @@ static void doBasicProjectSetup (Project& project, const NewProjectTemplates::Pr project.getMainGroup().addNewSubGroup ("Source", 0); project.getConfigFlag ("JUCE_STRICT_REFCOUNTEDPOINTER") = true; - project.getConfigFlag ("JUCE_VST3_HOST_CROSS_PLATFORM_UID") = true; project.getProjectValue (Ids::useAppConfig) = false; project.getProjectValue (Ids::addUsingNamespaceToJuceHeader) = false; diff --git a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm index f303b3b82e..ff8e4e7ccb 100644 --- a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm +++ b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm @@ -846,9 +846,9 @@ public: desc.name = pluginName; desc.descriptiveName = pluginName; desc.fileOrIdentifier = AudioUnitFormatHelpers::createPluginIdentifier (componentDesc); - desc.uid = ((int) componentDesc.componentType) - ^ ((int) componentDesc.componentSubType) - ^ ((int) componentDesc.componentManufacturer); + desc.uniqueId = desc.deprecatedUid = ((int) componentDesc.componentType) + ^ ((int) componentDesc.componentSubType) + ^ ((int) componentDesc.componentManufacturer); desc.lastFileModTime = Time(); desc.lastInfoUpdateTime = Time::getCurrentTime(); desc.pluginFormatName = "AudioUnit"; @@ -2634,7 +2634,7 @@ void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray& PluginDescription desc; desc.fileOrIdentifier = fileOrIdentifier; - desc.uid = 0; + desc.uniqueId = desc.deprecatedUid = 0; if (MessageManager::getInstance()->isThisTheMessageThread() && requiresUnblockedMessageThreadDuringCreation (desc)) diff --git a/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp index dd95cafbcb..117685893b 100644 --- a/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp @@ -220,7 +220,7 @@ public: { desc.name = getName(); desc.fileOrIdentifier = module->file.getFullPathName(); - desc.uid = getUID(); + desc.uniqueId = desc.deprecatedUid = getUID(); desc.lastFileModTime = module->file.getLastModificationTime(); desc.lastInfoUpdateTime = Time::getCurrentTime(); desc.pluginFormatName = "LADSPA"; @@ -583,7 +583,7 @@ void LADSPAPluginFormat::findAllTypesForFile (OwnedArray& res PluginDescription desc; desc.fileOrIdentifier = fileOrIdentifier; - desc.uid = 0; + desc.uniqueId = desc.deprecatedUid = 0; auto createdInstance = createInstanceFromDescription (desc, 44100.0, 512); auto instance = dynamic_cast (createdInstance.get()); @@ -600,7 +600,7 @@ void LADSPAPluginFormat::findAllTypesForFile (OwnedArray& res { if (auto* plugin = instance->module->moduleMain ((size_t) uid)) { - desc.uid = uid; + desc.uniqueId = desc.deprecatedUid = uid; desc.name = plugin->Name != nullptr ? plugin->Name : "Unknown"; if (! arrayContainsPlugin (results, desc)) @@ -631,7 +631,7 @@ void LADSPAPluginFormat::createPluginInstance (const PluginDescription& desc, if (module != nullptr) { - shellLADSPAUIDToCreate = desc.uid; + shellLADSPAUIDToCreate = desc.uniqueId; result.reset (new LADSPAPluginInstance (module)); diff --git a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index 978f1c9ebd..5a6c27d8b0 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -79,18 +79,18 @@ static int warnOnFailureIfImplemented (int result) noexcept #endif //============================================================================== -static int getHashForTUID (const TUID& tuid) noexcept +std::array getNormalisedTUID (const TUID& tuid) noexcept { - #if JUCE_VST3_HOST_CROSS_PLATFORM_UID const FUID fuid { tuid }; - const uint32 inputArray[] { fuid.getLong1(), fuid.getLong2(), fuid.getLong3(), fuid.getLong4() }; - #else - const auto& inputArray = tuid; - #endif + return { { fuid.getLong1(), fuid.getLong2(), fuid.getLong3(), fuid.getLong4() } }; +} +template +static int getHashForRange (Range&& range) noexcept +{ uint32 value = 0; - for (const auto& item : inputArray) + for (const auto& item : range) value = (value * 31) + (uint32) item; return (int) value; @@ -120,7 +120,9 @@ static void createPluginDescription (PluginDescription& description, description.pluginFormatName = "VST3"; description.numInputChannels = numInputs; description.numOutputChannels = numOutputs; - description.uid = getHashForTUID (info.cid); + + description.deprecatedUid = getHashForRange (info.cid); + description.uniqueId = getHashForRange (getNormalisedTUID (info.cid)); if (infoW != nullptr) fillDescriptionWith (description, *infoW); else if (info2 != nullptr) fillDescriptionWith (description, *info2); @@ -780,7 +782,7 @@ struct DescriptionFactory } } - if (desc.uid != 0) + if (desc.uniqueId != 0) result = performOnDescription (desc); if (result.failed()) @@ -1105,7 +1107,8 @@ private: continue; if (toString (info.name).trim() == description.name - && getHashForTUID (info.cid) == description.uid) + && (getHashForRange (getNormalisedTUID (info.cid)) == description.uniqueId + || getHashForRange (info.cid) == description.deprecatedUid)) { name = description.name; return true; diff --git a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index 5532e0091f..fa0e346b3e 100644 --- a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -1170,7 +1170,7 @@ struct VSTPluginInstance : public AudioPluginInstance, } desc.fileOrIdentifier = vstModule->file.getFullPathName(); - desc.uid = getUID(); + desc.uniqueId = desc.deprecatedUid = getUID(); desc.lastFileModTime = vstModule->file.getLastModificationTime(); desc.lastInfoUpdateTime = Time::getCurrentTime(); desc.pluginFormatName = "VST"; @@ -3470,7 +3470,7 @@ void VSTPluginFormat::findAllTypesForFile (OwnedArray& result PluginDescription desc; desc.fileOrIdentifier = fileOrIdentifier; - desc.uid = 0; + desc.uniqueId = desc.deprecatedUid = 0; auto instance = createAndUpdateDesc (*this, desc); @@ -3495,7 +3495,7 @@ void VSTPluginFormat::findAllTypesForFile (OwnedArray& result if (uid == 0) break; - desc.uid = uid; + desc.uniqueId = desc.deprecatedUid = uid; desc.name = shellEffectName; aboutToScanVSTShellPlugin (desc); @@ -3504,7 +3504,8 @@ void VSTPluginFormat::findAllTypesForFile (OwnedArray& result if (shellInstance != nullptr) { - jassert (desc.uid == uid); + jassert (desc.deprecatedUid == uid); + jassert (desc.uniqueId == uid); desc.hasSharedContainer = true; desc.name = shellEffectName; @@ -3530,7 +3531,7 @@ void VSTPluginFormat::createPluginInstance (const PluginDescription& desc, if (auto module = ModuleHandle::findOrCreateModule (file)) { - shellUIDToCreate = desc.uid; + shellUIDToCreate = desc.uniqueId; result.reset (VSTPluginInstance::create (module, sampleRate, blockSize)); diff --git a/modules/juce_audio_processors/juce_audio_processors.h b/modules/juce_audio_processors/juce_audio_processors.h index 6137be89b4..e543a92fdb 100644 --- a/modules/juce_audio_processors/juce_audio_processors.h +++ b/modules/juce_audio_processors/juce_audio_processors.h @@ -102,15 +102,6 @@ #define JUCE_CUSTOM_VST3_SDK 0 #endif -/** Config: JUCE_VST3_HOST_CROSS_PLATFORM_UID - If enabled, ensures that PluginDescription::uid will produce consistent values for VST3 plugins on all platforms. - It is recommended to enable this flag in all new projects. - Projects which predate this flag should leave it disabled, in case they need to interact with uid values which they previously stored. -*/ -#ifndef JUCE_VST3_HOST_CROSS_PLATFORM_UID - #define JUCE_VST3_HOST_CROSS_PLATFORM_UID 0 -#endif - #if ! (JUCE_PLUGINHOST_AU || JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_VST3 || JUCE_PLUGINHOST_LADSPA) // #error "You need to set either the JUCE_PLUGINHOST_AU and/or JUCE_PLUGINHOST_VST and/or JUCE_PLUGINHOST_VST3 and/or JUCE_PLUGINHOST_LADSPA flags if you're using this module!" #endif diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp b/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp index 17c5af30c0..8b2435de40 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp +++ b/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp @@ -1418,13 +1418,14 @@ const String AudioProcessorGraph::AudioGraphIOProcessor::getName() const void AudioProcessorGraph::AudioGraphIOProcessor::fillInPluginDescription (PluginDescription& d) const { d.name = getName(); - d.uid = d.name.hashCode(); d.category = "I/O devices"; d.pluginFormatName = "Internal"; d.manufacturerName = "JUCE"; d.version = "1.0"; d.isInstrument = false; + d.deprecatedUid = d.uniqueId = d.name.hashCode(); + d.numInputChannels = getTotalNumInputChannels(); if (type == audioOutputNode && graph != nullptr) diff --git a/modules/juce_audio_processors/processors/juce_PluginDescription.cpp b/modules/juce_audio_processors/processors/juce_PluginDescription.cpp index 956f2de2c4..6e61ae7017 100644 --- a/modules/juce_audio_processors/processors/juce_PluginDescription.cpp +++ b/modules/juce_audio_processors/processors/juce_PluginDescription.cpp @@ -28,14 +28,18 @@ namespace juce bool PluginDescription::isDuplicateOf (const PluginDescription& other) const noexcept { - return fileOrIdentifier == other.fileOrIdentifier - && uid == other.uid; + const auto tie = [] (const PluginDescription& d) + { + return std::tie (d.fileOrIdentifier, d.deprecatedUid, d.uniqueId); + }; + + return tie (*this) == tie (other); } static String getPluginDescSuffix (const PluginDescription& d) { return "-" + String::toHexString (d.fileOrIdentifier.hashCode()) - + "-" + String::toHexString (d.uid); + + "-" + String::toHexString (d.uniqueId); } bool PluginDescription::matchesIdentifierString (const String& identifierString) const @@ -62,7 +66,7 @@ std::unique_ptr PluginDescription::createXml() const e->setAttribute ("manufacturer", manufacturerName); e->setAttribute ("version", version); e->setAttribute ("file", fileOrIdentifier); - e->setAttribute ("uid", String::toHexString (uid)); + e->setAttribute ("uniqueId", String::toHexString (uniqueId)); e->setAttribute ("isInstrument", isInstrument); e->setAttribute ("fileTime", String::toHexString (lastFileModTime.toMilliseconds())); e->setAttribute ("infoUpdateTime", String::toHexString (lastInfoUpdateTime.toMilliseconds())); @@ -70,6 +74,8 @@ std::unique_ptr PluginDescription::createXml() const e->setAttribute ("numOutputs", numOutputChannels); e->setAttribute ("isShell", hasSharedContainer); + e->setAttribute ("uid", String::toHexString (deprecatedUid)); + return e; } @@ -84,7 +90,6 @@ bool PluginDescription::loadFromXml (const XmlElement& xml) manufacturerName = xml.getStringAttribute ("manufacturer"); version = xml.getStringAttribute ("version"); fileOrIdentifier = xml.getStringAttribute ("file"); - uid = xml.getStringAttribute ("uid").getHexValue32(); isInstrument = xml.getBoolAttribute ("isInstrument", false); lastFileModTime = Time (xml.getStringAttribute ("fileTime").getHexValue64()); lastInfoUpdateTime = Time (xml.getStringAttribute ("infoUpdateTime").getHexValue64()); @@ -92,6 +97,9 @@ bool PluginDescription::loadFromXml (const XmlElement& xml) numOutputChannels = xml.getIntAttribute ("numOutputs"); hasSharedContainer = xml.getBoolAttribute ("isShell", false); + deprecatedUid = xml.getStringAttribute ("uid").getHexValue32(); + uniqueId = xml.getStringAttribute ("uniqueId", "0").getHexValue32(); + return true; } diff --git a/modules/juce_audio_processors/processors/juce_PluginDescription.h b/modules/juce_audio_processors/processors/juce_PluginDescription.h index bce3a41e6a..c01b73b995 100644 --- a/modules/juce_audio_processors/processors/juce_PluginDescription.h +++ b/modules/juce_audio_processors/processors/juce_PluginDescription.h @@ -44,9 +44,12 @@ class JUCE_API PluginDescription public: //============================================================================== PluginDescription() = default; - PluginDescription (const PluginDescription& other) = default; - PluginDescription& operator= (const PluginDescription& other) = default; + PluginDescription (const PluginDescription&) = default; + PluginDescription (PluginDescription&&) = default; + + PluginDescription& operator= (const PluginDescription&) = default; + PluginDescription& operator= (PluginDescription&&) = default; //============================================================================== /** The name of the plug-in. */ @@ -88,14 +91,31 @@ public: */ Time lastInfoUpdateTime; + /** Deprecated: New projects should use uniqueId instead. + + A unique ID for the plug-in. + + Note that this might not be unique between formats, e.g. a VST and some + other format might actually have the same id. + + @see createIdentifierString + */ + int deprecatedUid = 0; + /** A unique ID for the plug-in. Note that this might not be unique between formats, e.g. a VST and some other format might actually have the same id. + The uniqueId field replaces the deprecatedUid field, and fixes an issue + where VST3 plugins with matching FUIDs would generate different uid + values depending on the platform. The deprecatedUid field is kept for + backwards compatibility, allowing existing hosts to migrate from the + old uid to the new uniqueId. + @see createIdentifierString */ - int uid = 0; + int uniqueId = 0; /** True if the plug-in identifies itself as a synthesiser. */ bool isInstrument = false;