mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Audio plugins: better bypass support, with an AudioProcessor::processBlockBypassed() method that can be used for custom behaviour.
This commit is contained in:
parent
c6944afd4c
commit
f903695ba3
6 changed files with 95 additions and 96 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 (...)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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…
Add table
Add a link
Reference in a new issue