Skip to content

Commit

Permalink
VST3 Host: Ensure AudioProcessor parameter indices are used when appr…
Browse files Browse the repository at this point in the history
…opriate

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.
  • Loading branch information
reuk committed May 6, 2021
1 parent 32f99d2 commit f35c2d9
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 60 deletions.
8 changes: 4 additions & 4 deletions modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
Expand Up @@ -437,15 +437,15 @@ class JuceAudioProcessor : public Vst::IUnitInfo
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);
}

template <typename Callback>
void forAllChangedParameters (Callback&& callback)
{
cachedParamValues.ifSet ([&] (size_t index, float value)
cachedParamValues.ifSet ([&] (Steinberg::int32 index, float value)
{
callback (cachedParamValues.getParamID (index), value);
});
Expand Down Expand Up @@ -1176,7 +1176,7 @@ class JuceVST3EditController : public Vst::EditController,
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;
Expand All @@ -1189,7 +1189,7 @@ class JuceVST3EditController : public Vst::EditController,
}
else
{
audioProcessor->setParameterValue ((size_t) parameterIndex, (float) newValue);
audioProcessor->setParameterValue (parameterIndex, (float) newValue);
}
}

Expand Down
19 changes: 15 additions & 4 deletions modules/juce_audio_processors/format_types/juce_VST3Common.h
Expand Up @@ -991,6 +991,11 @@ class FloatCache
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
{
Expand All @@ -1002,14 +1007,20 @@ class CachedParamValues

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>
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:
std::vector<Steinberg::Vst::ParamID> paramIds;
Expand Down
Expand Up @@ -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;
Expand All @@ -123,7 +123,7 @@ class EditControllerParameterDispatcher : private Timer

void flush()
{
cache.ifSet ([this] (size_t index, float value)
cache.ifSet ([this] (Steinberg::int32 index, float value)
{
controller->setParamNormalized (cache.getParamID (index), value);
});
Expand Down Expand Up @@ -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;
Expand All @@ -1849,7 +1849,7 @@ class ParamValueQueue : public Vst::IParamValueQueue

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; }

Expand Down Expand Up @@ -1892,7 +1892,7 @@ class ParamValueQueue : public Vst::IParamValueQueue

private:
const Vst::ParamID paramId;
const size_t parameterIndex;
const Steinberg::int32 parameterIndex;
float cachedValue;
Steinberg::int32 size = 0;
Atomic<int> refCount;
Expand Down Expand Up @@ -1981,10 +1981,10 @@ class ParameterChanges : public Vst::IParameterChanges

void initialise (const std::vector<Vst::ParamID>& idsIn)
{
size_t index = 0;
Steinberg::int32 index = 0;

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.clear();
Expand Down Expand Up @@ -2014,7 +2014,7 @@ class VST3PluginInstance : public AudioPluginInstance
struct VST3Parameter final : public Parameter
{
VST3Parameter (VST3PluginInstance& parent,
int vstParameterIndex,
Steinberg::int32 vstParameterIndex,
Steinberg::Vst::ParamID parameterID,
bool parameterIsAutomatable)
: pluginInstance (parent),
Expand All @@ -2026,15 +2026,15 @@ class VST3PluginInstance : public AudioPluginInstance

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
Expand All @@ -2044,7 +2044,7 @@ class VST3PluginInstance : public AudioPluginInstance
*/
void setValueFromEditor (float newValue)
{
pluginInstance.cachedParamValues.set ((size_t) vstParamIndex, newValue);
pluginInstance.cachedParamValues.set (vstParamIndex, newValue);
sendValueChangedMessageToListeners (newValue);
}

Expand Down Expand Up @@ -2122,7 +2122,7 @@ class VST3PluginInstance : public AudioPluginInstance
}

VST3PluginInstance& pluginInstance;
const int vstParamIndex;
const Steinberg::int32 vstParamIndex;
const Steinberg::Vst::ParamID paramID;
const bool automatable;
const bool discrete = getNumSteps() != AudioProcessor::getDefaultNumParameterSteps();
Expand Down Expand Up @@ -2373,10 +2373,18 @@ class VST3PluginInstance : public AudioPluginInstance
}

//==============================================================================
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;
}

//==============================================================================
Expand Down Expand Up @@ -2459,14 +2467,14 @@ class VST3PluginInstance : public AudioPluginInstance
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);
});
Expand Down Expand Up @@ -2886,38 +2894,7 @@ class VST3PluginInstance : public AudioPluginInstance
StringArray programNames;
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;

//==============================================================================
Expand Down Expand Up @@ -3045,6 +3022,19 @@ class VST3PluginInstance : public AudioPluginInstance
}

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()
Expand Down Expand Up @@ -3216,12 +3206,12 @@ class VST3PluginInstance : public AudioPluginInstance
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;
}
Expand Down

0 comments on commit f35c2d9

Please sign in to comment.