diff --git a/modules/juce_audio_processors/format_types/juce_VST3Common.h b/modules/juce_audio_processors/format_types/juce_VST3Common.h index a54f0e1871..d7e66cdc89 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3Common.h +++ b/modules/juce_audio_processors/format_types/juce_VST3Common.h @@ -85,6 +85,18 @@ static Steinberg::Vst::TChar* toString (const juce::String& source) noexcept //============================================================================== +static Steinberg::Vst::SpeakerArrangement getArrangementForBus (Steinberg::Vst::IAudioProcessor* processor, + bool isInput, int busIndex) +{ + Steinberg::Vst::SpeakerArrangement arrangement = Steinberg::Vst::SpeakerArr::kEmpty; + + if (processor != nullptr) + processor->getBusArrangement (isInput ? Steinberg::Vst::kInput : Steinberg::Vst::kOutput, + (Steinberg::int32) busIndex, arrangement); + + return arrangement; +} + /** For the sake of simplicity, there can only be 1 arrangement type per channel count. i.e.: 4 channels == k31Cine OR k40Cine */ @@ -341,7 +353,7 @@ namespace VST3BufferExchange Bus& bus, AudioSampleBuffer& buffer, int numChannels, int channelStartOffset, - int sampleOffset = 0) noexcept + int sampleOffset = 0) { const int channelEnd = numChannels + channelStartOffset; jassert (channelEnd >= 0 && channelEnd <= buffer.getNumChannels()); @@ -356,37 +368,52 @@ namespace VST3BufferExchange vstBuffers.silenceFlags = 0; } + static void mapArrangementToBusses (int& channelIndexOffset, int index, + Array& result, + BusMap& busMapToUse, Steinberg::Vst::SpeakerArrangement arrangement, + AudioSampleBuffer& source) + { + const int numChansForBus = BigInteger ((juce::int64) arrangement).countNumberOfSetBits(); + + if (index >= result.size()) + result.add (Steinberg::Vst::AudioBusBuffers()); + + if (index >= busMapToUse.size()) + busMapToUse.add (Bus()); + + if (numChansForBus > 0) + { + associateBufferTo (result.getReference (index), + busMapToUse.getReference (index), + source, numChansForBus, channelIndexOffset); + } + + channelIndexOffset += numChansForBus; + } + + static void mapBufferToBusses (Array& result, BusMap& busMapToUse, + const Array& arrangements, + AudioSampleBuffer& source) + { + int channelIndexOffset = 0; + + for (int i = 0; i < arrangements.size(); ++i) + mapArrangementToBusses (channelIndexOffset, i, result, busMapToUse, + arrangements.getUnchecked (i), source); + } + static void mapBufferToBusses (Array& result, Steinberg::Vst::IAudioProcessor& processor, - BusMap& busMapToUse, - bool isInput, int numBusses, + BusMap& busMapToUse, bool isInput, int numBusses, AudioSampleBuffer& source) { int channelIndexOffset = 0; for (int i = 0; i < numBusses; ++i) - { - Steinberg::Vst::SpeakerArrangement arrangement = 0; - processor.getBusArrangement (isInput ? Steinberg::Vst::kInput : Steinberg::Vst::kOutput, - (Steinberg::int32) i, arrangement); - - const int numChansForBus = BigInteger ((juce::int64) arrangement).countNumberOfSetBits(); - - if (i >= result.size()) - result.add (Steinberg::Vst::AudioBusBuffers()); - - if (i >= busMapToUse.size()) - busMapToUse.add (Bus()); - - if (numChansForBus > 0) - { - associateBufferTo (result.getReference (i), - busMapToUse.getReference (i), - source, numChansForBus, channelIndexOffset); - } - - channelIndexOffset += numChansForBus; - } + mapArrangementToBusses (channelIndexOffset, i, + result, busMapToUse, + getArrangementForBus (&processor, isInput, i), + source); } } diff --git a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index 40754826a7..ba1aa1433c 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -1632,7 +1632,8 @@ public: midiInputs (new MidiEventList()), midiOutputs (new MidiEventList()), isComponentInitialised (false), - isControllerInitialised (false) + isControllerInitialised (false), + isActive (false) { host = new VST3HostContext (this); } @@ -1719,8 +1720,27 @@ public: return module != nullptr ? module->name : String::empty; } + void repopulateArrangements() + { + inputArrangements.clearQuick(); + outputArrangements.clearQuick(); + + // NB: Some plugins need a valid arrangement despite specifying 0 for their I/O busses + for (int i = 0; i < jmax (1, numInputAudioBusses); ++i) + inputArrangements.add (getArrangementForBus (processor, true, i)); + + for (int i = 0; i < jmax (1, numOutputAudioBusses); ++i) + outputArrangements.add (getArrangementForBus (processor, false, i)); + } + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock) override { + // Avoid redundantly calling things like setActive, which can be a heavy-duty call for some plugins: + if (isActive + && getSampleRate() == sampleRate + && getBlockSize() == estimatedSamplesPerBlock) + return; + using namespace Vst; ProcessSetup setup; @@ -1736,21 +1756,25 @@ public: editController->setComponentHandler (host); - Array inArrangements, outArrangements; + if (inputArrangements.size() <= 0 || outputArrangements.size() <= 0) + repopulateArrangements(); - // NB: Some plugins need a valid arrangement despite specifying 0 for their I/O busses - for (int i = 0; i < jmax (1, numInputAudioBusses); ++i) - inArrangements.add (getArrangementForNumChannels (jmax (0, (int) getBusInfo (true, true, i).channelCount))); - - for (int i = 0; i < jmax (1, numOutputAudioBusses); ++i) - outArrangements.add (getArrangementForNumChannels (jmax (0, (int) getBusInfo (false, true, i).channelCount))); - - warnOnFailure (processor->setBusArrangements (inArrangements.getRawDataPointer(), numInputAudioBusses, - outArrangements.getRawDataPointer(), numOutputAudioBusses)); + warnOnFailure (processor->setBusArrangements (inputArrangements.getRawDataPointer(), numInputAudioBusses, + outputArrangements.getRawDataPointer(), numOutputAudioBusses)); // Update the num. busses in case the configuration has been modified by the plugin. (May affect number of channels!): - numInputAudioBusses = getNumSingleDirectionBussesFor (component, true, true); - numOutputAudioBusses = getNumSingleDirectionBussesFor (component, false, true); + const int newNumInputAudioBusses = getNumSingleDirectionBussesFor (component, true, true); + const int newNumOutputAudioBusses = getNumSingleDirectionBussesFor (component, false, true); + + // Repopulate arrangements if the number of busses have changed: + if (numInputAudioBusses != newNumInputAudioBusses + || numOutputAudioBusses != newNumOutputAudioBusses) + { + numInputAudioBusses = newNumInputAudioBusses; + numOutputAudioBusses = newNumOutputAudioBusses; + + repopulateArrangements(); + } // Needed for having the same sample rate in processBlock(); some plugins need this! setPlayConfigDetails (getNumSingleDirectionChannelsFor (component, true, true), @@ -1763,19 +1787,26 @@ public: warnOnFailure (component->setActive (true)); warnOnFailure (processor->setProcessing (true)); + + isActive = true; } void releaseResources() override { + if (! isActive) + return; // Avoids redundantly calling things like setActive + JUCE_TRY { + isActive = false; + setStateForAllBusses (false); if (processor != nullptr) - processor->setProcessing (false); + warnOnFailure (processor->setProcessing (false)); if (component != nullptr) - component->setActive (false); + warnOnFailure (component->setActive (false)); } JUCE_CATCH_ALL_ASSERT } @@ -1784,7 +1815,8 @@ public: { using namespace Vst; - if (processor != nullptr + if (isActive + && processor != nullptr && processor->canProcessSampleSize (kSample32) == kResultTrue) { const int numSamples = buffer.getNumSamples(); @@ -1856,15 +1888,22 @@ public: //============================================================================== bool silenceInProducesSilenceOut() const override { - return processor == nullptr; + if (processor != nullptr) + return processor->getTailSamples() == Vst::kNoTail; + + return true; } /** 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 { if (processor != nullptr) - return (double) jmin ((int) jmax ((Steinberg::uint32) 0, processor->getTailSamples()), 0x7fffffff) - * getSampleRate(); + { + const double sampleRate = getSampleRate(); + + if (sampleRate > 0.0) + return jlimit (0, 0x7fffffff, (int) processor->getTailSamples()) / sampleRate; + } return 0.0; } @@ -2039,6 +2078,7 @@ private: as very poorly specified by the Steinberg SDK */ int numInputAudioBusses, numOutputAudioBusses; + Array inputArrangements, outputArrangements; // Caching to improve performance and to avoid possible non-thread-safe calls to getBusArrangements(). VST3BufferExchange::BusMap inputBusMap, outputBusMap; Array inputBusses, outputBusses; @@ -2105,7 +2145,7 @@ private: ComSmartPtr inputParameterChanges, outputParameterChanges; ComSmartPtr midiInputs, midiOutputs; Vst::ProcessContext timingInfo; //< Only use this in processBlock()! - bool isComponentInitialised, isControllerInitialised; + bool isComponentInitialised, isControllerInitialised, isActive; //============================================================================== bool fetchComponentAndController (IPluginFactory* factory, const Steinberg::int32 numClasses) @@ -2310,11 +2350,8 @@ private: { using namespace VST3BufferExchange; - mapBufferToBusses (inputBusses, *processor, inputBusMap, - true, numInputAudioBusses, buffer); - - mapBufferToBusses (outputBusses, *processor, outputBusMap, - false, numOutputAudioBusses, buffer); + mapBufferToBusses (inputBusses, inputBusMap, inputArrangements, buffer); + mapBufferToBusses (outputBusses, outputBusMap, outputArrangements, buffer); destination.inputs = inputBusses.getRawDataPointer(); destination.outputs = outputBusses.getRawDataPointer();