Browse Source

Audio plugins: better bypass support, with an AudioProcessor::processBlockBypassed() method that can be used for custom behaviour.

tags/2021-05-28
jules 13 years ago
parent
commit
f903695ba3
6 changed files with 95 additions and 96 deletions
  1. +41
    -59
      modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp
  2. +5
    -1
      modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm
  3. +17
    -30
      modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp
  4. +19
    -3
      modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
  5. +2
    -3
      modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
  6. +11
    -0
      modules/juce_audio_processors/processors/juce_AudioProcessor.h

+ 41
- 59
modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp View File

@@ -65,7 +65,7 @@ struct AAXClasses
{ {
static void check (AAX_Result result) static void check (AAX_Result result)
{ {
jassert (result == AAX_SUCCESS);
jassert (result == AAX_SUCCESS); (void) result;
} }
struct FourCharConst struct FourCharConst
@@ -148,22 +148,19 @@ struct AAXClasses
//============================================================================== //==============================================================================
struct PluginInstanceInfo struct PluginInstanceInfo
{ {
PluginInstanceInfo (AudioProcessor* pluginInstance_)
: pluginInstance (pluginInstance_)
{
}
PluginInstanceInfo (AudioProcessor& p) : pluginInstance (p) {}
void process (const float* const* inputs, float* const* outputs, const int bufferSize)
void process (const float* const* inputs, float* const* outputs, const int bufferSize, const bool bypass)
{ {
const int numIns = pluginInstance->getNumInputChannels();
const int numOuts = pluginInstance->getNumOutputChannels();
const int numIns = pluginInstance.getNumInputChannels();
const int numOuts = pluginInstance.getNumOutputChannels();
if (numOuts >= numIns) if (numOuts >= numIns)
{ {
for (int i = 0; i < numIns; ++i) for (int i = 0; i < numIns; ++i)
memcpy (outputs[i], inputs[i], bufferSize * sizeof (float)); memcpy (outputs[i], inputs[i], bufferSize * sizeof (float));
process (outputs, numOuts, bufferSize);
process (outputs, numOuts, bufferSize, bypass);
} }
else else
{ {
@@ -181,11 +178,11 @@ struct AAXClasses
for (int i = numOuts; i < numIns; ++i) for (int i = numOuts; i < numIns; ++i)
channels[i] = const_cast <float*> (inputs[i]); channels[i] = const_cast <float*> (inputs[i]);
process (channels, numIns, bufferSize);
process (channels, numIns, bufferSize, bypass);
} }
} }
void process (float* const* channels, const int numChans, const int bufferSize)
void process (float* const* channels, const int numChans, const int bufferSize, const bool bypass)
{ {
AudioSampleBuffer buffer (channels, numChans, bufferSize); AudioSampleBuffer buffer (channels, numChans, bufferSize);
@@ -193,26 +190,16 @@ struct AAXClasses
midiBuffer.clear(); midiBuffer.clear();
{ {
const ScopedLock sl (pluginInstance->getCallbackLock());
pluginInstance->processBlock (buffer, midiBuffer);
}
}
const ScopedLock sl (pluginInstance.getCallbackLock());
void bypass (float* const* inputs, float* const* outputs, int bufferSize)
{
const int numIns = pluginInstance->getNumInputChannels();
const int numOuts = pluginInstance->getNumOutputChannels();
for (int i = 0; i < numOuts; ++i)
{
if (i < numIns)
memcpy (outputs[i], inputs[i], sizeof (float) * bufferSize);
if (bypass)
pluginInstance.processBlockBypassed (buffer, midiBuffer);
else else
zeromem (outputs[i], sizeof (float) * bufferSize);
pluginInstance.processBlock (buffer, midiBuffer);
} }
} }
AudioProcessor* pluginInstance;
AudioProcessor& pluginInstance;
MidiBuffer midiBuffer; MidiBuffer midiBuffer;
Array<float*> channelList; Array<float*> channelList;
@@ -257,20 +244,18 @@ struct AAXClasses
{ {
if (component == nullptr) if (component == nullptr)
{ {
JuceAAX_Parameters* params = dynamic_cast <JuceAAX_Parameters*> (GetEffectParameters());
jassert (params != nullptr);
if (params != nullptr)
if (JuceAAX_Parameters* params = dynamic_cast <JuceAAX_Parameters*> (GetEffectParameters()))
component = new ContentWrapperComponent (*this, params->getPluginInstance()); component = new ContentWrapperComponent (*this, params->getPluginInstance());
else
jassertfalse;
} }
} }
void CreateViewContainer() void CreateViewContainer()
{ {
CreateViewContents(); CreateViewContents();
void* nativeViewToAttachTo = GetViewContainerPtr();
if (nativeViewToAttachTo != nullptr)
if (void* nativeViewToAttachTo = GetViewContainerPtr())
{ {
#if JUCE_MAC #if JUCE_MAC
if (GetViewContainerType() == AAX_eViewContainer_Type_NSView) if (GetViewContainerType() == AAX_eViewContainer_Type_NSView)
@@ -322,11 +307,11 @@ struct AAXClasses
class ContentWrapperComponent : public juce::Component class ContentWrapperComponent : public juce::Component
{ {
public: public:
ContentWrapperComponent (JuceAAX_GUI& gui, AudioProcessor* plugin)
ContentWrapperComponent (JuceAAX_GUI& gui, AudioProcessor& plugin)
: owner (gui) : owner (gui)
{ {
setOpaque (true); setOpaque (true);
addAndMakeVisible (pluginEditor = plugin->createEditorIfNeeded());
addAndMakeVisible (pluginEditor = plugin.createEditorIfNeeded());
setBounds (pluginEditor->getLocalBounds()); setBounds (pluginEditor->getLocalBounds());
setBroughtToFrontOnMouseClick (true); setBroughtToFrontOnMouseClick (true);
} }
@@ -378,9 +363,9 @@ struct AAXClasses
JuceAAX_Parameters() JuceAAX_Parameters()
{ {
pluginInstance = createPluginFilter(); pluginInstance = createPluginFilter();
jassert (pluginInstance != nullptr); // your createPluginFilter() method must return an object!
if (pluginInstance != nullptr)
pluginInstance->wrapperType = AudioProcessor::wrapperType_AAX;
pluginInstance->wrapperType = AudioProcessor::wrapperType_AAX;
} }
static AAX_CEffectParameters* AAX_CALLBACK Create() { return new JuceAAX_Parameters(); } static AAX_CEffectParameters* AAX_CALLBACK Create() { return new JuceAAX_Parameters(); }
@@ -408,7 +393,7 @@ struct AAXClasses
jassert (numObjects == 1); // not sure how to handle more than one.. jassert (numObjects == 1); // not sure how to handle more than one..
for (size_t i = 0; i < numObjects; ++i) for (size_t i = 0; i < numObjects; ++i)
new (objects + i) PluginInstanceInfo (pluginInstance);
new (objects + i) PluginInstanceInfo (*pluginInstance);
break; break;
} }
@@ -431,7 +416,7 @@ struct AAXClasses
//return AAX_ERROR_INVALID_FIELD_INDEX; //return AAX_ERROR_INVALID_FIELD_INDEX;
} }
AudioProcessor* getPluginInstance() const noexcept { return pluginInstance; }
AudioProcessor& getPluginInstance() const noexcept { return *pluginInstance; }
private: private:
void addBypassParameter() void addBypassParameter()
@@ -467,9 +452,9 @@ struct AAXClasses
int32_t bufferSize = 0; int32_t bufferSize = 0;
check (Controller()->GetSignalLatency (&bufferSize)); check (Controller()->GetSignalLatency (&bufferSize));
AudioProcessor* audioProcessor = getPluginInstance();
audioProcessor->setPlayConfigDetails (numberOfInputChannels, numberOfOutputChannels, sampleRate, bufferSize);
audioProcessor->prepareToPlay (sampleRate, bufferSize);
AudioProcessor& audioProcessor = getPluginInstance();
audioProcessor.setPlayConfigDetails (numberOfInputChannels, numberOfOutputChannels, sampleRate, bufferSize);
audioProcessor.prepareToPlay (sampleRate, bufferSize);
} }
JUCELibraryRefCount juceCount; JUCELibraryRefCount juceCount;
@@ -487,10 +472,8 @@ struct AAXClasses
{ {
const JUCEAlgorithmContext& i = **iter; const JUCEAlgorithmContext& i = **iter;
if (*(i.bypass) != 0)
i.pluginInstance->bypass (i.inputChannels, i.outputChannels, *(i.bufferSize));
else
i.pluginInstance->process (i.inputChannels, i.outputChannels, *(i.bufferSize));
i.pluginInstance->process (i.inputChannels, i.outputChannels,
*(i.bufferSize), *(i.bypass) != 0);
} }
} }
@@ -544,9 +527,7 @@ struct AAXClasses
for (int i = 0; i < numConfigs; ++i) for (int i = 0; i < numConfigs; ++i)
{ {
AAX_IComponentDescriptor* const desc = descriptor.NewComponentDescriptor();
if (desc != nullptr)
if (AAX_IComponentDescriptor* const desc = descriptor.NewComponentDescriptor())
{ {
createDescriptor (*desc, createDescriptor (*desc,
channelConfigs [i][0], channelConfigs [i][0],
@@ -563,20 +544,21 @@ AAX_Result JUCE_CDECL GetEffectDescriptions (AAX_ICollection* collection)
{ {
AAXClasses::JUCELibraryRefCount libraryRefCount; AAXClasses::JUCELibraryRefCount libraryRefCount;
AAX_IEffectDescriptor* const descriptor = collection->NewDescriptor();
if (descriptor == nullptr)
return AAX_ERROR_NULL_OBJECT;
if (AAX_IEffectDescriptor* const descriptor = collection->NewDescriptor())
{
AAXClasses::getPlugInDescription (*descriptor);
collection->AddEffect (JUCE_STRINGIFY (JucePlugin_AAXIdentifier), descriptor);
AAXClasses::getPlugInDescription (*descriptor);
collection->AddEffect (JUCE_STRINGIFY (JucePlugin_AAXIdentifier), descriptor);
collection->SetManufacturerName (JucePlugin_Manufacturer);
collection->AddPackageName (JucePlugin_Desc);
collection->AddPackageName (JucePlugin_Name);
collection->AddPackageName (AAXClasses::FourCharConst (JucePlugin_PluginCode).asString);
collection->SetPackageVersion (JucePlugin_VersionCode);
collection->SetManufacturerName (JucePlugin_Manufacturer);
collection->AddPackageName (JucePlugin_Desc);
collection->AddPackageName (JucePlugin_Name);
collection->AddPackageName (AAXClasses::FourCharConst (JucePlugin_PluginCode).asString);
collection->SetPackageVersion (JucePlugin_VersionCode);
return AAX_SUCCESS;
}
return AAX_SUCCESS;
return AAX_ERROR_NULL_OBJECT;
} }
#endif #endif

+ 5
- 1
modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm View File

@@ -141,7 +141,7 @@ public:
} }
juceFilter = createPluginFilter(); juceFilter = createPluginFilter();
jassert (juceFilter != nullptr);
jassert (juceFilter != nullptr); // your createPluginFilter() method must return an object!
juceFilter->wrapperType = AudioProcessor::wrapperType_AudioUnit; juceFilter->wrapperType = AudioProcessor::wrapperType_AudioUnit;
@@ -781,6 +781,10 @@ public:
for (int j = 0; j < numOut; ++j) for (int j = 0; j < numOut; ++j)
zeromem (channels [j], sizeof (float) * numSamples); zeromem (channels [j], sizeof (float) * numSamples);
} }
else if (ShouldBypassEffect())
{
juceFilter->processBlockBypassed (buffer, midiEvents);
}
else else
{ {
juceFilter->processBlock (buffer, midiEvents); juceFilter->processBlock (buffer, midiEvents);


+ 17
- 30
modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp View File

@@ -156,7 +156,7 @@ public:
{ {
asyncUpdater = new InternalAsyncUpdater (*this); asyncUpdater = new InternalAsyncUpdater (*this);
juceFilter = createPluginFilter(); juceFilter = createPluginFilter();
jassert (juceFilter != nullptr);
jassert (juceFilter != nullptr); // your createPluginFilter() method must return an object!
juceFilter->wrapperType = AudioProcessor::wrapperType_RTAS; juceFilter->wrapperType = AudioProcessor::wrapperType_RTAS;
@@ -278,13 +278,8 @@ public:
#if JUCE_WINDOWS #if JUCE_WINDOWS
if (wrapper != nullptr) if (wrapper != nullptr)
{ {
ComponentPeer* const peer = wrapper->getPeer();
if (peer != nullptr)
{
// (seems to be required in PT6.4, but not in 7.x)
peer->repaint (wrapper->getLocalBounds());
}
if (ComponentPeer* const peer = wrapper->getPeer())
peer->repaint (wrapper->getLocalBounds()); // (seems to be required in PT6.4, but not in 7.x)
} }
#endif #endif
} }
@@ -300,13 +295,12 @@ public:
void deleteEditorComp() void deleteEditorComp()
{ {
if (editorComp != 0 || wrapper != nullptr)
if (editorComp != nullptr || wrapper != nullptr)
{ {
JUCE_AUTORELEASEPOOL JUCE_AUTORELEASEPOOL
PopupMenu::dismissAllActiveMenus(); PopupMenu::dismissAllActiveMenus();
juce::Component* const modalComponent = juce::Component::getCurrentlyModalComponent();
if (modalComponent != nullptr)
if (juce::Component* const modalComponent = juce::Component::getCurrentlyModalComponent())
modalComponent->exitModalState (0); modalComponent->exitModalState (0);
filter->editorBeingDeleted (editorComp); filter->editorBeingDeleted (editorComp);
@@ -372,9 +366,7 @@ public:
void resized() void resized()
{ {
juce::Component* const ed = getEditor();
if (ed != nullptr)
if (juce::Component* const ed = getEditor())
ed->setBounds (getLocalBounds()); ed->setBounds (getLocalBounds());
repaint(); repaint();
@@ -432,8 +424,8 @@ public:
void GetViewRect (Rect* size) void GetViewRect (Rect* size)
{ {
if (getView() != nullptr)
getView()->updateSize();
if (JuceCustomUIView* const v = getView())
v->updateSize();
CEffectProcessRTAS::GetViewRect (size); CEffectProcessRTAS::GetViewRect (size);
} }
@@ -447,8 +439,8 @@ public:
{ {
CEffectProcessRTAS::SetViewPort (port); CEffectProcessRTAS::SetViewPort (port);
if (getView() != nullptr)
getView()->attachToWindow (port);
if (JuceCustomUIView* const v = getView())
v->attachToWindow (port);
} }
//============================================================================== //==============================================================================
@@ -481,9 +473,7 @@ protected:
if (MIDILogIn() == noErr) if (MIDILogIn() == noErr)
{ {
#if JucePlugin_WantsMidiInput #if JucePlugin_WantsMidiInput
CEffectType* const type = dynamic_cast <CEffectType*> (this->GetProcessType());
if (type != nullptr)
if (CEffectType* const type = dynamic_cast <CEffectType*> (this->GetProcessType()))
{ {
char nodeName [64]; char nodeName [64];
type->GetProcessTypeName (63, nodeName); type->GetProcessTypeName (63, nodeName);
@@ -536,25 +526,19 @@ protected:
return; return;
} }
if (mBypassed)
{
bypassBuffers (inputs, outputs, numSamples);
return;
}
#if JucePlugin_WantsMidiInput #if JucePlugin_WantsMidiInput
midiEvents.clear(); midiEvents.clear();
const Cmn_UInt32 bufferSize = mRTGlobals->mHWBufferSizeInSamples; const Cmn_UInt32 bufferSize = mRTGlobals->mHWBufferSizeInSamples;
if (midiBufferNode != 0)
if (midiBufferNode != nullptr)
{ {
if (midiBufferNode->GetAdvanceScheduleTime() != bufferSize) if (midiBufferNode->GetAdvanceScheduleTime() != bufferSize)
midiBufferNode->SetAdvanceScheduleTime (bufferSize); midiBufferNode->SetAdvanceScheduleTime (bufferSize);
if (midiBufferNode->FillMIDIBuffer (mRTGlobals->mRunningTime, numSamples) == noErr) if (midiBufferNode->FillMIDIBuffer (mRTGlobals->mRunningTime, numSamples) == noErr)
{ {
jassert (midiBufferNode->GetBufferPtr() != 0);
jassert (midiBufferNode->GetBufferPtr() != nullptr);
const int numMidiEvents = midiBufferNode->GetBufferSize(); const int numMidiEvents = midiBufferNode->GetBufferSize();
for (int i = 0; i < numMidiEvents; ++i) for (int i = 0; i < numMidiEvents; ++i)
@@ -605,7 +589,10 @@ protected:
AudioSampleBuffer chans (channels, totalChans, numSamples); AudioSampleBuffer chans (channels, totalChans, numSamples);
juceFilter->processBlock (chans, midiEvents);
if (mBypassed)
juceFilter->processBlockBypassed (chans, midiEvents);
else
juceFilter->processBlock (chans, midiEvents);
} }
} }


+ 19
- 3
modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp View File

@@ -271,6 +271,7 @@ public:
numInChans (JucePlugin_MaxNumInputChannels), numInChans (JucePlugin_MaxNumInputChannels),
numOutChans (JucePlugin_MaxNumOutputChannels), numOutChans (JucePlugin_MaxNumOutputChannels),
isProcessing (false), isProcessing (false),
isBypassed (false),
hasShutdown (false), hasShutdown (false),
firstProcessCallback (true), firstProcessCallback (true),
shouldDeleteEditor (false), shouldDeleteEditor (false),
@@ -397,7 +398,8 @@ public:
#endif #endif
} }
else if (strcmp (text, "receiveVstTimeInfo") == 0 else if (strcmp (text, "receiveVstTimeInfo") == 0
|| strcmp (text, "conformsToWindowRules") == 0)
|| strcmp (text, "conformsToWindowRules") == 0
|| strcmp (text, "bypass") == 0)
{ {
result = 1; result = 1;
} }
@@ -452,6 +454,12 @@ public:
} }
} }
bool setBypass (bool b)
{
isBypassed = b;
return true;
}
//============================================================================== //==============================================================================
VstInt32 processEvents (VstEvents* events) VstInt32 processEvents (VstEvents* events)
{ {
@@ -556,7 +564,11 @@ public:
{ {
AudioSampleBuffer chans (channels, jmax (numIn, numOut), numSamples); AudioSampleBuffer chans (channels, jmax (numIn, numOut), numSamples);
filter->processBlock (chans, midiEvents);
if (isBypassed)
filter->processBlockBypassed (chans, midiEvents);
else
filter->processBlock (chans, midiEvents);
} }
// copy back any temp channels that may have been used.. // copy back any temp channels that may have been used..
@@ -1369,7 +1381,7 @@ private:
VSTMidiEventList outgoingEvents; VSTMidiEventList outgoingEvents;
VstSpeakerArrangementType speakerIn, speakerOut; VstSpeakerArrangementType speakerIn, speakerOut;
int numInChans, numOutChans; int numInChans, numOutChans;
bool isProcessing, hasShutdown, firstProcessCallback, shouldDeleteEditor;
bool isProcessing, isBypassed, hasShutdown, firstProcessCallback, shouldDeleteEditor;
HeapBlock<float*> channels; HeapBlock<float*> channels;
Array<float*> tempChannels; // see note in processReplacing() Array<float*> tempChannels; // see note in processReplacing()
@@ -1467,6 +1479,10 @@ namespace
JuceVSTWrapper* const wrapper = new JuceVSTWrapper (audioMaster, filter); JuceVSTWrapper* const wrapper = new JuceVSTWrapper (audioMaster, filter);
return wrapper->getAeffect(); return wrapper->getAeffect();
} }
else
{
jassertfalse; // your createPluginFilter() method must return an object!
}
} }
} }
catch (...) catch (...)


+ 2
- 3
modules/juce_audio_processors/processors/juce_AudioProcessor.cpp View File

@@ -207,9 +207,8 @@ void AudioProcessor::suspendProcessing (const bool shouldBeSuspended)
suspended = shouldBeSuspended; suspended = shouldBeSuspended;
} }
void AudioProcessor::reset()
{
}
void AudioProcessor::reset() {}
void AudioProcessor::processBlockBypassed (AudioSampleBuffer&, MidiBuffer&) {}
//============================================================================== //==============================================================================
void AudioProcessor::editorBeingDeleted (AudioProcessorEditor* const editor) noexcept void AudioProcessor::editorBeingDeleted (AudioProcessorEditor* const editor) noexcept


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

@@ -134,6 +134,17 @@ public:
virtual void processBlock (AudioSampleBuffer& buffer, virtual void processBlock (AudioSampleBuffer& buffer,
MidiBuffer& midiMessages) = 0; MidiBuffer& midiMessages) = 0;
/** Renders the next block when the processor is being bypassed.
The default implementation of this method will pass-through any incoming audio, but
you may override this method e.g. to add latency compensation to the data to match
the processor's latency characteristics. This will avoid situations where bypassing
will shift the signal forward in time, possibly creating pre-echo effects and odd timings.
Another use for this method would be to cross-fade or morph between the wet (not bypassed)
and dry (bypassed) signals.
*/
virtual void processBlockBypassed (AudioSampleBuffer& buffer,
MidiBuffer& midiMessages);
//============================================================================== //==============================================================================
/** Returns the current AudioPlayHead object that should be used to find /** Returns the current AudioPlayHead object that should be used to find
out the state and position of the playhead. out the state and position of the playhead.


Loading…
Cancel
Save