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 74cdf48b90..b9efb2d983 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 @@ -643,39 +643,61 @@ public: compatibleParameterIdMap = {}; compatibleParameterIdMap[currentPluginId] = paramMap; - if (const auto* ext = audioProcessor->getVST3ClientExtensions()) + auto* ext = audioProcessor->getVST3ClientExtensions(); + + // If there are no extensions, we assume that no adjustments should be made to the mapping. + if (ext == nullptr) + return; + + for (const auto& compatibleClass : getAllCompatibleClasses (*audioProcessor)) { - for (auto& compatibleClass : ext->getCompatibleClasses()) + auto& parameterIdMap = compatibleParameterIdMap[compatibleClass]; + for (auto [oldParamId, newParamId] : ext->getCompatibleParameterIds (compatibleClass)) { - auto& parameterIdMap = compatibleParameterIdMap[compatibleClass]; + auto* parameter = getParameter (newParamId); + parameterIdMap[oldParamId] = parameter; - for (auto [oldParamId, newParamId] : ext->getCompatibleParameterIds (compatibleClass)) - { - auto* parameter = getParameter (newParamId); - parameterIdMap[oldParamId] = parameter; + // This means a parameter ID returned by getCompatibleParameterIds() + // does not match any parameters declared in the plugin. All IDs must + // match an existing parameter, or return an empty string to indicate + // there is no parameter to map to. + jassert (parameter != nullptr || newParamId.isEmpty()); - // This means a parameter ID returned by getCompatibleParameterIds() - // does not match any parameters declared in the plugin. All IDs must - // match an existing parameter, or return an empty string to indicate - // there is no parameter to map to. - jassert (parameter != nullptr || newParamId.isEmpty()); - - // This means getCompatibleParameterIds() returned a parameter mapping - // that will hide a parameter in the current plugin! If this is due to - // an ID collision between plugin versions, you may be able to determine - // the mapping to report based on setStateInformation(). If you've - // already done this you can safely ignore this warning. If there is no - // way to determine the difference between the two plugin versions in - // setStateInformation() the best course of action is to remove the - // problematic parameter from the mapping. - jassert (compatibleClass != currentPluginId - || getParamForVSTParamID (oldParamId) == nullptr - || parameter == getParamForVSTParamID (oldParamId)); - } + // This means getCompatibleParameterIds() returned a parameter mapping + // that will hide a parameter in the current plugin! If this is due to + // an ID collision between plugin versions, you may be able to determine + // the mapping to report based on setStateInformation(). If you've + // already done this you can safely ignore this warning. If there is no + // way to determine the difference between the two plugin versions in + // setStateInformation() the best course of action is to remove the + // problematic parameter from the mapping. + jassert (compatibleClass != currentPluginId + || getParamForVSTParamID (oldParamId) == nullptr + || parameter == getParamForVSTParamID (oldParamId)); } } } + /* Includes all compatible classes declared in the client extensions, along with + our own ID in the case that JUCE_VST3_CAN_REPLACE_VST2 is set. + */ + static std::vector getAllCompatibleClasses (AudioProcessor& processor) + { + auto result = std::invoke ([&] + { + if (auto* ext = processor.getVST3ClientExtensions()) + return ext->getCompatibleClasses(); + + return std::vector{}; + }); + + #if JUCE_VST3_CAN_REPLACE_VST2 + result.push_back (getInterfaceId (VST3InterfaceType::component)); + #endif + + return result; + } + //============================================================================== inline static const FUID iid = toSteinbergUID (getInterfaceId (VST3InterfaceType::processor)); @@ -1160,8 +1182,18 @@ public: } const auto* parameter = iter->second; - newParamID = parameter != nullptr ? audioProcessor->getVSTParamIDForIndex (parameter->getParameterIndex()) - : 0xffffffff; + + if (parameter == nullptr) + { + // There's a null entry in the map of compatible parameters. + // This implies a problem with the implementation of getCompatibleParameterIds - one of + // the IDs in the returned map doesn't refer to any parameter in the current plugin. + jassertfalse; + return kResultFalse; + } + + // We found a compatible parameter in the map. + newParamID = audioProcessor->getVSTParamIDForIndex (parameter->getParameterIndex()); return kResultTrue; } @@ -1221,6 +1253,8 @@ public: // As an IEditController member, the host should only call this from the message thread. assertHostMessageThread(); + auto restartFlags = toUnderlyingType (Vst::kParamValuesChanged); + if (audioProcessor != nullptr) { auto* pluginInstance = getPluginInstance(); @@ -1239,11 +1273,15 @@ public: setParamNormalized (vstParamId, paramValue); } - audioProcessor->updateParameterMapping(); + if (! JuceAudioProcessor::getAllCompatibleClasses (*pluginInstance).empty()) + { + restartFlags |= Vst::kParamIDMappingChanged; + audioProcessor->updateParameterMapping(); + } } if (auto* handler = getComponentHandler()) - handler->restartComponent (Vst::kParamValuesChanged | Vst::kParamIDMappingChanged); + handler->restartComponent (restartFlags); return kResultOk; } @@ -2947,14 +2985,7 @@ public: //============================================================================== bool shouldTryToLoadVst2State() { - #if JUCE_VST3_CAN_REPLACE_VST2 - return true; - #else - if (auto extensions = pluginInstance->getVST3ClientExtensions()) - return ! extensions->getCompatibleClasses().empty(); - - return false; - #endif + return ! JuceAudioProcessor::getAllCompatibleClasses (*pluginInstance).empty(); } bool shouldWriteStateWithVst2Compatibility() @@ -4120,29 +4151,30 @@ public: const ScopedJuceInitialiser_GUI libraryInitialiser; auto filter = createPluginFilterOfType (AudioProcessor::WrapperType::wrapperType_VST3); - auto* extensions = filter->getVST3ClientExtensions(); - const auto compatibilityObjects = [&] + const auto compatibilityObjects = std::invoke ([&] { - if (extensions == nullptr || extensions->getCompatibleClasses().empty()) + const auto compatibleClasses = JuceAudioProcessor::getAllCompatibleClasses (*filter); + + if (compatibleClasses.empty()) return Array(); 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", [&] + object->setProperty ("Old", std::invoke ([&] { Array oldArray; - for (const auto& uid : extensions->getCompatibleClasses()) + for (const auto& uid : compatibleClasses) oldArray.add (String::toHexString (uid.data(), (int) uid.size(), 0)); return oldArray; - }()); + })); return Array { object.get() }; - }(); + }); MemoryOutputStream memory; JSON::writeToStream (memory, var { compatibilityObjects });