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)
{
jassert (result == AAX_SUCCESS);
jassert (result == AAX_SUCCESS); (void) result;
}
struct FourCharConst
@@ -148,22 +148,19 @@ struct AAXClasses
//==============================================================================
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)
{
for (int i = 0; i < numIns; ++i)
memcpy (outputs[i], inputs[i], bufferSize * sizeof (float));
process (outputs, numOuts, bufferSize);
process (outputs, numOuts, bufferSize, bypass);
}
else
{
@@ -181,11 +178,11 @@ struct AAXClasses
for (int i = numOuts; i < numIns; ++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);
@@ -193,26 +190,16 @@ struct AAXClasses
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
zeromem (outputs[i], sizeof (float) * bufferSize);
pluginInstance.processBlock (buffer, midiBuffer);
}
}
AudioProcessor* pluginInstance;
AudioProcessor& pluginInstance;
MidiBuffer midiBuffer;
Array<float*> channelList;
@@ -257,20 +244,18 @@ struct AAXClasses
{
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());
else
jassertfalse;
}
}
void CreateViewContainer()
{
CreateViewContents();
void* nativeViewToAttachTo = GetViewContainerPtr();
if (nativeViewToAttachTo != nullptr)
if (void* nativeViewToAttachTo = GetViewContainerPtr())
{
#if JUCE_MAC
if (GetViewContainerType() == AAX_eViewContainer_Type_NSView)
@@ -322,11 +307,11 @@ struct AAXClasses
class ContentWrapperComponent : public juce::Component
{
public:
ContentWrapperComponent (JuceAAX_GUI& gui, AudioProcessor* plugin)
ContentWrapperComponent (JuceAAX_GUI& gui, AudioProcessor& plugin)
: owner (gui)
{
setOpaque (true);
addAndMakeVisible (pluginEditor = plugin->createEditorIfNeeded());
addAndMakeVisible (pluginEditor = plugin.createEditorIfNeeded());
setBounds (pluginEditor->getLocalBounds());
setBroughtToFrontOnMouseClick (true);
}
@@ -378,9 +363,9 @@ struct AAXClasses
JuceAAX_Parameters()
{
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(); }
@@ -408,7 +393,7 @@ struct AAXClasses
jassert (numObjects == 1); // not sure how to handle more than one..
for (size_t i = 0; i < numObjects; ++i)
new (objects + i) PluginInstanceInfo (pluginInstance);
new (objects + i) PluginInstanceInfo (*pluginInstance);
break;
}
@@ -431,7 +416,7 @@ struct AAXClasses
//return AAX_ERROR_INVALID_FIELD_INDEX;
}
AudioProcessor* getPluginInstance() const noexcept { return pluginInstance; }
AudioProcessor& getPluginInstance() const noexcept { return *pluginInstance; }
private:
void addBypassParameter()
@@ -467,9 +452,9 @@ struct AAXClasses
int32_t bufferSize = 0;
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;
@@ -487,10 +472,8 @@ struct AAXClasses
{
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)
{
AAX_IComponentDescriptor* const desc = descriptor.NewComponentDescriptor();
if (desc != nullptr)
if (AAX_IComponentDescriptor* const desc = descriptor.NewComponentDescriptor())
{
createDescriptor (*desc,
channelConfigs [i][0],
@@ -563,20 +544,21 @@ AAX_Result JUCE_CDECL GetEffectDescriptions (AAX_ICollection* collection)
{
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

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

@@ -141,7 +141,7 @@ public:
}
juceFilter = createPluginFilter();
jassert (juceFilter != nullptr);
jassert (juceFilter != nullptr); // your createPluginFilter() method must return an object!
juceFilter->wrapperType = AudioProcessor::wrapperType_AudioUnit;
@@ -781,6 +781,10 @@ public:
for (int j = 0; j < numOut; ++j)
zeromem (channels [j], sizeof (float) * numSamples);
}
else if (ShouldBypassEffect())
{
juceFilter->processBlockBypassed (buffer, midiEvents);
}
else
{
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);
juceFilter = createPluginFilter();
jassert (juceFilter != nullptr);
jassert (juceFilter != nullptr); // your createPluginFilter() method must return an object!
juceFilter->wrapperType = AudioProcessor::wrapperType_RTAS;
@@ -278,13 +278,8 @@ public:
#if JUCE_WINDOWS
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
}
@@ -300,13 +295,12 @@ public:
void deleteEditorComp()
{
if (editorComp != 0 || wrapper != nullptr)
if (editorComp != nullptr || wrapper != nullptr)
{
JUCE_AUTORELEASEPOOL
PopupMenu::dismissAllActiveMenus();
juce::Component* const modalComponent = juce::Component::getCurrentlyModalComponent();
if (modalComponent != nullptr)
if (juce::Component* const modalComponent = juce::Component::getCurrentlyModalComponent())
modalComponent->exitModalState (0);
filter->editorBeingDeleted (editorComp);
@@ -372,9 +366,7 @@ public:
void resized()
{
juce::Component* const ed = getEditor();
if (ed != nullptr)
if (juce::Component* const ed = getEditor())
ed->setBounds (getLocalBounds());
repaint();
@@ -432,8 +424,8 @@ public:
void GetViewRect (Rect* size)
{
if (getView() != nullptr)
getView()->updateSize();
if (JuceCustomUIView* const v = getView())
v->updateSize();
CEffectProcessRTAS::GetViewRect (size);
}
@@ -447,8 +439,8 @@ public:
{
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 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];
type->GetProcessTypeName (63, nodeName);
@@ -536,25 +526,19 @@ protected:
return;
}
if (mBypassed)
{
bypassBuffers (inputs, outputs, numSamples);
return;
}
#if JucePlugin_WantsMidiInput
midiEvents.clear();
const Cmn_UInt32 bufferSize = mRTGlobals->mHWBufferSizeInSamples;
if (midiBufferNode != 0)
if (midiBufferNode != nullptr)
{
if (midiBufferNode->GetAdvanceScheduleTime() != bufferSize)
midiBufferNode->SetAdvanceScheduleTime (bufferSize);
if (midiBufferNode->FillMIDIBuffer (mRTGlobals->mRunningTime, numSamples) == noErr)
{
jassert (midiBufferNode->GetBufferPtr() != 0);
jassert (midiBufferNode->GetBufferPtr() != nullptr);
const int numMidiEvents = midiBufferNode->GetBufferSize();
for (int i = 0; i < numMidiEvents; ++i)
@@ -605,7 +589,10 @@ protected:
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),
numOutChans (JucePlugin_MaxNumOutputChannels),
isProcessing (false),
isBypassed (false),
hasShutdown (false),
firstProcessCallback (true),
shouldDeleteEditor (false),
@@ -397,7 +398,8 @@ public:
#endif
}
else if (strcmp (text, "receiveVstTimeInfo") == 0
|| strcmp (text, "conformsToWindowRules") == 0)
|| strcmp (text, "conformsToWindowRules") == 0
|| strcmp (text, "bypass") == 0)
{
result = 1;
}
@@ -452,6 +454,12 @@ public:
}
}
bool setBypass (bool b)
{
isBypassed = b;
return true;
}
//==============================================================================
VstInt32 processEvents (VstEvents* events)
{
@@ -556,7 +564,11 @@ public:
{
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..
@@ -1369,7 +1381,7 @@ private:
VSTMidiEventList outgoingEvents;
VstSpeakerArrangementType speakerIn, speakerOut;
int numInChans, numOutChans;
bool isProcessing, hasShutdown, firstProcessCallback, shouldDeleteEditor;
bool isProcessing, isBypassed, hasShutdown, firstProcessCallback, shouldDeleteEditor;
HeapBlock<float*> channels;
Array<float*> tempChannels; // see note in processReplacing()
@@ -1467,6 +1479,10 @@ namespace
JuceVSTWrapper* const wrapper = new JuceVSTWrapper (audioMaster, filter);
return wrapper->getAeffect();
}
else
{
jassertfalse; // your createPluginFilter() method must return an object!
}
}
}
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;
}
void AudioProcessor::reset()
{
}
void AudioProcessor::reset() {}
void AudioProcessor::processBlockBypassed (AudioSampleBuffer&, MidiBuffer&) {}
//==============================================================================
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,
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
out the state and position of the playhead.


Loading…
Cancel
Save