| @@ -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(); } | |||