| @@ -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 | |||
| ============= | |||
| @@ -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; | |||
| } | |||
| @@ -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) | |||
| @@ -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; | |||
| @@ -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>& | |||
| PluginDescription desc; | |||
| desc.fileOrIdentifier = fileOrIdentifier; | |||
| desc.uid = 0; | |||
| desc.uniqueId = desc.deprecatedUid = 0; | |||
| if (MessageManager::getInstance()->isThisTheMessageThread() | |||
| && requiresUnblockedMessageThreadDuringCreation (desc)) | |||
| @@ -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<PluginDescription>& 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<LADSPAPluginInstance*> (createdInstance.get()); | |||
| @@ -600,7 +600,7 @@ void LADSPAPluginFormat::findAllTypesForFile (OwnedArray<PluginDescription>& 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)); | |||
| @@ -79,18 +79,18 @@ static int warnOnFailureIfImplemented (int result) noexcept | |||
| #endif | |||
| //============================================================================== | |||
| static int getHashForTUID (const TUID& tuid) noexcept | |||
| std::array<uint32, 4> 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 <typename Range> | |||
| 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; | |||
| @@ -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<PluginDescription>& 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<PluginDescription>& 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<PluginDescription>& 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)); | |||
| @@ -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 | |||
| @@ -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) | |||
| @@ -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<XmlElement> 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<XmlElement> 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; | |||
| } | |||
| @@ -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; | |||