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:
parent
702bfdb2c0
commit
3f315ddd00
7 changed files with 55 additions and 15 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -1533,7 +1533,7 @@ namespace AAXClasses
|
|||
}
|
||||
}
|
||||
|
||||
if (bypass)
|
||||
if (bypass && pluginInstance->getBypassParameter() == nullptr)
|
||||
pluginInstance->processBlockBypassed (buffer, midiBuffer);
|
||||
else
|
||||
pluginInstance->processBlock (buffer, midiBuffer);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(); }
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue