1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Plugin clients: Fix bypass behaviours to match getBypassParameter() documentation

This commit is contained in:
reuk 2022-01-21 17:56:29 +00:00
parent 702bfdb2c0
commit 3f315ddd00
7 changed files with 55 additions and 15 deletions

View file

@ -4,6 +4,36 @@ JUCE breaking changes
develop
=======
Change
------
Plugin wrappers will no longer call processBlockBypassed() if the wrapped
AudioProcessor returns a parameter from getBypassParameter().
Possible Issues
---------------
Plugins that used to depend on processBlockBypassed() being called may no
longer correctly enter a bypassed state.
Workaround
----------
AudioProcessors that implement getBypassParameter() must check the current
value of the bypass parameter on each call to processBlock(), and bypass
processing as appropriate. When switching between bypassed and non-bypassed
states, the plugin must use some sort of ramping or smoothing to avoid
discontinuities in the output. If the plugin introduces latency when not
bypassed, the plugin must delay its output when in bypassed mode so that the
overall latency does not change when enabling/disabling bypass.
Rationale
---------
The documentation for AudioProcessor::getBypassParameter() says
> if this method returns a non-null value, you should never call
processBlockBypassed but use the returned parameter to control the bypass
state instead.
Some plugin wrappers were not following this rule. After this change, the
behaviour of all plugin wrappers is consistent with the documented behaviour.
Change
------
The ComponentPeer::getFrameSize() function has been deprecated on Linux.

View file

@ -1533,7 +1533,7 @@ namespace AAXClasses
}
}
if (bypass)
if (bypass && pluginInstance->getBypassParameter() == nullptr)
pluginInstance->processBlockBypassed (buffer, midiBuffer);
else
pluginInstance->processBlock (buffer, midiBuffer);

View file

@ -1652,7 +1652,7 @@ private:
if (processor.isSuspended())
buffer.clear();
else if (bypassParam != nullptr && [au shouldBypassEffect])
else if (bypassParam == nullptr && [au shouldBypassEffect])
processor.processBlockBypassed (buffer, midiBuffer);
else
processor.processBlock (buffer, midiBuffer);

View file

@ -588,7 +588,7 @@ public:
AudioBuffer<float> chans (channels, totalChans, numSamples);
if (mBypassed)
if (mBypassed && juceFilter->getBypassParameter() == nullptr)
juceFilter->processBlockBypassed (chans, midiEvents);
else
juceFilter->processBlock (chans, midiEvents);
@ -685,6 +685,10 @@ public:
else
{
mBypassed = (value > 0);
if (auto* param = juceFilter->getBypassParameter())
if (mBypassed != (param->getValue() >= 0.5f))
param.setValueNotifyingHost (mBypassed ? 1.0f : 0.0f);
}
return CProcess::UpdateControlValue (controlIndex, value);

View file

@ -350,6 +350,11 @@ public:
void process (float* inBuffer, float* outBuffer, int bufferSize, int numInChannels, int numOutChannels, bool isBypassed)
{
// If the plugin has a bypass parameter, set it to the current bypass state
if (auto* param = pluginInstance->getBypassParameter())
if (isBypassed != (param->getValue() >= 0.5f))
param->setValueNotifyingHost (isBypassed ? 1.0f : 0.0f);
for (int pos = 0; pos < bufferSize;)
{
auto max = jmin (bufferSize - pos, samplesPerBlock);
@ -452,7 +457,7 @@ private:
{
MidiBuffer mb;
if (isBypassed)
if (isBypassed && pluginInstance->getBypassParameter() == nullptr)
pluginInstance->processBlockBypassed (scratchBuffer, mb);
else
pluginInstance->processBlock (scratchBuffer, mb);
@ -619,8 +624,7 @@ namespace UnityCallbacks
auto isMuted = ((state->flags & stateIsMuted) != 0);
auto isPaused = ((state->flags & stateIsPaused) != 0);
auto bypassed = ! isPlaying || (isMuted || isPaused);
const auto bypassed = ! isPlaying || (isMuted || isPaused);
pluginInstance->process (inBuffer, outBuffer, static_cast<int> (bufferSize), numInChannels, numOutChannels, bypassed);
}
else

View file

@ -447,7 +447,7 @@ public:
const int numChannels = jmax (numIn, numOut);
AudioBuffer<FloatType> chans (tmpBuffers.channels, isMidiEffect ? 0 : numChannels, numSamples);
if (isBypassed)
if (isBypassed && processor->getBypassParameter() == nullptr)
processor->processBlockBypassed (chans, midiEvents);
else
processor->processBlock (chans, midiEvents);
@ -737,7 +737,7 @@ public:
void parameterValueChanged (int, float newValue) override
{
// this can only come from the bypass parameter
isBypassed = (newValue != 0.0f);
isBypassed = (newValue >= 0.5f);
}
void parameterGestureChanged (int, bool) override {}
@ -1800,10 +1800,10 @@ private:
pointer_sized_int handleSetBypass (VstOpCodeArguments args)
{
isBypassed = (args.value != 0);
isBypassed = args.value != 0;
if (auto* bypass = processor->getBypassParameter())
bypass->setValueNotifyingHost (isBypassed ? 1.0f : 0.0f);
if (auto* param = processor->getBypassParameter())
param->setValueNotifyingHost (isBypassed ? 1.0f : 0.0f);
return 1;
}

View file

@ -2370,10 +2370,10 @@ public:
tresult PLUGIN_API getRoutingInfo (Vst::RoutingInfo&, Vst::RoutingInfo&) override { return kNotImplemented; }
//==============================================================================
bool isBypassed()
bool isBypassed() const
{
if (auto* bypassParam = comPluginInstance->getBypassParameter())
return (bypassParam->getValue() != 0.0f);
return bypassParam->getValue() >= 0.5f;
return false;
}
@ -3374,7 +3374,9 @@ private:
if (totalInputChans == pluginInstance->getTotalNumInputChannels()
&& totalOutputChans == pluginInstance->getTotalNumOutputChannels())
{
if (isBypassed())
// processBlockBypassed should only ever be called if the AudioProcessor doesn't
// return a valid parameter from getBypassParameter
if (pluginInstance->getBypassParameter() == nullptr && comPluginInstance->getBypassParameter()->getValue() >= 0.5f)
pluginInstance->processBlockBypassed (buffer, midiBuffer);
else
pluginInstance->processBlock (buffer, midiBuffer);
@ -3489,7 +3491,7 @@ private:
ptr = {};
}
T* operator->() { return ptr.operator->(); }
T* operator->() const { return ptr.operator->(); }
T* get() const noexcept { return ptr.get(); }
operator T*() const noexcept { return ptr.get(); }