Browse Source

Add bypass support to both hosting and plug-in client code

tags/2021-05-28
hogliux 7 years ago
parent
commit
0db9415de6
13 changed files with 721 additions and 221 deletions
  1. +49
    -2
      extras/AudioPluginHost/Source/GraphEditorPanel.cpp
  2. +54
    -77
      modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp
  3. +32
    -6
      modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm
  4. +50
    -3
      modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm
  5. +17
    -1
      modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
  6. +112
    -106
      modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
  7. +147
    -2
      modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
  8. +76
    -12
      modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp
  9. +100
    -4
      modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
  10. +29
    -0
      modules/juce_audio_processors/processors/juce_AudioProcessor.h
  11. +43
    -4
      modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp
  12. +8
    -1
      modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h
  13. +4
    -3
      modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp

+ 49
- 2
extras/AudioPluginHost/Source/GraphEditorPanel.cpp View File

@@ -109,19 +109,40 @@ struct GraphEditorPanel::PinComponent : public Component,
}; };
//============================================================================== //==============================================================================
struct GraphEditorPanel::FilterComponent : public Component
struct GraphEditorPanel::FilterComponent : public Component, private AudioProcessorParameter::Listener
{ {
FilterComponent (GraphEditorPanel& p, uint32 id) : panel (p), graph (p.graph), pluginID (id) FilterComponent (GraphEditorPanel& p, uint32 id) : panel (p), graph (p.graph), pluginID (id)
{ {
shadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 3, { 0, 1 })); shadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 3, { 0, 1 }));
setComponentEffect (&shadow); setComponentEffect (&shadow);
if (auto f = graph.graph.getNodeForId (pluginID))
{
if (auto* processor = f->getProcessor())
{
if (auto* bypassParam = processor->getBypassParameter())
bypassParam->addListener (this);
}
}
setSize (150, 60); setSize (150, 60);
} }
FilterComponent (const FilterComponent&) = delete; FilterComponent (const FilterComponent&) = delete;
FilterComponent& operator= (const FilterComponent&) = delete; FilterComponent& operator= (const FilterComponent&) = delete;
~FilterComponent()
{
if (auto f = graph.graph.getNodeForId (pluginID))
{
if (auto* processor = f->getProcessor())
{
if (auto* bypassParam = processor->getBypassParameter())
bypassParam->removeListener (this);
}
}
}
void mouseDown (const MouseEvent& e) override void mouseDown (const MouseEvent& e) override
{ {
originalPos = localPointToGlobal (Point<int>()); originalPos = localPointToGlobal (Point<int>());
@@ -177,8 +198,17 @@ struct GraphEditorPanel::FilterComponent : public Component
void paint (Graphics& g) override void paint (Graphics& g) override
{ {
auto boxArea = getLocalBounds().reduced (4, pinSize); auto boxArea = getLocalBounds().reduced (4, pinSize);
bool isBypassed = false;
if (auto* f = graph.graph.getNodeForId (pluginID))
isBypassed = f->isBypassed();
auto boxColour = findColour (TextEditor::backgroundColourId);
g.setColour (findColour (TextEditor::backgroundColourId));
if (isBypassed)
boxColour = boxColour.brighter();
g.setColour (boxColour);
g.fillRect (boxArea.toFloat()); g.fillRect (boxArea.toFloat());
g.setColour (findColour (TextEditor::textColourId)); g.setColour (findColour (TextEditor::textColourId));
@@ -290,6 +320,7 @@ struct GraphEditorPanel::FilterComponent : public Component
PopupMenu m; PopupMenu m;
m.addItem (1, "Delete this filter"); m.addItem (1, "Delete this filter");
m.addItem (2, "Disconnect all pins"); m.addItem (2, "Disconnect all pins");
m.addItem (3, "Toggle Bypass");
m.addSeparator(); m.addSeparator();
m.addItem (10, "Show plugin GUI"); m.addItem (10, "Show plugin GUI");
m.addItem (11, "Show all programs"); m.addItem (11, "Show all programs");
@@ -302,6 +333,15 @@ struct GraphEditorPanel::FilterComponent : public Component
{ {
case 1: graph.graph.removeNode (pluginID); break; case 1: graph.graph.removeNode (pluginID); break;
case 2: graph.graph.disconnectNode (pluginID); break; case 2: graph.graph.disconnectNode (pluginID); break;
case 3:
{
if (auto* node = graph.graph.getNodeForId (pluginID))
node->setBypassed (! node->isBypassed());
repaint();
break;
}
case 10: showWindow (PluginWindow::Type::normal); break; case 10: showWindow (PluginWindow::Type::normal); break;
case 11: showWindow (PluginWindow::Type::programs); break; case 11: showWindow (PluginWindow::Type::programs); break;
case 12: showWindow (PluginWindow::Type::generic); break; case 12: showWindow (PluginWindow::Type::generic); break;
@@ -329,6 +369,13 @@ struct GraphEditorPanel::FilterComponent : public Component
w->toFront (true); w->toFront (true);
} }
void parameterValueChanged (int, float) override
{
repaint();
}
void parameterGestureChanged (int, bool) override {}
GraphEditorPanel& panel; GraphEditorPanel& panel;
FilterGraph& graph; FilterGraph& graph;
const AudioProcessorGraph::NodeID pluginID; const AudioProcessorGraph::NodeID pluginID;


+ 54
- 77
modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp View File

@@ -133,11 +133,6 @@ namespace AAXClasses
jassert (result == AAX_SUCCESS); ignoreUnused (result); jassert (result == AAX_SUCCESS); ignoreUnused (result);
} }
static bool isBypassParam (AAX_CParamID paramID) noexcept
{
return AAX::IsParameterIDEqual (paramID, cDefaultMasterBypassID) != 0;
}
// maps a channel index of an AAX format to an index of a juce format // maps a channel index of an AAX format to an index of a juce format
struct AAXChannelStreamOrder struct AAXChannelStreamOrder
{ {
@@ -483,10 +478,12 @@ namespace AAXClasses
{ {
if (component != nullptr && component->pluginEditor != nullptr) if (component != nullptr && component->pluginEditor != nullptr)
{ {
if (! isBypassParam (paramID))
auto index = getParamIndexFromID (paramID);
if (index >= 0)
{ {
AudioProcessorEditor::ParameterControlHighlightInfo info; AudioProcessorEditor::ParameterControlHighlightInfo info;
info.parameterIndex = getParamIndexFromID (paramID);
info.parameterIndex = index;
info.isHighlighted = (isHighlighted != 0); info.isHighlighted = (isHighlighted != 0);
info.suggestedColour = getColourFromHighlightEnum (colour); info.suggestedColour = getColourFromHighlightEnum (colour);
@@ -660,7 +657,6 @@ namespace AAXClasses
if (err != AAX_SUCCESS) if (err != AAX_SUCCESS)
return err; return err;
addBypassParameter();
addAudioProcessorParameters(); addAudioProcessorParameters();
return AAX_SUCCESS; return AAX_SUCCESS;
@@ -804,21 +800,13 @@ namespace AAXClasses
AAX_Result UpdateParameterNormalizedValue (AAX_CParamID paramID, double value, AAX_EUpdateSource source) override AAX_Result UpdateParameterNormalizedValue (AAX_CParamID paramID, double value, AAX_EUpdateSource source) override
{ {
auto result = AAX_CEffectParameters::UpdateParameterNormalizedValue (paramID, value, source); auto result = AAX_CEffectParameters::UpdateParameterNormalizedValue (paramID, value, source);
if (! isBypassParam (paramID))
setAudioProcessorParameter (paramID, value);
setAudioProcessorParameter (paramID, value);
return result; return result;
} }
AAX_Result GetParameterValueFromString (AAX_CParamID paramID, double* result, const AAX_IString& text) const override AAX_Result GetParameterValueFromString (AAX_CParamID paramID, double* result, const AAX_IString& text) const override
{ {
if (isBypassParam (paramID))
{
*result = (text.Get()[0] == 'B') ? 1.0 : 0.0;
return AAX_SUCCESS;
}
if (auto* param = getParameterFromID (paramID)) if (auto* param = getParameterFromID (paramID))
{ {
if (! LegacyAudioParameter::isLegacy (param)) if (! LegacyAudioParameter::isLegacy (param))
@@ -833,39 +821,22 @@ namespace AAXClasses
AAX_Result GetParameterStringFromValue (AAX_CParamID paramID, double value, AAX_IString* result, int32_t maxLen) const override AAX_Result GetParameterStringFromValue (AAX_CParamID paramID, double value, AAX_IString* result, int32_t maxLen) const override
{ {
if (isBypassParam (paramID))
{
result->Set (value == 0 ? "Off" : (maxLen >= 8 ? "Bypassed" : "Byp"));
}
else
{
if (auto* param = getParameterFromID (paramID))
result->Set (param->getText ((float) value, maxLen).toRawUTF8());
}
if (auto* param = getParameterFromID (paramID))
result->Set (param->getText ((float) value, maxLen).toRawUTF8());
return AAX_SUCCESS; return AAX_SUCCESS;
} }
AAX_Result GetParameterNumberofSteps (AAX_CParamID paramID, int32_t* result) const AAX_Result GetParameterNumberofSteps (AAX_CParamID paramID, int32_t* result) const
{ {
if (isBypassParam (paramID))
{
*result = 2;
}
else
{
if (auto* param = getParameterFromID (paramID))
*result = param->getNumSteps();
}
if (auto* param = getParameterFromID (paramID))
*result = param->getNumSteps();
return AAX_SUCCESS; return AAX_SUCCESS;
} }
AAX_Result GetParameterNormalizedValue (AAX_CParamID paramID, double* result) const override AAX_Result GetParameterNormalizedValue (AAX_CParamID paramID, double* result) const override
{ {
if (isBypassParam (paramID))
return AAX_CEffectParameters::GetParameterNormalizedValue (paramID, result);
if (auto* param = getParameterFromID (paramID)) if (auto* param = getParameterFromID (paramID))
*result = (double) param->getValue(); *result = (double) param->getValue();
else else
@@ -876,9 +847,6 @@ namespace AAXClasses
AAX_Result SetParameterNormalizedValue (AAX_CParamID paramID, double newValue) override AAX_Result SetParameterNormalizedValue (AAX_CParamID paramID, double newValue) override
{ {
if (isBypassParam (paramID))
return AAX_CEffectParameters::SetParameterNormalizedValue (paramID, newValue);
if (auto* p = mParameterManager.GetParameterByID (paramID)) if (auto* p = mParameterManager.GetParameterByID (paramID))
p->SetValueWithFloat ((float) newValue); p->SetValueWithFloat ((float) newValue);
@@ -889,9 +857,6 @@ namespace AAXClasses
AAX_Result SetParameterNormalizedRelative (AAX_CParamID paramID, double newDeltaValue) override AAX_Result SetParameterNormalizedRelative (AAX_CParamID paramID, double newDeltaValue) override
{ {
if (isBypassParam (paramID))
return AAX_CEffectParameters::SetParameterNormalizedRelative (paramID, newDeltaValue);
if (auto* param = getParameterFromID (paramID)) if (auto* param = getParameterFromID (paramID))
{ {
auto newValue = param->getValue() + (float) newDeltaValue; auto newValue = param->getValue() + (float) newDeltaValue;
@@ -907,25 +872,15 @@ namespace AAXClasses
AAX_Result GetParameterNameOfLength (AAX_CParamID paramID, AAX_IString* result, int32_t maxLen) const override AAX_Result GetParameterNameOfLength (AAX_CParamID paramID, AAX_IString* result, int32_t maxLen) const override
{ {
if (isBypassParam (paramID))
{
result->Set (maxLen >= 13 ? "Master Bypass"
: (maxLen >= 8 ? "Mast Byp"
: (maxLen >= 6 ? "MstByp" : "MByp")));
}
else if (auto* param = getParameterFromID (paramID))
{
if (auto* param = getParameterFromID (paramID))
result->Set (param->getName (maxLen).toRawUTF8()); result->Set (param->getName (maxLen).toRawUTF8());
}
return AAX_SUCCESS; return AAX_SUCCESS;
} }
AAX_Result GetParameterName (AAX_CParamID paramID, AAX_IString* result) const override AAX_Result GetParameterName (AAX_CParamID paramID, AAX_IString* result) const override
{ {
if (isBypassParam (paramID))
result->Set ("Master Bypass");
else if (auto* param = getParameterFromID (paramID))
if (auto* param = getParameterFromID (paramID))
result->Set (param->getName (31).toRawUTF8()); result->Set (param->getName (31).toRawUTF8());
return AAX_SUCCESS; return AAX_SUCCESS;
@@ -933,15 +888,12 @@ namespace AAXClasses
AAX_Result GetParameterDefaultNormalizedValue (AAX_CParamID paramID, double* result) const override AAX_Result GetParameterDefaultNormalizedValue (AAX_CParamID paramID, double* result) const override
{ {
if (! isBypassParam (paramID))
{
if (auto* param = getParameterFromID (paramID))
*result = (double) param->getDefaultValue();
else
*result = 0.0;
if (auto* param = getParameterFromID (paramID))
*result = (double) param->getDefaultValue();
else
*result = 0.0;
jassert (*result >= 0 && *result <= 1.0f);
}
jassert (*result >= 0 && *result <= 1.0f);
return AAX_SUCCESS; return AAX_SUCCESS;
} }
@@ -1420,18 +1372,18 @@ namespace AAXClasses
#endif #endif
} }
void addBypassParameter()
bool isBypassPartOfRegularParemeters() const
{ {
auto* masterBypass = new AAX_CParameter<bool> (cDefaultMasterBypassID,
AAX_CString ("Master Bypass"),
false,
AAX_CBinaryTaperDelegate<bool>(),
AAX_CBinaryDisplayDelegate<bool> ("bypass", "on"),
true);
masterBypass->SetNumberOfSteps (2);
masterBypass->SetType (AAX_eParameterType_Discrete);
mParameterManager.AddParameter (masterBypass);
mPacketDispatcher.RegisterPacket (cDefaultMasterBypassID, JUCEAlgorithmIDs::bypass);
auto& audioProcessor = getPluginInstance();
int n = juceParameters.getNumParameters();
if (auto* bypassParam = audioProcessor.getBypassParameter())
for (int i = 0; i < n; ++i)
if (juceParameters.getParamForIndex (i) == bypassParam)
return true;
return false;
} }
void addAudioProcessorParameters() void addAudioProcessorParameters()
@@ -1444,14 +1396,35 @@ namespace AAXClasses
const bool forceLegacyParamIDs = false; const bool forceLegacyParamIDs = false;
#endif #endif
auto bypassPartOfRegularParams = isBypassPartOfRegularParemeters();
juceParameters.update (audioProcessor, forceLegacyParamIDs); juceParameters.update (audioProcessor, forceLegacyParamIDs);
bool aaxWrapperProvidedBypassParam = false;
auto* bypassParameter = pluginInstance->getBypassParameter();
if (bypassParameter == nullptr)
{
aaxWrapperProvidedBypassParam = true;
ownedBypassParameter = new AudioParameterBool (cDefaultMasterBypassID, "Master Bypass", false, {}, {}, {});
bypassParameter = ownedBypassParameter;
}
if (! bypassPartOfRegularParams)
juceParameters.params.add (bypassParameter);
int parameterIndex = 0; int parameterIndex = 0;
for (auto* juceParam : juceParameters.params) for (auto* juceParam : juceParameters.params)
{ {
auto isBypassParameter = (juceParam == bypassParameter);
auto category = juceParam->getCategory(); auto category = juceParam->getCategory();
auto paramID = juceParameters.getParamID (audioProcessor, parameterIndex)
.toRawUTF8();
auto paramID = isBypassParameter ? cDefaultMasterBypassID
: juceParameters.getParamID (audioProcessor, parameterIndex)
.toRawUTF8();
aaxParamIDs.add (paramID); aaxParamIDs.add (paramID);
auto aaxParamID = aaxParamIDs.getReference (parameterIndex++).getCharPointer(); auto aaxParamID = aaxParamIDs.getReference (parameterIndex++).getCharPointer();
@@ -1493,6 +1466,9 @@ namespace AAXClasses
| AAX_eParameterOrientation_RotarySingleDotMode | AAX_eParameterOrientation_RotaryLeftMinRightMax)); | AAX_eParameterOrientation_RotarySingleDotMode | AAX_eParameterOrientation_RotaryLeftMinRightMax));
mParameterManager.AddParameter (parameter); mParameterManager.AddParameter (parameter);
if (isBypassParameter)
mPacketDispatcher.RegisterPacket (aaxParamID, JUCEAlgorithmIDs::bypass);
} }
} }
@@ -1785,6 +1761,7 @@ namespace AAXClasses
Array<String> aaxParamIDs; Array<String> aaxParamIDs;
HashMap<int32, AudioProcessorParameter*> paramMap; HashMap<int32, AudioProcessorParameter*> paramMap;
LegacyAudioParametersWrapper juceParameters; LegacyAudioParametersWrapper juceParameters;
ScopedPointer<AudioProcessorParameter> ownedBypassParameter;
Array<AudioProcessorParameter*> aaxMeters; Array<AudioProcessorParameter*> aaxMeters;


+ 32
- 6
modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm View File

@@ -121,7 +121,8 @@ class JuceAU : public AudioProcessorHolder,
public MusicDeviceBase, public MusicDeviceBase,
public AudioProcessorListener, public AudioProcessorListener,
public AudioPlayHead, public AudioPlayHead,
public ComponentListener
public ComponentListener,
public AudioProcessorParameter::Listener
{ {
public: public:
JuceAU (AudioUnit component) JuceAU (AudioUnit component)
@@ -181,6 +182,9 @@ public:
~JuceAU() ~JuceAU()
{ {
if (bypassParam != nullptr)
bypassParam->removeListener (this);
deleteActiveEditors(); deleteActiveEditors();
juceFilter = nullptr; juceFilter = nullptr;
clearPresetsArray(); clearPresetsArray();
@@ -484,7 +488,10 @@ public:
return noErr; return noErr;
case kAudioUnitProperty_BypassEffect: case kAudioUnitProperty_BypassEffect:
*(UInt32*) outData = isBypassed ? 1 : 0;
if (bypassParam != nullptr)
*(UInt32*) outData = (bypassParam->getValue() != 0.0f ? 1 : 0);
else
*(UInt32*) outData = isBypassed ? 1 : 0;
return noErr; return noErr;
case kAudioUnitProperty_SupportsMPE: case kAudioUnitProperty_SupportsMPE:
@@ -605,12 +612,16 @@ public:
return kAudioUnitErr_InvalidPropertyValue; return kAudioUnitErr_InvalidPropertyValue;
const bool newBypass = *((UInt32*) inData) != 0; const bool newBypass = *((UInt32*) inData) != 0;
const bool currentlyBypassed = (bypassParam != nullptr ? (bypassParam->getValue() != 0.0f) : isBypassed);
if (newBypass != isBypassed)
if (newBypass != currentlyBypassed)
{ {
isBypassed = newBypass;
if (bypassParam != nullptr)
bypassParam->setValueNotifyingHost (newBypass ? 1.0f : 0.0f);
else
isBypassed = newBypass;
if (! isBypassed && IsInitialized()) // turning bypass off and we're initialized
if (! currentlyBypassed && IsInitialized()) // turning bypass off and we're initialized
Reset (0, 0); Reset (0, 0);
} }
@@ -1117,6 +1128,15 @@ public:
PropertyChanged (kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0); PropertyChanged (kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0);
} }
//==============================================================================
// this will only ever be called by the bypass parameter
void parameterValueChanged (int, float) override
{
PropertyChanged (kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0);
}
void parameterGestureChanged (int, bool) override {}
//============================================================================== //==============================================================================
bool StreamFormatWritable (AudioUnitScope scope, AudioUnitElement element) override bool StreamFormatWritable (AudioUnitScope scope, AudioUnitElement element) override
{ {
@@ -1690,6 +1710,9 @@ private:
//============================================================================== //==============================================================================
OwnedArray<OwnedArray<const __CFString>> parameterValueStringArrays; OwnedArray<OwnedArray<const __CFString>> parameterValueStringArrays;
//==============================================================================
AudioProcessorParameter* bypassParam = nullptr;
//============================================================================== //==============================================================================
void pullInputAudio (AudioUnitRenderActionFlags& flags, const AudioTimeStamp& timestamp, const UInt32 nFrames) noexcept void pullInputAudio (AudioUnitRenderActionFlags& flags, const AudioTimeStamp& timestamp, const UInt32 nFrames) noexcept
{ {
@@ -1730,7 +1753,7 @@ private:
{ {
buffer.clear(); buffer.clear();
} }
else if (isBypassed)
else if (bypassParam == nullptr && isBypassed)
{ {
juceFilter->processBlockBypassed (buffer, midiBuffer); juceFilter->processBlockBypassed (buffer, midiBuffer);
} }
@@ -1878,6 +1901,9 @@ private:
parameterValueStringArrays.add (stringValues); parameterValueStringArrays.add (stringValues);
} }
if ((bypassParam = juceFilter->getBypassParameter()) != nullptr)
bypassParam->addListener (this);
} }
//============================================================================== //==============================================================================


+ 50
- 3
modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm View File

@@ -195,6 +195,18 @@ public:
virtual bool getRenderingOffline() = 0; virtual bool getRenderingOffline() = 0;
virtual void setRenderingOffline (bool offline) = 0; virtual void setRenderingOffline (bool offline) = 0;
virtual bool getShouldBypassEffect()
{
objc_super s = { getAudioUnit(), [AUAudioUnit class] };
return (ObjCMsgSendSuper<BOOL> (&s, @selector (shouldBypassEffect)) == YES);
}
virtual void setShouldBypassEffect (bool shouldBypass)
{
objc_super s = { getAudioUnit(), [AUAudioUnit class] };
ObjCMsgSendSuper<void, BOOL> (&s, @selector (setShouldBypassEffect:), shouldBypass ? YES : NO);
}
//============================================================================== //==============================================================================
virtual NSString* getContextName() const = 0; virtual NSString* getContextName() const = 0;
virtual void setContextName (NSString*) = 0; virtual void setContextName (NSString*) = 0;
@@ -274,6 +286,8 @@ private:
addMethod (@selector (canProcessInPlace), getCanProcessInPlace, @encode (BOOL), "@:"); addMethod (@selector (canProcessInPlace), getCanProcessInPlace, @encode (BOOL), "@:");
addMethod (@selector (isRenderingOffline), getRenderingOffline, @encode (BOOL), "@:"); addMethod (@selector (isRenderingOffline), getRenderingOffline, @encode (BOOL), "@:");
addMethod (@selector (setRenderingOffline:), setRenderingOffline, "v@:", @encode (BOOL)); addMethod (@selector (setRenderingOffline:), setRenderingOffline, "v@:", @encode (BOOL));
addMethod (@selector (shouldBypassEffect), getShouldBypassEffect, @encode (BOOL), "@:");
addMethod (@selector (setShouldBypassEffect:), setShouldBypassEffect, "v@:", @encode (BOOL));
addMethod (@selector (allocateRenderResourcesAndReturnError:), allocateRenderResourcesAndReturnError, "B@:^@"); addMethod (@selector (allocateRenderResourcesAndReturnError:), allocateRenderResourcesAndReturnError, "B@:^@");
addMethod (@selector (deallocateRenderResources), deallocateRenderResources, "v@:"); addMethod (@selector (deallocateRenderResources), deallocateRenderResources, "v@:");
@@ -388,6 +402,8 @@ private:
static void setRenderingOffline (id self, SEL, BOOL renderingOffline) { _this (self)->setRenderingOffline (renderingOffline); } static void setRenderingOffline (id self, SEL, BOOL renderingOffline) { _this (self)->setRenderingOffline (renderingOffline); }
static BOOL allocateRenderResourcesAndReturnError (id self, SEL, NSError** error) { return _this (self)->allocateRenderResourcesAndReturnError (error) ? YES : NO; } static BOOL allocateRenderResourcesAndReturnError (id self, SEL, NSError** error) { return _this (self)->allocateRenderResourcesAndReturnError (error) ? YES : NO; }
static void deallocateRenderResources (id self, SEL) { _this (self)->deallocateRenderResources(); } static void deallocateRenderResources (id self, SEL) { _this (self)->deallocateRenderResources(); }
static BOOL getShouldBypassEffect (id self, SEL) { return _this (self)->getShouldBypassEffect() ? YES : NO; }
static void setShouldBypassEffect (id self, SEL, BOOL shouldBypass) { _this (self)->setShouldBypassEffect (shouldBypass); }
//============================================================================== //==============================================================================
static NSString* getContextName (id self, SEL) { return _this (self)->getContextName(); } static NSString* getContextName (id self, SEL) { return _this (self)->getContextName(); }
@@ -417,7 +433,8 @@ JuceAudioUnitv3Base::Class JuceAudioUnitv3Base::audioUnitObjCClass;
//============================================================================== //==============================================================================
class JuceAudioUnitv3 : public JuceAudioUnitv3Base, class JuceAudioUnitv3 : public JuceAudioUnitv3Base,
public AudioProcessorListener, public AudioProcessorListener,
public AudioPlayHead
public AudioPlayHead,
private AudioProcessorParameter::Listener
{ {
public: public:
JuceAudioUnitv3 (const AudioProcessorHolder::Ptr& processor, JuceAudioUnitv3 (const AudioProcessorHolder::Ptr& processor,
@@ -444,6 +461,9 @@ public:
auto& processor = getAudioProcessor(); auto& processor = getAudioProcessor();
processor.removeListener (this); processor.removeListener (this);
if (bypassParam != nullptr)
bypassParam->removeListener (this);
removeEditor (processor); removeEditor (processor);
if (editorObserverToken != nullptr) if (editorObserverToken != nullptr)
@@ -735,6 +755,22 @@ public:
} }
} }
bool getShouldBypassEffect() override
{
if (bypassParam != nullptr)
return (bypassParam->getValue() != 0.0f);
return JuceAudioUnitv3Base::getShouldBypassEffect();
}
void setShouldBypassEffect (bool shouldBypass) override
{
if (bypassParam != nullptr)
bypassParam->setValue (shouldBypass ? 1.0f : 0.0f);
JuceAudioUnitv3Base::setShouldBypassEffect (shouldBypass);
}
//============================================================================== //==============================================================================
NSString* getContextName() const override { return juceStringToNS (contextName); } NSString* getContextName() const override { return juceStringToNS (contextName); }
void setContextName (NSString* str) override void setContextName (NSString* str) override
@@ -1216,7 +1252,6 @@ private:
#endif #endif
// create methods in AUParameterTree return unretained objects (!) -> see Apple header AUAudioUnitImplementation.h // create methods in AUParameterTree return unretained objects (!) -> see Apple header AUAudioUnitImplementation.h
ScopedPointer<AUParameter> param = [[AUParameterTree createParameterWithIdentifier: juceStringToNS (identifier) ScopedPointer<AUParameter> param = [[AUParameterTree createParameterWithIdentifier: juceStringToNS (identifier)
name: juceStringToNS (name) name: juceStringToNS (name)
address: address address: address
@@ -1252,6 +1287,9 @@ private:
editorParamObserver = CreateObjCBlock (this, &JuceAudioUnitv3::valueChangedForObserver); editorParamObserver = CreateObjCBlock (this, &JuceAudioUnitv3::valueChangedForObserver);
editorObserverToken = [paramTree tokenByAddingParameterObserver: editorParamObserver]; editorObserverToken = [paramTree tokenByAddingParameterObserver: editorParamObserver];
} }
if ((bypassParam = processor.getBypassParameter()) != nullptr)
bypassParam->addListener (this);
} }
void setAudioProcessorParameter (AudioProcessorParameter* juceParam, float value) void setAudioProcessorParameter (AudioProcessorParameter* juceParam, float value)
@@ -1454,7 +1492,7 @@ private:
if (processor.isSuspended()) if (processor.isSuspended())
buffer.clear(); buffer.clear();
else if ([au shouldBypassEffect])
else if (bypassParam != nullptr && [au shouldBypassEffect])
processor.processBlockBypassed (buffer, midiBuffer); processor.processBlockBypassed (buffer, midiBuffer);
else else
processor.processBlock (buffer, midiBuffer); processor.processBlock (buffer, midiBuffer);
@@ -1526,6 +1564,14 @@ private:
} }
//============================================================================== //==============================================================================
// this is only ever called for the bypass parameter
void parameterValueChanged (int, float newValue) override
{
JuceAudioUnitv3Base::setShouldBypassEffect (newValue != 0.0f);
}
void parameterGestureChanged (int, bool) override {}
//==============================================================================
#if JUCE_FORCE_USE_LEGACY_PARAM_IDS #if JUCE_FORCE_USE_LEGACY_PARAM_IDS
inline AUParameterAddress getAUParameterAddressForIndex (int paramIndex) const noexcept { return static_cast<AUParameterAddress> (paramIndex); } inline AUParameterAddress getAUParameterAddressForIndex (int paramIndex) const noexcept { return static_cast<AUParameterAddress> (paramIndex); }
inline int getJuceParameterIndexForAUAddress (AUParameterAddress address) const noexcept { return static_cast<int> (address); } inline int getJuceParameterIndexForAUAddress (AUParameterAddress address) const noexcept { return static_cast<int> (address); }
@@ -1610,6 +1656,7 @@ private:
#else #else
static constexpr bool forceLegacyParamIDs = false; static constexpr bool forceLegacyParamIDs = false;
#endif #endif
AudioProcessorParameter* bypassParam = nullptr;
}; };
const double JuceAudioUnitv3::kDefaultSampleRate = 44100.0; const double JuceAudioUnitv3::kDefaultSampleRate = 44100.0;


+ 17
- 1
modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp View File

@@ -223,7 +223,8 @@ struct AbletonLiveHostSpecific
class JuceVSTWrapper : public AudioProcessorListener, class JuceVSTWrapper : public AudioProcessorListener,
public AudioPlayHead, public AudioPlayHead,
private Timer, private Timer,
private AsyncUpdater
private AsyncUpdater,
private AudioProcessorParameter::Listener
{ {
private: private:
//============================================================================== //==============================================================================
@@ -282,6 +283,9 @@ public:
processor->setPlayHead (this); processor->setPlayHead (this);
processor->addListener (this); processor->addListener (this);
if (auto* juceParam = processor->getBypassParameter())
juceParam->addListener (this);
juceParameters.update (*processor, false); juceParameters.update (*processor, false);
memset (&vstEffect, 0, sizeof (vstEffect)); memset (&vstEffect, 0, sizeof (vstEffect));
@@ -760,6 +764,14 @@ public:
hostCallback (&vstEffect, hostOpcodeParameterChangeGestureEnd, index, 0, 0, 0); hostCallback (&vstEffect, hostOpcodeParameterChangeGestureEnd, index, 0, 0, 0);
} }
void parameterValueChanged (int, float newValue) override
{
// this can only come from the bypass parameter
isBypassed = (newValue != 0.0f);
}
void parameterGestureChanged (int, bool) override {}
void audioProcessorChanged (AudioProcessor*) override void audioProcessorChanged (AudioProcessor*) override
{ {
vstEffect.latency = processor->getLatencySamples(); vstEffect.latency = processor->getLatencySamples();
@@ -1921,6 +1933,10 @@ private:
pointer_sized_int handleSetBypass (VstOpCodeArguments args) 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);
return 1; return 1;
} }


+ 112
- 106
modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp View File

@@ -119,6 +119,11 @@ public:
return paramMap[static_cast<int32> (paramID)]; return paramMap[static_cast<int32> (paramID)];
} }
AudioProcessorParameter* getBypassParameter() const noexcept
{
return getParamForVSTParamID (bypassParamID);
}
int getNumParameters() const noexcept { return vstParamIDs.size(); } int getNumParameters() const noexcept { return vstParamIDs.size(); }
bool isUsingManagedParameters() const noexcept { return juceParameters.isUsingManagedParameters(); } bool isUsingManagedParameters() const noexcept { return juceParameters.isUsingManagedParameters(); }
@@ -126,7 +131,7 @@ public:
static const FUID iid; static const FUID iid;
Array<Vst::ParamID> vstParamIDs; Array<Vst::ParamID> vstParamIDs;
Vst::ParamID bypassParamID = 0; Vst::ParamID bypassParamID = 0;
bool isBypassed = false;
bool bypassIsRegularParameter = false;
private: private:
enum InternalParameters enum InternalParameters
@@ -135,6 +140,18 @@ private:
}; };
//============================================================================== //==============================================================================
bool isBypassPartOfRegularParemeters() const
{
int n = juceParameters.getNumParameters();
if (auto* bypassParam = audioProcessor->getBypassParameter())
for (int i = 0; i < n; ++i)
if (juceParameters.getParamForIndex (i) == bypassParam)
return true;
return false;
}
void setupParameters() void setupParameters()
{ {
#if JUCE_FORCE_USE_LEGACY_PARAM_IDS #if JUCE_FORCE_USE_LEGACY_PARAM_IDS
@@ -146,17 +163,42 @@ private:
juceParameters.update (*audioProcessor, forceLegacyParamIDs); juceParameters.update (*audioProcessor, forceLegacyParamIDs);
auto numParameters = juceParameters.getNumParameters(); auto numParameters = juceParameters.getNumParameters();
bool vst3WrapperProvidedBypassParam = false;
auto* bypassParameter = audioProcessor->getBypassParameter();
if (bypassParameter == nullptr)
{
vst3WrapperProvidedBypassParam = true;
bypassParameter = ownedBypassParameter = new AudioParameterBool ("byps", "Bypass", false, {}, {}, {});
}
// if the bypass parameter is not part of the exported parameters that the plug-in supports
// then add it to the end of the list as VST3 requires the bypass parameter to be exported!
bypassIsRegularParameter = isBypassPartOfRegularParemeters();
if (! bypassIsRegularParameter)
juceParameters.params.add (bypassParameter);
int i = 0; int i = 0;
for (auto* juceParam : juceParameters.params) for (auto* juceParam : juceParameters.params)
{ {
bool isBypassParameter = (juceParam == bypassParameter);
Vst::ParamID vstParamID = forceLegacyParamIDs ? static_cast<Vst::ParamID> (i++) Vst::ParamID vstParamID = forceLegacyParamIDs ? static_cast<Vst::ParamID> (i++)
: generateVSTParamIDForParam (juceParam); : generateVSTParamIDForParam (juceParam);
if (isBypassParameter)
{
// we need to remain backward compatible with the old bypass id
if (vst3WrapperProvidedBypassParam)
vstParamID = static_cast<Vst::ParamID> (isUsingManagedParameters() ? paramBypass : numParameters);
bypassParamID = vstParamID;
}
vstParamIDs.add (vstParamID); vstParamIDs.add (vstParamID);
paramMap.set (static_cast<int32> (vstParamID), juceParam); paramMap.set (static_cast<int32> (vstParamID), juceParam);
} }
bypassParamID = static_cast<Vst::ParamID> (isUsingManagedParameters() ? paramBypass : numParameters);
} }
Vst::ParamID generateVSTParamIDForParam (AudioProcessorParameter* param) Vst::ParamID generateVSTParamIDForParam (AudioProcessorParameter* param)
@@ -181,6 +223,7 @@ private:
//============================================================================== //==============================================================================
LegacyAudioParametersWrapper juceParameters; LegacyAudioParametersWrapper juceParameters;
HashMap<int32, AudioProcessorParameter*> paramMap; HashMap<int32, AudioProcessorParameter*> paramMap;
ScopedPointer<AudioProcessorParameter> ownedBypassParameter;
JuceAudioProcessor() = delete; JuceAudioProcessor() = delete;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceAudioProcessor) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceAudioProcessor)
@@ -194,7 +237,8 @@ static ThreadLocalValue<bool> inParameterChangedCallback;
class JuceVST3EditController : public Vst::EditController, class JuceVST3EditController : public Vst::EditController,
public Vst::IMidiMapping, public Vst::IMidiMapping,
public Vst::ChannelContext::IInfoListener, public Vst::ChannelContext::IInfoListener,
public AudioProcessorListener
public AudioProcessorListener,
private AudioProcessorParameter::Listener
{ {
public: public:
JuceVST3EditController (Vst::IHostApplication* host) JuceVST3EditController (Vst::IHostApplication* host)
@@ -279,7 +323,7 @@ public:
struct Param : public Vst::Parameter struct Param : public Vst::Parameter
{ {
Param (JuceVST3EditController& editController, AudioProcessorParameter& p, Param (JuceVST3EditController& editController, AudioProcessorParameter& p,
Vst::ParamID vstParamID, bool forceLegacyParamIDs)
Vst::ParamID vstParamID, bool isBypassParameter, bool forceLegacyParamIDs)
: owner (editController), param (p) : owner (editController), param (p)
{ {
info.id = vstParamID; info.id = vstParamID;
@@ -306,6 +350,9 @@ public:
else else
info.flags = param.isAutomatable() ? Vst::ParameterInfo::kCanAutomate : 0; info.flags = param.isAutomatable() ? Vst::ParameterInfo::kCanAutomate : 0;
if (isBypassParameter)
info.flags |= Vst::ParameterInfo::kIsBypass;
valueNormalized = info.defaultNormalizedValue; valueNormalized = info.defaultNormalizedValue;
} }
@@ -370,88 +417,6 @@ public:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Param) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Param)
}; };
//==============================================================================
struct BypassParam : public Vst::Parameter
{
BypassParam (Vst::ParamID vstParamID)
{
info.id = vstParamID;
toString128 (info.title, "Bypass");
toString128 (info.shortTitle, "Bypass");
toString128 (info.units, "");
info.stepCount = 1;
info.defaultNormalizedValue = 0.0f;
info.unitId = Vst::kRootUnitId;
info.flags = Vst::ParameterInfo::kIsBypass | Vst::ParameterInfo::kCanAutomate;
}
virtual ~BypassParam() {}
bool setNormalized (Vst::ParamValue v) override
{
bool bypass = (v != 0.0f);
v = (bypass ? 1.0f : 0.0f);
if (valueNormalized != v)
{
valueNormalized = v;
changed();
return true;
}
return false;
}
void toString (Vst::ParamValue value, Vst::String128 result) const override
{
bool bypass = (value != 0.0f);
toString128 (result, bypass ? "On" : "Off");
}
bool fromString (const Vst::TChar* text, Vst::ParamValue& outValueNormalized) const override
{
auto paramValueString = getStringFromVstTChars (text);
if (paramValueString.equalsIgnoreCase ("on")
|| paramValueString.equalsIgnoreCase ("yes")
|| paramValueString.equalsIgnoreCase ("true"))
{
outValueNormalized = 1.0f;
return true;
}
if (paramValueString.equalsIgnoreCase ("off")
|| paramValueString.equalsIgnoreCase ("no")
|| paramValueString.equalsIgnoreCase ("false"))
{
outValueNormalized = 0.0f;
return true;
}
var varValue = JSON::fromString (paramValueString);
if (varValue.isDouble() || varValue.isInt()
|| varValue.isInt64() || varValue.isBool())
{
double value = varValue;
outValueNormalized = (value != 0.0) ? 1.0f : 0.0f;
return true;
}
return false;
}
static String getStringFromVstTChars (const Vst::TChar* text)
{
return juce::String (juce::CharPointer_UTF16 (reinterpret_cast<const juce::CharPointer_UTF16::CharType*> (text)));
}
Vst::ParamValue toPlain (Vst::ParamValue v) const override { return v; }
Vst::ParamValue toNormalized (Vst::ParamValue v) const override { return v; }
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BypassParam)
};
//============================================================================== //==============================================================================
struct ProgramChangeParameter : public Vst::Parameter struct ProgramChangeParameter : public Vst::Parameter
{ {
@@ -696,6 +661,19 @@ public:
componentHandler->restartComponent (Vst::kLatencyChanged | Vst::kParamValuesChanged); componentHandler->restartComponent (Vst::kLatencyChanged | Vst::kParamValuesChanged);
} }
void parameterValueChanged (int, float newValue) override
{
// this can only come from the bypass parameter
paramChanged (audioProcessor->bypassParamID, newValue);
}
void parameterGestureChanged (int, bool gestureIsStarting) override
{
// this can only come from the bypass parameter
if (gestureIsStarting) beginEdit (audioProcessor->bypassParamID);
else endEdit (audioProcessor->bypassParamID);
}
//============================================================================== //==============================================================================
AudioProcessor* getPluginInstance() const noexcept AudioProcessor* getPluginInstance() const noexcept
{ {
@@ -732,6 +710,11 @@ private:
{ {
pluginInstance->addListener (this); pluginInstance->addListener (this);
// as the bypass is not part of the regular parameters
// we need to listen for it explicitly
if (! audioProcessor->bypassIsRegularParameter)
audioProcessor->getBypassParameter()->addListener (this);
if (parameters.getParameterCount() <= 0) if (parameters.getParameterCount() <= 0)
{ {
#if JUCE_FORCE_USE_LEGACY_PARAM_IDS #if JUCE_FORCE_USE_LEGACY_PARAM_IDS
@@ -747,11 +730,10 @@ private:
auto vstParamID = audioProcessor->getVSTParamIDForIndex (i); auto vstParamID = audioProcessor->getVSTParamIDForIndex (i);
auto* juceParam = audioProcessor->getParamForVSTParamID (vstParamID); auto* juceParam = audioProcessor->getParamForVSTParamID (vstParamID);
parameters.addParameter (new Param (*this, *juceParam, vstParamID, forceLegacyParamIDs));
parameters.addParameter (new Param (*this, *juceParam, vstParamID,
(vstParamID == audioProcessor->bypassParamID), forceLegacyParamIDs));
} }
parameters.addParameter (new BypassParam (audioProcessor->bypassParamID));
if (pluginInstance->getNumPrograms() > 1) if (pluginInstance->getNumPrograms() > 1)
parameters.addParameter (new ProgramChangeParameter (*pluginInstance)); parameters.addParameter (new ProgramChangeParameter (*pluginInstance));
} }
@@ -1331,24 +1313,50 @@ public:
tresult PLUGIN_API setIoMode (Vst::IoMode) override { return kNotImplemented; } tresult PLUGIN_API setIoMode (Vst::IoMode) override { return kNotImplemented; }
tresult PLUGIN_API getRoutingInfo (Vst::RoutingInfo&, Vst::RoutingInfo&) override { return kNotImplemented; } tresult PLUGIN_API getRoutingInfo (Vst::RoutingInfo&, Vst::RoutingInfo&) override { return kNotImplemented; }
bool isBypassed() { return comPluginInstance->isBypassed; }
void setBypassed (bool bypassed) { comPluginInstance->isBypassed = bypassed; }
//==============================================================================
bool isBypassed()
{
if (auto* bypassParam = comPluginInstance->getBypassParameter())
return (bypassParam->getValue() != 0.0f);
return false;
}
void setBypassed (bool shouldBeBypassed)
{
if (auto* bypassParam = comPluginInstance->getBypassParameter())
{
auto floatValue = (shouldBeBypassed ? 1.0f : 0.0f);
bypassParam->setValue (floatValue);
inParameterChangedCallback = true;
bypassParam->sendValueChangedMessageToListeners (floatValue);
}
}
//============================================================================== //==============================================================================
void writeJucePrivateStateInformation (MemoryOutputStream& out) void writeJucePrivateStateInformation (MemoryOutputStream& out)
{ {
ValueTree privateData (kJucePrivateDataIdentifier);
// for now we only store the bypass value
privateData.setProperty ("Bypass", var (isBypassed()), nullptr);
if (pluginInstance->getBypassParameter() == nullptr)
{
ValueTree privateData (kJucePrivateDataIdentifier);
privateData.writeToStream (out);
// for now we only store the bypass value
privateData.setProperty ("Bypass", var (isBypassed()), nullptr);
privateData.writeToStream (out);
}
} }
void setJucePrivateStateInformation (const void* data, int sizeInBytes) void setJucePrivateStateInformation (const void* data, int sizeInBytes)
{ {
auto privateData = ValueTree::readFromData (data, static_cast<size_t> (sizeInBytes));
setBypassed (static_cast<bool> (privateData.getProperty ("Bypass", var (false))));
if (pluginInstance->getBypassParameter() == nullptr)
{
auto privateData = ValueTree::readFromData (data, static_cast<size_t> (sizeInBytes));
auto isBypassed = static_cast<bool> (privateData.getProperty ("Bypass", var (false)));
if (auto* bypassParam = comPluginInstance->getBypassParameter())
setBypassed (isBypassed ? 1.0f : 0.0f);
}
} }
void getStateInformation (MemoryBlock& destData) void getStateInformation (MemoryBlock& destData)
@@ -2040,13 +2048,7 @@ public:
{ {
auto vstParamID = paramQueue->getParameterId(); auto vstParamID = paramQueue->getParameterId();
if (vstParamID == comPluginInstance->bypassParamID)
setBypassed (static_cast<float> (value) != 0.0f);
#if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS
else if (juceVST3EditController->isMidiControllerParamID (vstParamID))
addParameterChangeToMidiBuffer (offsetSamples, vstParamID, value);
#endif
else if (vstParamID == JuceVST3EditController::paramPreset)
if (vstParamID == JuceVST3EditController::paramPreset)
{ {
auto numPrograms = pluginInstance->getNumPrograms(); auto numPrograms = pluginInstance->getNumPrograms();
auto programValue = roundToInt (value * (jmax (0, numPrograms - 1))); auto programValue = roundToInt (value * (jmax (0, numPrograms - 1)));
@@ -2055,6 +2057,10 @@ public:
&& programValue != pluginInstance->getCurrentProgram()) && programValue != pluginInstance->getCurrentProgram())
pluginInstance->setCurrentProgram (programValue); pluginInstance->setCurrentProgram (programValue);
} }
#if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS
else if (juceVST3EditController->isMidiControllerParamID (vstParamID))
addParameterChangeToMidiBuffer (offsetSamples, vstParamID, value);
#endif
else else
{ {
auto floatValue = static_cast<float> (value); auto floatValue = static_cast<float> (value);


+ 147
- 2
modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm View File

@@ -1010,10 +1010,20 @@ public:
for (int i = 0; i < getBusCount (false); ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Output, static_cast<UInt32> (i)); for (int i = 0; i < getBusCount (false); ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Output, static_cast<UInt32> (i));
} }
void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
void processAudio (AudioBuffer<float>& buffer, MidiBuffer& midiMessages, bool processBlockBypassedCalled)
{ {
auto numSamples = buffer.getNumSamples(); auto numSamples = buffer.getNumSamples();
if (auSupportsBypass)
{
updateBypass (processBlockBypassedCalled);
}
else if (processBlockBypassedCalled)
{
AudioProcessor::processBlockBypassed (buffer, midiMessages);
return;
}
if (prepared) if (prepared)
{ {
timeStamp.mHostTime = GetCurrentHostTime (numSamples, getSampleRate(), isAUv3); timeStamp.mHostTime = GetCurrentHostTime (numSamples, getSampleRate(), isAUv3);
@@ -1111,7 +1121,18 @@ public:
} }
} }
void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
{
processAudio (buffer, midiMessages, false);
}
void processBlockBypassed (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
{
processAudio (buffer, midiMessages, true);
}
//============================================================================== //==============================================================================
AudioProcessorParameter* getBypassParameter() const override { return auSupportsBypass ? bypassParam.get() : nullptr; }
bool hasEditor() const override { return true; } bool hasEditor() const override { return true; }
AudioProcessorEditor* createEditor() override; AudioProcessorEditor* createEditor() override;
@@ -1408,6 +1429,14 @@ public:
} }
} }
} }
UInt32 propertySize = 0;
Boolean writable = false;
auSupportsBypass = (AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_BypassEffect,
kAudioUnitScope_Global, 0, &propertySize, &writable) == noErr
&& propertySize >= sizeof (UInt32) && writable);
bypassParam = new AUBypassParameter (*this);
} }
void updateLatency() void updateLatency()
@@ -1439,7 +1468,7 @@ private:
String pluginName, manufacturer, version; String pluginName, manufacturer, version;
String fileOrIdentifier; String fileOrIdentifier;
CriticalSection lock; CriticalSection lock;
bool wantsMidiMessages, producesMidiMessages, wasPlaying, prepared, isAUv3, isMidiEffectPlugin;
bool wantsMidiMessages, producesMidiMessages, wasPlaying, prepared, isAUv3, isMidiEffectPlugin, lastBypassValue = false;
struct AUBuffer struct AUBuffer
{ {
@@ -1459,6 +1488,99 @@ private:
HeapBlock<AudioBufferList> bufferList; HeapBlock<AudioBufferList> bufferList;
}; };
//==============================================================================
struct AUBypassParameter : Parameter
{
AUBypassParameter (AudioUnitPluginInstance& effectToUse)
: parent (effectToUse), currentValue (getCurrentHostValue())
{}
bool getCurrentHostValue()
{
if (parent.auSupportsBypass)
{
UInt32 dataSize = sizeof (UInt32);
UInt32 value = 0;
if (AudioUnitGetProperty (parent.audioUnit, kAudioUnitProperty_BypassEffect,
kAudioUnitScope_Global, 0, &value, &dataSize) == noErr
&& dataSize == sizeof (UInt32))
return (value != 0);
}
return false;
}
float getValue() const override
{
return currentValue ? 1.0f : 0.0f;
}
void setValue (float newValue) override
{
auto newBypassValue = (newValue != 0.0f);
const ScopedLock sl (parent.lock);
if (newBypassValue != currentValue)
{
currentValue = newBypassValue;
if (parent.auSupportsBypass)
{
UInt32 value = (newValue != 0.0f ? 1 : 0);
AudioUnitSetProperty (parent.audioUnit, kAudioUnitProperty_BypassEffect,
kAudioUnitScope_Global, 0, &value, sizeof (UInt32));
#if JUCE_MAC
jassert (parent.audioUnit != nullptr);
AudioUnitEvent ev;
ev.mEventType = kAudioUnitEvent_PropertyChange;
ev.mArgument.mProperty.mAudioUnit = parent.audioUnit;
ev.mArgument.mProperty.mPropertyID = kAudioUnitProperty_BypassEffect;
ev.mArgument.mProperty.mScope = kAudioUnitScope_Global;
ev.mArgument.mProperty.mElement = 0;
AUEventListenerNotify (parent.eventListenerRef, nullptr, &ev);
#endif
}
}
}
float getValueForText (const String& text) const override
{
String lowercaseText (text.toLowerCase());
for (auto& testText : onStrings)
if (lowercaseText == testText)
return 1.0f;
for (auto& testText : offStrings)
if (lowercaseText == testText)
return 0.0f;
return text.getIntValue() != 0 ? 1.0f : 0.0f;
}
float getDefaultValue() const override { return 0.0f; }
String getName (int /*maximumStringLength*/) const override { return "Bypass"; }
String getText (float value, int) const override { return (value != 0.0f ? TRANS("On") : TRANS("Off")); }
bool isAutomatable() const override { return true; }
bool isDiscrete() const override { return true; }
bool isBoolean() const override { return true; }
int getNumSteps() const override { return 2; }
StringArray getAllValueStrings() const override { return values; }
String getLabel() const override { return {}; }
AudioUnitPluginInstance& parent;
const StringArray onStrings { TRANS("on"), TRANS("yes"), TRANS("true") };
const StringArray offStrings { TRANS("off"), TRANS("no"), TRANS("false") };
const StringArray values { TRANS("Off"), TRANS("On") };
bool currentValue = false;
};
OwnedArray<AUBuffer> outputBufferList; OwnedArray<AUBuffer> outputBufferList;
AudioTimeStamp timeStamp; AudioTimeStamp timeStamp;
AudioBuffer<float>* currentBuffer; AudioBuffer<float>* currentBuffer;
@@ -1477,6 +1599,8 @@ private:
MidiDataConcatenator midiConcatenator; MidiDataConcatenator midiConcatenator;
CriticalSection midiInLock; CriticalSection midiInLock;
MidiBuffer incomingMidi; MidiBuffer incomingMidi;
ScopedPointer<AUBypassParameter> bypassParam;
bool lastProcessBlockCallWasBypass = false, auSupportsBypass = false;
void createPluginCallbacks() void createPluginCallbacks()
{ {
@@ -1536,6 +1660,7 @@ private:
addPropertyChangeListener (kAudioUnitProperty_PresentPreset); addPropertyChangeListener (kAudioUnitProperty_PresentPreset);
addPropertyChangeListener (kAudioUnitProperty_ParameterList); addPropertyChangeListener (kAudioUnitProperty_ParameterList);
addPropertyChangeListener (kAudioUnitProperty_Latency); addPropertyChangeListener (kAudioUnitProperty_Latency);
addPropertyChangeListener (kAudioUnitProperty_BypassEffect);
#endif #endif
} }
} }
@@ -1607,6 +1732,9 @@ private:
sendAllParametersChangedEvents(); sendAllParametersChangedEvents();
else if (event.mArgument.mProperty.mPropertyID == kAudioUnitProperty_Latency) else if (event.mArgument.mProperty.mPropertyID == kAudioUnitProperty_Latency)
updateLatency(); updateLatency();
else if (event.mArgument.mProperty.mPropertyID == kAudioUnitProperty_BypassEffect)
if (bypassParam != nullptr)
bypassParam->setValueNotifyingHost (bypassParam->getValue());
break; break;
} }
@@ -2038,6 +2166,23 @@ private:
return false; return false;
} }
//==============================================================================
void updateBypass (bool processBlockBypassedCalled)
{
if (processBlockBypassedCalled && bypassParam != nullptr)
{
if (bypassParam->getValue() == 0.0f || ! lastProcessBlockCallWasBypass)
bypassParam->setValue (1.0f);
}
else
{
if (lastProcessBlockCallWasBypass && bypassParam != nullptr)
bypassParam->setValue (0.0f);
}
lastProcessBlockCallWasBypass = processBlockBypassedCalled;
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginInstance) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginInstance)
}; };


+ 76
- 12
modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp View File

@@ -1883,14 +1883,18 @@ struct VST3PluginInstance : public AudioPluginInstance
int numSteps = isDiscrete ? paramInfo.stepCount + 1 int numSteps = isDiscrete ? paramInfo.stepCount + 1
: AudioProcessor::getDefaultNumParameterSteps(); : AudioProcessor::getDefaultNumParameterSteps();
addParameter (new VST3Parameter (*this,
paramInfo.id,
toString (paramInfo.title),
toString (paramInfo.units),
paramInfo.defaultNormalizedValue,
(paramInfo.flags & Vst::ParameterInfo::kCanAutomate) != 0,
isDiscrete,
numSteps));
VST3Parameter* p = new VST3Parameter (*this,
paramInfo.id,
toString (paramInfo.title),
toString (paramInfo.units),
paramInfo.defaultNormalizedValue,
(paramInfo.flags & Vst::ParameterInfo::kCanAutomate) != 0,
isDiscrete,
numSteps);
addParameter (p);
if ((paramInfo.flags & Vst::ParameterInfo::kIsBypass) != 0)
bypassParam = p;
} }
synchroniseStates(); synchroniseStates();
@@ -2015,12 +2019,13 @@ struct VST3PluginInstance : public AudioPluginInstance
return (processor->canProcessSampleSize (Vst::kSample64) == kResultTrue); return (processor->canProcessSampleSize (Vst::kSample64) == kResultTrue);
} }
//==============================================================================
void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
{ {
jassert (! isUsingDoublePrecision()); jassert (! isUsingDoublePrecision());
if (isActive && processor != nullptr) if (isActive && processor != nullptr)
processAudio (buffer, midiMessages, Vst::kSample32);
processAudio (buffer, midiMessages, Vst::kSample32, false);
} }
void processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages) override void processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages) override
@@ -2028,12 +2033,43 @@ struct VST3PluginInstance : public AudioPluginInstance
jassert (isUsingDoublePrecision()); jassert (isUsingDoublePrecision());
if (isActive && processor != nullptr) if (isActive && processor != nullptr)
processAudio (buffer, midiMessages, Vst::kSample64);
processAudio (buffer, midiMessages, Vst::kSample64, false);
}
void processBlockBypassed (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
{
jassert (! isUsingDoublePrecision());
if (bypassParam != nullptr)
{
if (isActive && processor != nullptr)
processAudio (buffer, midiMessages, Vst::kSample32, true);
}
else
{
AudioProcessor::processBlockBypassed (buffer, midiMessages);
}
}
void processBlockBypassed (AudioBuffer<double>& buffer, MidiBuffer& midiMessages) override
{
jassert (isUsingDoublePrecision());
if (bypassParam != nullptr)
{
if (isActive && processor != nullptr)
processAudio (buffer, midiMessages, Vst::kSample64, true);
}
else
{
AudioProcessor::processBlockBypassed (buffer, midiMessages);
}
} }
//==============================================================================
template <typename FloatType> template <typename FloatType>
void processAudio (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages, void processAudio (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages,
Vst::SymbolicSampleSizes sampleSize)
Vst::SymbolicSampleSizes sampleSize, bool isProcessBlockBypassedCall)
{ {
using namespace Vst; using namespace Vst;
auto numSamples = buffer.getNumSamples(); auto numSamples = buffer.getNumSamples();
@@ -2041,6 +2077,8 @@ struct VST3PluginInstance : public AudioPluginInstance
auto numInputAudioBuses = getBusCount (true); auto numInputAudioBuses = getBusCount (true);
auto numOutputAudioBuses = getBusCount (false); auto numOutputAudioBuses = getBusCount (false);
updateBypass (isProcessBlockBypassedCall);
ProcessData data; ProcessData data;
data.processMode = isNonRealtime() ? kOffline : kRealtime; data.processMode = isNonRealtime() ? kOffline : kRealtime;
data.symbolicSampleSize = sampleSize; data.symbolicSampleSize = sampleSize;
@@ -2266,6 +2304,9 @@ struct VST3PluginInstance : public AudioPluginInstance
bool acceptsMidi() const override { return getNumSingleDirectionBusesFor (holder->component, true, false) > 0; } bool acceptsMidi() const override { return getNumSingleDirectionBusesFor (holder->component, true, false) > 0; }
bool producesMidi() const override { return getNumSingleDirectionBusesFor (holder->component, false, false) > 0; } bool producesMidi() const override { return getNumSingleDirectionBusesFor (holder->component, false, false) > 0; }
//==============================================================================
AudioProcessorParameter* getBypassParameter() const override { return bypassParam; }
//============================================================================== //==============================================================================
/** May return a negative value as a means of informing us that the plugin has "infinite tail," or 0 for "no tail." */ /** May return a negative value as a means of informing us that the plugin has "infinite tail," or 0 for "no tail." */
double getTailLengthSeconds() const override double getTailLengthSeconds() const override
@@ -2588,7 +2629,8 @@ private:
ComSmartPtr<ParamValueQueueList> inputParameterChanges, outputParameterChanges; ComSmartPtr<ParamValueQueueList> inputParameterChanges, outputParameterChanges;
ComSmartPtr<MidiEventList> midiInputs, midiOutputs; ComSmartPtr<MidiEventList> midiInputs, midiOutputs;
Vst::ProcessContext timingInfo; //< Only use this in processBlock()! Vst::ProcessContext timingInfo; //< Only use this in processBlock()!
bool isControllerInitialised = false, isActive = false;
bool isControllerInitialised = false, isActive = false, lastProcessBlockCallWasBypass = false;
VST3Parameter* bypassParam = nullptr;
//============================================================================== //==============================================================================
/** Some plugins need to be "connected" to intercommunicate between their implemented classes */ /** Some plugins need to be "connected" to intercommunicate between their implemented classes */
@@ -2705,6 +2747,28 @@ private:
return busInfo; return busInfo;
} }
//==============================================================================
void updateBypass (bool processBlockBypassedCalled)
{
// to remain backward compatible, the logic needs to be the following:
// - if processBlockBypassed was called then definitely bypass the VST3
// - if processBlock was called then only un-bypass the VST3 if the previous
// call was processBlockBypassed, otherwise do nothing
if (processBlockBypassedCalled)
{
if (bypassParam != nullptr && (bypassParam->getValue() == 0.0f || ! lastProcessBlockCallWasBypass))
bypassParam->setValue (1.0f);
}
else
{
if (lastProcessBlockCallWasBypass && bypassParam != nullptr)
bypassParam->setValue (0.0f);
}
lastProcessBlockCallWasBypass = processBlockBypassedCalled;
}
//============================================================================== //==============================================================================
/** @note An IPlugView, when first created, should start with a ref-count of 1! */ /** @note An IPlugView, when first created, should start with a ref-count of 1! */
IPlugView* tryCreatingView() const IPlugView* tryCreatingView() const


+ 100
- 4
modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp View File

@@ -979,7 +979,8 @@ struct VSTPluginInstance : public AudioPluginInstance,
: AudioPluginInstance (ioConfig), : AudioPluginInstance (ioConfig),
vstEffect (effect), vstEffect (effect),
vstModule (mh), vstModule (mh),
name (mh->pluginName)
name (mh->pluginName),
bypassParam (new VST2BypassParameter (*this))
{ {
jassert (vstEffect != nullptr); jassert (vstEffect != nullptr);
@@ -1063,6 +1064,7 @@ struct VSTPluginInstance : public AudioPluginInstance,
valueType)); valueType));
} }
vstSupportsBypass = pluginCanDo ("bypass");
setRateAndBufferSizeDetails (sampleRateToUse, blockSizeToUse); setRateAndBufferSizeDetails (sampleRateToUse, blockSizeToUse);
} }
@@ -1399,24 +1401,40 @@ struct VSTPluginInstance : public AudioPluginInstance,
} }
} }
//==============================================================================
void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
{ {
jassert (! isUsingDoublePrecision()); jassert (! isUsingDoublePrecision());
processAudio (buffer, midiMessages, tmpBufferFloat, channelBufferFloat);
processAudio (buffer, midiMessages, tmpBufferFloat, channelBufferFloat, false);
} }
void processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages) override void processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages) override
{ {
jassert (isUsingDoublePrecision()); jassert (isUsingDoublePrecision());
processAudio (buffer, midiMessages, tmpBufferDouble, channelBufferDouble);
processAudio (buffer, midiMessages, tmpBufferDouble, channelBufferDouble, false);
}
void processBlockBypassed (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
{
jassert (! isUsingDoublePrecision());
processAudio (buffer, midiMessages, tmpBufferFloat, channelBufferFloat, true);
}
void processBlockBypassed (AudioBuffer<double>& buffer, MidiBuffer& midiMessages) override
{
jassert (isUsingDoublePrecision());
processAudio (buffer, midiMessages, tmpBufferDouble, channelBufferDouble, true);
} }
//==============================================================================
bool supportsDoublePrecisionProcessing() const override bool supportsDoublePrecisionProcessing() const override
{ {
return ((vstEffect->flags & vstEffectFlagInplaceAudio) != 0 return ((vstEffect->flags & vstEffectFlagInplaceAudio) != 0
&& (vstEffect->flags & vstEffectFlagInplaceDoubleAudio) != 0); && (vstEffect->flags & vstEffectFlagInplaceDoubleAudio) != 0);
} }
AudioProcessorParameter* getBypassParameter() const override { return vstSupportsBypass ? bypassParam.get() : nullptr; }
//============================================================================== //==============================================================================
bool canAddBus (bool) const override { return false; } bool canAddBus (bool) const override { return false; }
bool canRemoveBus (bool) const override { return false; } bool canRemoveBus (bool) const override { return false; }
@@ -1932,9 +1950,57 @@ struct VSTPluginInstance : public AudioPluginInstance,
bool usesCocoaNSView = false; bool usesCocoaNSView = false;
private: private:
//==============================================================================
struct VST2BypassParameter : Parameter
{
VST2BypassParameter (VSTPluginInstance& effectToUse) : parent (effectToUse) {}
void setValue (float newValue) override
{
currentValue = (newValue != 0.0f);
if (parent.vstSupportsBypass)
parent.dispatch (plugInOpcodeSetBypass, 0, currentValue ? 1 : 0, nullptr, 0.0f);
}
float getValueForText (const String& text) const override
{
String lowercaseText (text.toLowerCase());
for (auto& testText : onStrings)
if (lowercaseText == testText)
return 1.0f;
for (auto& testText : offStrings)
if (lowercaseText == testText)
return 0.0f;
return text.getIntValue() != 0 ? 1.0f : 0.0f;
}
float getValue() const override { return currentValue; }
float getDefaultValue() const override { return 0.0f; }
String getName (int /*maximumStringLength*/) const override { return "Bypass"; }
String getText (float value, int) const override { return (value != 0.0f ? TRANS("On") : TRANS("Off")); }
bool isAutomatable() const override { return true; }
bool isDiscrete() const override { return true; }
bool isBoolean() const override { return true; }
int getNumSteps() const override { return 2; }
StringArray getAllValueStrings() const override { return values; }
String getLabel() const override { return {}; }
VSTPluginInstance& parent;
bool currentValue = false;
StringArray onStrings { TRANS("on"), TRANS("yes"), TRANS("true") };
StringArray offStrings { TRANS("off"), TRANS("no"), TRANS("false") };
StringArray values { TRANS("Off"), TRANS("On") };
};
//==============================================================================
String name; String name;
CriticalSection lock; CriticalSection lock;
bool wantsMidiMessages = false, initialised = false, isPowerOn = false; bool wantsMidiMessages = false, initialised = false, isPowerOn = false;
bool lastProcessBlockCallWasBypass = false, vstSupportsBypass = false;
mutable StringArray programNames; mutable StringArray programNames;
AudioBuffer<float> outOfPlaceBuffer; AudioBuffer<float> outOfPlaceBuffer;
@@ -1948,6 +2014,7 @@ private:
AudioBuffer<double> tmpBufferDouble; AudioBuffer<double> tmpBufferDouble;
HeapBlock<double*> channelBufferDouble; HeapBlock<double*> channelBufferDouble;
ScopedPointer<VST2BypassParameter> bypassParam;
ScopedPointer<VSTXMLInfo> xmlInfo; ScopedPointer<VSTXMLInfo> xmlInfo;
@@ -2202,8 +2269,20 @@ private:
template <typename FloatType> template <typename FloatType>
void processAudio (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages, void processAudio (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages,
AudioBuffer<FloatType>& tmpBuffer, AudioBuffer<FloatType>& tmpBuffer,
HeapBlock<FloatType*>& channelBuffer)
HeapBlock<FloatType*>& channelBuffer,
bool processBlockBypassedCalled)
{ {
if (vstSupportsBypass)
{
updateBypass (processBlockBypassedCalled);
}
else if (processBlockBypassedCalled)
{
// if this vst does not support bypass then we will have to do this ourselves
AudioProcessor::processBlockBypassed (buffer, midiMessages);
return;
}
auto numSamples = buffer.getNumSamples(); auto numSamples = buffer.getNumSamples();
auto numChannels = buffer.getNumChannels(); auto numChannels = buffer.getNumChannels();
@@ -2588,6 +2667,23 @@ private:
isPowerOn = on; isPowerOn = on;
} }
//==============================================================================
void updateBypass (bool processBlockBypassedCalled)
{
if (processBlockBypassedCalled)
{
if (bypassParam->getValue() == 0.0f || ! lastProcessBlockCallWasBypass)
bypassParam->setValue (1.0f);
}
else
{
if (lastProcessBlockCallWasBypass)
bypassParam->setValue (0.0f);
}
lastProcessBlockCallWasBypass = processBlockBypassedCalled;
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginInstance) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginInstance)
}; };


+ 29
- 0
modules/juce_audio_processors/processors/juce_AudioProcessor.h View File

@@ -188,6 +188,13 @@ public:
be the processor's MIDI output. This means that your processor should be careful to be the processor's MIDI output. This means that your processor should be careful to
clear any incoming messages from the array if it doesn't want them to be passed-on. clear any incoming messages from the array if it doesn't want them to be passed-on.
If you have implemented the getBypassParameter method, then you need to check the
value of this parameter in this callback and bypass your processing if the parameter
has a non-zero value.
Note that when calling this method as a host, the result may still be bypassed as
the parameter that controls the bypass may be non-zero.
Be very careful about what you do in this callback - it's going to be called by Be very careful about what you do in this callback - it's going to be called by
the audio thread, so any kind of interaction with the UI is absolutely the audio thread, so any kind of interaction with the UI is absolutely
out of the question. If you change a parameter in here and need to tell your UI to out of the question. If you change a parameter in here and need to tell your UI to
@@ -252,6 +259,13 @@ public:
be the processor's MIDI output. This means that your processor should be careful to be the processor's MIDI output. This means that your processor should be careful to
clear any incoming messages from the array if it doesn't want them to be passed-on. clear any incoming messages from the array if it doesn't want them to be passed-on.
If you have implemented the getBypassParameter method, then you need to check the
value of this parameter in this callback and bypass your processing if the parameter
has a non-zero value.
Note that when calling this method as a host, the result may still be bypassed as
the parameter that controls the bypass may be non-zero.
Be very careful about what you do in this callback - it's going to be called by Be very careful about what you do in this callback - it's going to be called by
the audio thread, so any kind of interaction with the UI is absolutely the audio thread, so any kind of interaction with the UI is absolutely
out of the question. If you change a parameter in here and need to tell your UI to out of the question. If you change a parameter in here and need to tell your UI to
@@ -890,6 +904,21 @@ public:
*/ */
virtual void reset(); virtual void reset();
//==============================================================================
/** Returns the parameter that controls the AudioProcessor's bypass state.
If this method returns a nullptr then you can still control the bypass by
calling processBlockBypassed instaed of processBlock. On the other hand,
if this method returns a non-null value, you should never call
processBlockBypassed but use the returned parameter to conrol the bypass
state instead.
A plug-in can override this function to return a parameter which control's your
plug-in's bypass. You should always check the value of this parameter in your
processBlock callback and bypass any effects if it is non-zero.
*/
virtual AudioProcessorParameter* getBypassParameter() const { return nullptr; }
//============================================================================== //==============================================================================
/** Returns true if the processor is being run in an offline mode for rendering. /** Returns true if the processor is being run in an offline mode for rendering.


+ 43
- 4
modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp View File

@@ -270,12 +270,20 @@ private:
if (processor.isUsingDoublePrecision()) if (processor.isUsingDoublePrecision())
{ {
tempBufferDouble.makeCopyOf (buffer, true); tempBufferDouble.makeCopyOf (buffer, true);
processor.processBlock (tempBufferDouble, midiMessages);
if (node->isBypassed())
processor.processBlockBypassed (tempBufferDouble, midiMessages);
else
processor.processBlock (tempBufferDouble, midiMessages);
buffer.makeCopyOf (tempBufferDouble, true); buffer.makeCopyOf (tempBufferDouble, true);
} }
else else
{ {
processor.processBlock (buffer, midiMessages);
if (node->isBypassed())
processor.processBlockBypassed (buffer, midiMessages);
else
processor.processBlock (buffer, midiMessages);
} }
} }
@@ -283,12 +291,20 @@ private:
{ {
if (processor.isUsingDoublePrecision()) if (processor.isUsingDoublePrecision())
{ {
processor.processBlock (buffer, midiMessages);
if (node->isBypassed())
processor.processBlockBypassed (buffer, midiMessages);
else
processor.processBlock (buffer, midiMessages);
} }
else else
{ {
tempBufferFloat.makeCopyOf (buffer, true); tempBufferFloat.makeCopyOf (buffer, true);
processor.processBlock (tempBufferFloat, midiMessages);
if (node->isBypassed())
processor.processBlockBypassed (tempBufferFloat, midiMessages);
else
processor.processBlock (tempBufferFloat, midiMessages);
buffer.makeCopyOf (tempBufferFloat, true); buffer.makeCopyOf (tempBufferFloat, true);
} }
} }
@@ -826,6 +842,29 @@ bool AudioProcessorGraph::Node::Connection::operator== (const Connection& other)
&& otherChannel == other.otherChannel; && otherChannel == other.otherChannel;
} }
//==============================================================================
bool AudioProcessorGraph::Node::isBypassed() const noexcept
{
if (processor != nullptr)
{
if (auto* bypassParam = processor->getBypassParameter())
return (bypassParam->getValue() != 0.0f);
}
return bypassed;
}
void AudioProcessorGraph::Node::setBypassed (bool shouldBeBypassed) noexcept
{
if (processor != nullptr)
{
if (auto* bypassParam = processor->getBypassParameter())
bypassParam->setValueNotifyingHost (shouldBeBypassed ? 1.0f : 0.0f);
}
bypassed = shouldBeBypassed;
}
//============================================================================== //==============================================================================
struct AudioProcessorGraph::RenderSequenceFloat : public GraphRenderSequence<float> {}; struct AudioProcessorGraph::RenderSequenceFloat : public GraphRenderSequence<float> {};
struct AudioProcessorGraph::RenderSequenceDouble : public GraphRenderSequence<double> {}; struct AudioProcessorGraph::RenderSequenceDouble : public GraphRenderSequence<double> {};


+ 8
- 1
modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h View File

@@ -108,6 +108,13 @@ public:
*/ */
NamedValueSet properties; NamedValueSet properties;
//==============================================================================
/** Returns if the node is bypassed or not. */
bool isBypassed() const noexcept;
/** Tell this node to bypass processing. */
void setBypassed (bool shouldBeBypassed) noexcept;
//============================================================================== //==============================================================================
/** A convenient typedef for referring to a pointer to a node object. */ /** A convenient typedef for referring to a pointer to a node object. */
typedef ReferenceCountedObjectPtr<Node> Ptr; typedef ReferenceCountedObjectPtr<Node> Ptr;
@@ -126,7 +133,7 @@ public:
const ScopedPointer<AudioProcessor> processor; const ScopedPointer<AudioProcessor> processor;
Array<Connection> inputs, outputs; Array<Connection> inputs, outputs;
bool isPrepared = false;
bool isPrepared = false, bypassed = false;
Node (NodeID, AudioProcessor*) noexcept; Node (NodeID, AudioProcessor*) noexcept;


+ 4
- 3
modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp View File

@@ -503,8 +503,6 @@ struct GenericAudioProcessorEditor::Pimpl
owner.addAndMakeVisible (view); owner.addAndMakeVisible (view);
view.setScrollBarsShown (true, false); view.setScrollBarsShown (true, false);
owner.setSize (view.getViewedComponent()->getWidth() + view.getVerticalScrollBar().getWidth(),
jmin (view.getViewedComponent()->getHeight(), 400));
} }
@@ -520,7 +518,10 @@ struct GenericAudioProcessorEditor::Pimpl
//============================================================================== //==============================================================================
GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor* const p) GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor* const p)
: AudioProcessorEditor (p), pimpl (new Pimpl (*this)) : AudioProcessorEditor (p), pimpl (new Pimpl (*this))
{}
{
setSize (pimpl->view.getViewedComponent()->getWidth() + pimpl->view.getVerticalScrollBar().getWidth(),
jmin (pimpl->view.getViewedComponent()->getHeight(), 400));
}
GenericAudioProcessorEditor::~GenericAudioProcessorEditor() {} GenericAudioProcessorEditor::~GenericAudioProcessorEditor() {}


Loading…
Cancel
Save