From f35c2d90e29638ab0b9bd8664d7417a26c23a96c Mon Sep 17 00:00:00 2001 From: reuk Date: Thu, 6 May 2021 15:08:59 +0100 Subject: [PATCH] VST3 Host: Ensure AudioProcessor parameter indices are used when appropriate Previously, IEditController parameter indices were being used to index into the AudioProcessor parameter array, but these parameter indices are not guaranteed to point to the same parameter (parameter groups may cause reordering on JUCE's side). Now, we use the IEditController indices universally. --- .../VST3/juce_VST3_Wrapper.cpp | 8 +- .../format_types/juce_VST3Common.h | 19 +++- .../format_types/juce_VST3PluginFormat.cpp | 94 +++++++++---------- 3 files changed, 61 insertions(+), 60 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 ad464e3625..7049f60b2e 100644 --- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -437,7 +437,7 @@ public: Vst::ParamID getProgramParamID() const noexcept { return programParamID; } bool isBypassRegularParameter() const noexcept { return bypassIsRegularParameter; } - void setParameterValue (size_t paramIndex, float value) + void setParameterValue (Steinberg::int32 paramIndex, float value) { cachedParamValues.set (paramIndex, value); } @@ -445,7 +445,7 @@ public: template void forAllChangedParameters (Callback&& callback) { - cachedParamValues.ifSet ([&] (size_t index, float value) + cachedParamValues.ifSet ([&] (Steinberg::int32 index, float value) { callback (cachedParamValues.getParamID (index), value); }); @@ -1176,7 +1176,7 @@ public: endEdit (vstParamId); } - void paramChanged (int parameterIndex, Vst::ParamID vstParamId, double newValue) + void paramChanged (Steinberg::int32 parameterIndex, Vst::ParamID vstParamId, double newValue) { if (inParameterChangedCallback.get()) return; @@ -1189,7 +1189,7 @@ public: } else { - audioProcessor->setParameterValue ((size_t) parameterIndex, (float) newValue); + audioProcessor->setParameterValue (parameterIndex, (float) newValue); } } diff --git a/modules/juce_audio_processors/format_types/juce_VST3Common.h b/modules/juce_audio_processors/format_types/juce_VST3Common.h index 8ef0fe3e43..99355fdc6f 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3Common.h +++ b/modules/juce_audio_processors/format_types/juce_VST3Common.h @@ -991,6 +991,11 @@ private: We must iterate all parameters on each processBlock call to check whether any parameter value has changed. This class attempts to make this polling process as quick as possible. + + The indices here are of type Steinberg::int32, as they are expected to correspond + to parameter information obtained from the IEditController. These indices may not + match the indices of parameters returned from AudioProcessor::getParameters(), so + be careful! */ class CachedParamValues { @@ -1002,14 +1007,20 @@ public: size_t size() const noexcept { return floatCache.size(); } - Steinberg::Vst::ParamID getParamID (size_t index) const noexcept { return paramIds[index]; } + Steinberg::Vst::ParamID getParamID (Steinberg::int32 index) const noexcept { return paramIds[(size_t) index]; } - void set (size_t index, float value) { floatCache.set (index, value); } + void set (Steinberg::int32 index, float value) { floatCache.set ((size_t) index, value); } - float get (size_t index) const noexcept { return floatCache.get (index); } + float get (Steinberg::int32 index) const noexcept { return floatCache.get ((size_t) index); } template - void ifSet (Callback&& callback) { floatCache.ifSet (std::forward (callback)); } + void ifSet (Callback&& callback) + { + floatCache.ifSet ([&] (size_t index, float value) + { + callback ((Steinberg::int32) index, value); + }); + } private: std::vector paramIds; diff --git a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index 85c7caa89c..e60e327f22 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -103,7 +103,7 @@ class EditControllerParameterDispatcher : private Timer public: ~EditControllerParameterDispatcher() override { stopTimer(); } - void push (size_t index, float value) + void push (Steinberg::int32 index, float value) { if (controller == nullptr) return; @@ -123,7 +123,7 @@ public: void flush() { - cache.ifSet ([this] (size_t index, float value) + cache.ifSet ([this] (Steinberg::int32 index, float value) { controller->setParamNormalized (cache.getParamID (index), value); }); @@ -1839,7 +1839,7 @@ struct VST3ComponentHolder class ParamValueQueue : public Vst::IParamValueQueue { public: - ParamValueQueue (Vst::ParamID idIn, size_t parameterIndexIn) + ParamValueQueue (Vst::ParamID idIn, Steinberg::int32 parameterIndexIn) : paramId (idIn), parameterIndex (parameterIndexIn) {} virtual ~ParamValueQueue() = default; @@ -1849,7 +1849,7 @@ public: Vst::ParamID PLUGIN_API getParameterId() override { return paramId; } - size_t getParameterIndex() const noexcept { return parameterIndex; } + Steinberg::int32 getParameterIndex() const noexcept { return parameterIndex; } Steinberg::int32 PLUGIN_API getPointCount() override { return size; } @@ -1892,7 +1892,7 @@ public: private: const Vst::ParamID paramId; - const size_t parameterIndex; + const Steinberg::int32 parameterIndex; float cachedValue; Steinberg::int32 size = 0; Atomic refCount; @@ -1981,10 +1981,10 @@ public: void initialise (const std::vector& idsIn) { - size_t index = 0; + Steinberg::int32 index = 0; for (const auto& id : idsIn) - map.emplace (id, Entry { std::make_unique (id, index++) }); + map.emplace (id, Entry { std::make_unique (id, Steinberg::int32 { index++ }) }); queues.reserve (map.size()); queues.clear(); @@ -2014,7 +2014,7 @@ public: struct VST3Parameter final : public Parameter { VST3Parameter (VST3PluginInstance& parent, - int vstParameterIndex, + Steinberg::int32 vstParameterIndex, Steinberg::Vst::ParamID parameterID, bool parameterIsAutomatable) : pluginInstance (parent), @@ -2026,15 +2026,15 @@ public: float getValue() const override { - return pluginInstance.cachedParamValues.get ((size_t) vstParamIndex); + return pluginInstance.cachedParamValues.get (vstParamIndex); } /* The 'normal' setValue call, which will update both the processor and editor. */ void setValue (float newValue) override { - pluginInstance.cachedParamValues.set ((size_t) vstParamIndex, newValue); - pluginInstance.parameterDispatcher.push ((size_t) vstParamIndex, newValue); + pluginInstance.cachedParamValues.set (vstParamIndex, newValue); + pluginInstance.parameterDispatcher.push (vstParamIndex, newValue); } /* If the editor set the value, there's no need to notify it that the parameter @@ -2044,7 +2044,7 @@ public: */ void setValueFromEditor (float newValue) { - pluginInstance.cachedParamValues.set ((size_t) vstParamIndex, newValue); + pluginInstance.cachedParamValues.set (vstParamIndex, newValue); sendValueChangedMessageToListeners (newValue); } @@ -2122,7 +2122,7 @@ public: } VST3PluginInstance& pluginInstance; - const int vstParamIndex; + const Steinberg::int32 vstParamIndex; const Steinberg::Vst::ParamID paramID; const bool automatable; const bool discrete = getNumSteps() != AudioProcessor::getDefaultNumParameterSteps(); @@ -2373,10 +2373,18 @@ public: } //============================================================================== - VST3Parameter* getParameterForID (Vst::ParamID paramID) + /* Important: It is strongly recommended to use this function if you need to + find the JUCE parameter corresponding to a particular IEditController + parameter. + + Note that a parameter at a given index in the IEditController does not + necessarily correspond to the parameter at the same index in + AudioProcessor::getParameters(). + */ + VST3Parameter* getParameterForID (Vst::ParamID paramID) const { - const auto index = getIndexOfParamID (paramID); - return index < 0 ? nullptr : static_cast (getParameters()[index]); + const auto it = idToParamMap.find (paramID); + return it != idToParamMap.end() ? it->second : nullptr; } //============================================================================== @@ -2459,14 +2467,14 @@ public: associateWith (data, buffer); associateWith (data, midiMessages); - cachedParamValues.ifSet ([&] (size_t index, float value) + cachedParamValues.ifSet ([&] (Steinberg::int32 index, float value) { inputParameterChanges->set (cachedParamValues.getParamID (index), value); }); processor->process (data); - outputParameterChanges->forEach ([&] (size_t index, float value) + outputParameterChanges->forEach ([&] (Steinberg::int32 index, float value) { parameterDispatcher.push (index, value); }); @@ -2886,38 +2894,7 @@ private: StringArray programNames; Vst::ParamID programParameterID = (Vst::ParamID) -1; - std::map paramToIndexMap; - - int getMappedParamID (Vst::ParamID paramID) const - { - auto it = paramToIndexMap.find (paramID); - return it != paramToIndexMap.end() ? it->second : -1; - } - - int getIndexOfParamID (Vst::ParamID paramID) - { - if (editController == nullptr) - return -1; - - auto result = getMappedParamID (paramID); - - if (result < 0) - { - auto numParams = editController->getParameterCount(); - - for (int i = 0; i < numParams; ++i) - { - Vst::ParameterInfo paramInfo; - editController->getParameterInfo (i, paramInfo); - paramToIndexMap[paramInfo.id] = i; - } - - result = getMappedParamID (paramID); - } - - return result; - } - + std::map idToParamMap; EditControllerParameterDispatcher parameterDispatcher; //============================================================================== @@ -3045,6 +3022,19 @@ private: } setParameterTree (std::move (newParameterTree)); + + idToParamMap = [this] + { + std::map result; + + for (auto* parameter : getParameters()) + { + auto* vst3Param = static_cast (parameter); + result.emplace (vst3Param->getParamID(), vst3Param); + } + + return result; + }(); } void synchroniseStates() @@ -3216,12 +3206,12 @@ private: destination.processContext = &timingInfo; } - Vst::ParameterInfo getParameterInfoForIndex (int index) const + Vst::ParameterInfo getParameterInfoForIndex (Steinberg::int32 index) const { Vst::ParameterInfo paramInfo{}; if (processor != nullptr) - editController->getParameterInfo (index, paramInfo); + editController->getParameterInfo ((int32) index, paramInfo); return paramInfo; }