Browse Source

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.
v6.1.6
reuk 4 years ago
parent
commit
f35c2d90e2
No known key found for this signature in database GPG Key ID: 9ADCD339CFC98A11
3 changed files with 61 additions and 60 deletions
  1. +4
    -4
      modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
  2. +15
    -4
      modules/juce_audio_processors/format_types/juce_VST3Common.h
  3. +42
    -52
      modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp

+ 4
- 4
modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp View File

@@ -437,7 +437,7 @@ public:
Vst::ParamID getProgramParamID() const noexcept { return programParamID; } Vst::ParamID getProgramParamID() const noexcept { return programParamID; }
bool isBypassRegularParameter() const noexcept { return bypassIsRegularParameter; } bool isBypassRegularParameter() const noexcept { return bypassIsRegularParameter; }
void setParameterValue (size_t paramIndex, float value)
void setParameterValue (Steinberg::int32 paramIndex, float value)
{ {
cachedParamValues.set (paramIndex, value); cachedParamValues.set (paramIndex, value);
} }
@@ -445,7 +445,7 @@ public:
template <typename Callback> template <typename Callback>
void forAllChangedParameters (Callback&& callback) void forAllChangedParameters (Callback&& callback)
{ {
cachedParamValues.ifSet ([&] (size_t index, float value)
cachedParamValues.ifSet ([&] (Steinberg::int32 index, float value)
{ {
callback (cachedParamValues.getParamID (index), value); callback (cachedParamValues.getParamID (index), value);
}); });
@@ -1176,7 +1176,7 @@ public:
endEdit (vstParamId); endEdit (vstParamId);
} }
void paramChanged (int parameterIndex, Vst::ParamID vstParamId, double newValue)
void paramChanged (Steinberg::int32 parameterIndex, Vst::ParamID vstParamId, double newValue)
{ {
if (inParameterChangedCallback.get()) if (inParameterChangedCallback.get())
return; return;
@@ -1189,7 +1189,7 @@ public:
} }
else else
{ {
audioProcessor->setParameterValue ((size_t) parameterIndex, (float) newValue);
audioProcessor->setParameterValue (parameterIndex, (float) newValue);
} }
} }


+ 15
- 4
modules/juce_audio_processors/format_types/juce_VST3Common.h View File

@@ -991,6 +991,11 @@ private:
We must iterate all parameters on each processBlock call to check whether any 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 parameter value has changed. This class attempts to make this polling process
as quick as possible. 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 class CachedParamValues
{ {
@@ -1002,14 +1007,20 @@ public:
size_t size() const noexcept { return floatCache.size(); } 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 <typename Callback> template <typename Callback>
void ifSet (Callback&& callback) { floatCache.ifSet (std::forward<Callback> (callback)); }
void ifSet (Callback&& callback)
{
floatCache.ifSet ([&] (size_t index, float value)
{
callback ((Steinberg::int32) index, value);
});
}
private: private:
std::vector<Steinberg::Vst::ParamID> paramIds; std::vector<Steinberg::Vst::ParamID> paramIds;


+ 42
- 52
modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp View File

@@ -103,7 +103,7 @@ class EditControllerParameterDispatcher : private Timer
public: public:
~EditControllerParameterDispatcher() override { stopTimer(); } ~EditControllerParameterDispatcher() override { stopTimer(); }
void push (size_t index, float value)
void push (Steinberg::int32 index, float value)
{ {
if (controller == nullptr) if (controller == nullptr)
return; return;
@@ -123,7 +123,7 @@ public:
void flush() void flush()
{ {
cache.ifSet ([this] (size_t index, float value)
cache.ifSet ([this] (Steinberg::int32 index, float value)
{ {
controller->setParamNormalized (cache.getParamID (index), value); controller->setParamNormalized (cache.getParamID (index), value);
}); });
@@ -1839,7 +1839,7 @@ struct VST3ComponentHolder
class ParamValueQueue : public Vst::IParamValueQueue class ParamValueQueue : public Vst::IParamValueQueue
{ {
public: public:
ParamValueQueue (Vst::ParamID idIn, size_t parameterIndexIn)
ParamValueQueue (Vst::ParamID idIn, Steinberg::int32 parameterIndexIn)
: paramId (idIn), parameterIndex (parameterIndexIn) {} : paramId (idIn), parameterIndex (parameterIndexIn) {}
virtual ~ParamValueQueue() = default; virtual ~ParamValueQueue() = default;
@@ -1849,7 +1849,7 @@ public:
Vst::ParamID PLUGIN_API getParameterId() override { return paramId; } 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; } Steinberg::int32 PLUGIN_API getPointCount() override { return size; }
@@ -1892,7 +1892,7 @@ public:
private: private:
const Vst::ParamID paramId; const Vst::ParamID paramId;
const size_t parameterIndex;
const Steinberg::int32 parameterIndex;
float cachedValue; float cachedValue;
Steinberg::int32 size = 0; Steinberg::int32 size = 0;
Atomic<int> refCount; Atomic<int> refCount;
@@ -1981,10 +1981,10 @@ public:
void initialise (const std::vector<Vst::ParamID>& idsIn) void initialise (const std::vector<Vst::ParamID>& idsIn)
{ {
size_t index = 0;
Steinberg::int32 index = 0;
for (const auto& id : idsIn) for (const auto& id : idsIn)
map.emplace (id, Entry { std::make_unique<ParamValueQueue> (id, index++) });
map.emplace (id, Entry { std::make_unique<ParamValueQueue> (id, Steinberg::int32 { index++ }) });
queues.reserve (map.size()); queues.reserve (map.size());
queues.clear(); queues.clear();
@@ -2014,7 +2014,7 @@ public:
struct VST3Parameter final : public Parameter struct VST3Parameter final : public Parameter
{ {
VST3Parameter (VST3PluginInstance& parent, VST3Parameter (VST3PluginInstance& parent,
int vstParameterIndex,
Steinberg::int32 vstParameterIndex,
Steinberg::Vst::ParamID parameterID, Steinberg::Vst::ParamID parameterID,
bool parameterIsAutomatable) bool parameterIsAutomatable)
: pluginInstance (parent), : pluginInstance (parent),
@@ -2026,15 +2026,15 @@ public:
float getValue() const override 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. /* The 'normal' setValue call, which will update both the processor and editor.
*/ */
void setValue (float newValue) override 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 /* 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) void setValueFromEditor (float newValue)
{ {
pluginInstance.cachedParamValues.set ((size_t) vstParamIndex, newValue);
pluginInstance.cachedParamValues.set (vstParamIndex, newValue);
sendValueChangedMessageToListeners (newValue); sendValueChangedMessageToListeners (newValue);
} }
@@ -2122,7 +2122,7 @@ public:
} }
VST3PluginInstance& pluginInstance; VST3PluginInstance& pluginInstance;
const int vstParamIndex;
const Steinberg::int32 vstParamIndex;
const Steinberg::Vst::ParamID paramID; const Steinberg::Vst::ParamID paramID;
const bool automatable; const bool automatable;
const bool discrete = getNumSteps() != AudioProcessor::getDefaultNumParameterSteps(); 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<VST3Parameter*> (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, buffer);
associateWith (data, midiMessages); associateWith (data, midiMessages);
cachedParamValues.ifSet ([&] (size_t index, float value)
cachedParamValues.ifSet ([&] (Steinberg::int32 index, float value)
{ {
inputParameterChanges->set (cachedParamValues.getParamID (index), value); inputParameterChanges->set (cachedParamValues.getParamID (index), value);
}); });
processor->process (data); processor->process (data);
outputParameterChanges->forEach ([&] (size_t index, float value)
outputParameterChanges->forEach ([&] (Steinberg::int32 index, float value)
{ {
parameterDispatcher.push (index, value); parameterDispatcher.push (index, value);
}); });
@@ -2886,38 +2894,7 @@ private:
StringArray programNames; StringArray programNames;
Vst::ParamID programParameterID = (Vst::ParamID) -1; Vst::ParamID programParameterID = (Vst::ParamID) -1;
std::map<Vst::ParamID, int> 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<Vst::ParamID, VST3Parameter*> idToParamMap;
EditControllerParameterDispatcher parameterDispatcher; EditControllerParameterDispatcher parameterDispatcher;
//============================================================================== //==============================================================================
@@ -3045,6 +3022,19 @@ private:
} }
setParameterTree (std::move (newParameterTree)); setParameterTree (std::move (newParameterTree));
idToParamMap = [this]
{
std::map<Vst::ParamID, VST3Parameter*> result;
for (auto* parameter : getParameters())
{
auto* vst3Param = static_cast<VST3Parameter*> (parameter);
result.emplace (vst3Param->getParamID(), vst3Param);
}
return result;
}();
} }
void synchroniseStates() void synchroniseStates()
@@ -3216,12 +3206,12 @@ private:
destination.processContext = &timingInfo; destination.processContext = &timingInfo;
} }
Vst::ParameterInfo getParameterInfoForIndex (int index) const
Vst::ParameterInfo getParameterInfoForIndex (Steinberg::int32 index) const
{ {
Vst::ParameterInfo paramInfo{}; Vst::ParameterInfo paramInfo{};
if (processor != nullptr) if (processor != nullptr)
editController->getParameterInfo (index, paramInfo);
editController->getParameterInfo ((int32) index, paramInfo);
return paramInfo; return paramInfo;
} }


Loading…
Cancel
Save