diff --git a/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm b/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm index a329eb6ed6..0a8b130474 100644 --- a/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm +++ b/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm @@ -131,8 +131,8 @@ public: JuceAU (AudioUnit component) : AudioProcessorHolder (activePlugins.size() + activeUIs.size() == 0), MusicDeviceBase (component, - (UInt32) AudioUnitHelpers::getBusCount (juceFilter.get(), true), - (UInt32) AudioUnitHelpers::getBusCount (juceFilter.get(), false)), + (UInt32) AudioUnitHelpers::getBusCountForWrapper (*juceFilter, true), + (UInt32) AudioUnitHelpers::getBusCountForWrapper (*juceFilter, false)), mapper (*juceFilter) { inParameterChangedCallback = false; @@ -210,7 +210,7 @@ public: return err; mapper.alloc(); - pulledSucceeded.calloc (static_cast (AudioUnitHelpers::getBusCount (juceFilter.get(), true))); + pulledSucceeded.calloc (static_cast (AudioUnitHelpers::getBusCountForWrapper (*juceFilter, true))); prepareToPlay(); @@ -300,7 +300,7 @@ public: if (isInput) return false; #endif - const int busCount = AudioUnitHelpers::getBusCount (juceFilter.get(), isInput); + const int busCount = AudioUnitHelpers::getBusCount (*juceFilter, isInput); return (juceFilter->canAddBus (isInput) || (busCount > 0 && juceFilter->canRemoveBus (isInput))); #endif } @@ -313,12 +313,12 @@ public: if ((err = scopeToDirection (scope, isInput)) != noErr) return err; - if (count != (UInt32) AudioUnitHelpers::getBusCount (juceFilter.get(), isInput)) + if (count != (UInt32) AudioUnitHelpers::getBusCount (*juceFilter, isInput)) { #ifdef JucePlugin_PreferredChannelConfigurations return kAudioUnitErr_PropertyNotWritable; #else - const int busCount = AudioUnitHelpers::getBusCount (juceFilter.get(), isInput); + const int busCount = AudioUnitHelpers::getBusCount (*juceFilter, isInput); if ((! juceFilter->canAddBus (isInput)) && ((busCount == 0) || (! juceFilter->canRemoveBus (isInput)))) return kAudioUnitErr_PropertyNotWritable; @@ -362,7 +362,7 @@ public: if (err != noErr) { // restore bus state - const int newBusCount = AudioUnitHelpers::getBusCount (juceFilter.get(), isInput); + const int newBusCount = AudioUnitHelpers::getBusCount (*juceFilter, isInput); for (int i = newBusCount; i != busCount; i += (busCount > newBusCount ? 1 : -1)) { if (busCount > newBusCount) @@ -798,15 +798,14 @@ public: UInt32 GetAudioChannelLayout (AudioUnitScope scope, AudioUnitElement element, AudioChannelLayout* outLayoutPtr, Boolean& outWritable) override { - bool isInput; - int busNr; - outWritable = false; - if (elementToBusIdx (scope, element, isInput, busNr) != noErr) + const auto info = getElementInfo (scope, element); + + if (info.error != noErr) return 0; - if (busIgnoresLayout (isInput, busNr)) + if (busIgnoresLayout (info.isInput, info.busNr)) return 0; outWritable = true; @@ -816,7 +815,7 @@ public: if (outLayoutPtr != nullptr) { zeromem (outLayoutPtr, sizeInBytes); - outLayoutPtr->mChannelLayoutTag = getCurrentLayout (isInput, busNr); + outLayoutPtr->mChannelLayoutTag = getCurrentLayout (info.isInput, info.busNr); } return sizeInBytes; @@ -824,16 +823,15 @@ public: UInt32 GetChannelLayoutTags (AudioUnitScope scope, AudioUnitElement element, AudioChannelLayoutTag* outLayoutTags) override { - bool isInput; - int busNr; + const auto info = getElementInfo (scope, element); - if (elementToBusIdx (scope, element, isInput, busNr) != noErr) + if (info.error != noErr) return 0; - if (busIgnoresLayout (isInput, busNr)) + if (busIgnoresLayout (info.isInput, info.busNr)) return 0; - const Array& layouts = getSupportedBusLayouts (isInput, busNr); + const Array& layouts = getSupportedBusLayouts (info.isInput, info.busNr); if (outLayoutTags != nullptr) std::copy (layouts.begin(), layouts.end(), outLayoutTags); @@ -843,20 +841,18 @@ public: OSStatus SetAudioChannelLayout (AudioUnitScope scope, AudioUnitElement element, const AudioChannelLayout* inLayout) override { - bool isInput; - int busNr; - OSStatus err; + const auto info = getElementInfo (scope, element); - if ((err = elementToBusIdx (scope, element, isInput, busNr)) != noErr) - return err; + if (info.error != noErr) + return info.error; - if (busIgnoresLayout (isInput, busNr)) + if (busIgnoresLayout (info.isInput, info.busNr)) return kAudioUnitErr_PropertyNotWritable; if (inLayout == nullptr) return kAudioUnitErr_InvalidPropertyValue; - if (const AUIOElement* ioElement = GetIOElement (isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output, element)) + if (const AUIOElement* ioElement = GetIOElement (info.isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output, element)) { const AudioChannelSet newChannelSet = CoreAudioLayouts::fromCoreAudio (*inLayout); const int currentNumChannels = static_cast (ioElement->GetStreamFormat().NumberChannels()); @@ -872,11 +868,11 @@ public: if (! AudioUnitHelpers::isLayoutSupported (*juceFilter, isInput, busNr, newChannelNum, configs)) return kAudioUnitErr_FormatNotSupported; #else - if (! juceFilter->getBus (isInput, busNr)->isLayoutSupported (newChannelSet)) + if (! juceFilter->getBus (info.isInput, info.busNr)->isLayoutSupported (newChannelSet)) return kAudioUnitErr_FormatNotSupported; #endif - getCurrentLayout (isInput, busNr) = CoreAudioLayouts::toCoreAudio (newChannelSet); + getCurrentLayout (info.isInput, info.busNr) = CoreAudioLayouts::toCoreAudio (newChannelSet); return noErr; } @@ -1051,7 +1047,14 @@ public: ComponentResult Version() override { return JucePlugin_VersionCode; } bool SupportsTail() override { return true; } Float64 GetTailTime() override { return juceFilter->getTailLengthSeconds(); } - double getSampleRate() { return AudioUnitHelpers::getBusCount (juceFilter.get(), false) > 0 ? GetOutput (0)->GetStreamFormat().mSampleRate : 44100.0; } + + double getSampleRate() + { + if (AudioUnitHelpers::getBusCountForWrapper (*juceFilter, false) > 0) + return GetOutput (0)->GetStreamFormat().mSampleRate; + + return 44100.0; + } Float64 GetLatency() override { @@ -1206,32 +1209,33 @@ public: //============================================================================== bool StreamFormatWritable (AudioUnitScope scope, AudioUnitElement element) override { - bool ignore; - int busIdx; + const auto info = getElementInfo (scope, element); - return ((! IsInitialized()) && (elementToBusIdx (scope, element, ignore, busIdx) == noErr)); + return ((! IsInitialized()) && (info.error == noErr)); } bool ValidFormat (AudioUnitScope scope, AudioUnitElement element, const CAStreamBasicDescription& format) override { - bool isInput; - int busNr; - // DSP Quattro incorrectly uses global scope for the ValidFormat call if (scope == kAudioUnitScope_Global) return ValidFormat (kAudioUnitScope_Input, element, format) || ValidFormat (kAudioUnitScope_Output, element, format); - if (elementToBusIdx (scope, element, isInput, busNr) != noErr) + const auto info = getElementInfo (scope, element); + + if (info.error != noErr) return false; + if (info.kind == BusKind::wrapperOnly) + return true; + const int newNumChannels = static_cast (format.NumberChannels()); - const int oldNumChannels = juceFilter->getChannelCountOfBus (isInput, busNr); + const int oldNumChannels = juceFilter->getChannelCountOfBus (info.isInput, info.busNr); if (newNumChannels == oldNumChannels) return true; - if (AudioProcessor::Bus* bus = juceFilter->getBus (isInput, busNr)) + if (AudioProcessor::Bus* bus = juceFilter->getBus (info.isInput, info.busNr)) { if (! MusicDeviceBase::ValidFormat (scope, element, format)) return false; @@ -1252,17 +1256,15 @@ public: // AU requires us to override this for the sole reason that we need to find a default layout tag if the number of channels have changed OSStatus ChangeStreamFormat (AudioUnitScope scope, AudioUnitElement element, const CAStreamBasicDescription& old, const CAStreamBasicDescription& format) override { - bool isInput; - int busNr; - OSStatus err = elementToBusIdx (scope, element, isInput, busNr); + const auto info = getElementInfo (scope, element); - if (err != noErr) - return err; + if (info.error != noErr) + return info.error; - AudioChannelLayoutTag& currentTag = getCurrentLayout (isInput, busNr); + AudioChannelLayoutTag& currentTag = getCurrentLayout (info.isInput, info.busNr); const int newNumChannels = static_cast (format.NumberChannels()); - const int oldNumChannels = juceFilter->getChannelCountOfBus (isInput, busNr); + const int oldNumChannels = juceFilter->getChannelCountOfBus (info.isInput, info.busNr); #ifdef JucePlugin_PreferredChannelConfigurations short configs[][2] = {JucePlugin_PreferredChannelConfigurations}; @@ -1272,13 +1274,21 @@ public: #endif // predict channel layout - AudioChannelSet set = (newNumChannels != oldNumChannels) ? juceFilter->getBus (isInput, busNr)->supportedLayoutWithChannels (newNumChannels) - : juceFilter->getChannelLayoutOfBus (isInput, busNr); + const auto set = [&] + { + if (info.kind == BusKind::wrapperOnly) + return AudioChannelSet::discreteChannels (newNumChannels); + + if (newNumChannels != oldNumChannels) + return juceFilter->getBus (info.isInput, info.busNr)->supportedLayoutWithChannels (newNumChannels); + + return juceFilter->getChannelLayoutOfBus (info.isInput, info.busNr); + }(); if (set == AudioChannelSet()) return kAudioUnitErr_FormatNotSupported; - err = MusicDeviceBase::ChangeStreamFormat (scope, element, old, format); + const auto err = MusicDeviceBase::ChangeStreamFormat (scope, element, old, format); if (err == noErr) currentTag = CoreAudioLayouts::toCoreAudio (set); @@ -1302,8 +1312,8 @@ public: ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence; - const int numInputBuses = static_cast (GetScope (kAudioUnitScope_Input) .GetNumberOfElements()); - const int numOutputBuses = static_cast (GetScope (kAudioUnitScope_Output).GetNumberOfElements()); + const int numInputBuses = AudioUnitHelpers::getBusCount (*juceFilter, true); + const int numOutputBuses = AudioUnitHelpers::getBusCount (*juceFilter, false); // set buffer pointers to minimize copying { @@ -1839,14 +1849,18 @@ private: void prepareOutputBuffers (const UInt32 nFrames) noexcept { - const unsigned int numOutputBuses = GetScope (kAudioUnitScope_Output).GetNumberOfElements(); + const auto numProcessorBuses = AudioUnitHelpers::getBusCount (*juceFilter, false); + const auto numWrapperBuses = GetScope (kAudioUnitScope_Output).GetNumberOfElements(); - for (unsigned int busIdx = 0; busIdx < numOutputBuses; ++busIdx) + for (UInt32 busIdx = 0; busIdx < numWrapperBuses; ++busIdx) { AUOutputElement* output = GetOutput (busIdx); if (output->WillAllocateBuffer()) output->PrepareBuffer (nFrames); + + if (busIdx >= (UInt32) numProcessorBuses) + AudioUnitHelpers::clearAudioBuffer (output->GetBufferList()); } } @@ -1944,16 +1958,37 @@ private: ? (OSStatus) kAudioUnitErr_InvalidScope : (OSStatus) noErr; } - OSStatus elementToBusIdx (AudioUnitScope scope, AudioUnitElement element, bool& isInput, int& busIdx) noexcept + enum class BusKind { + processor, + wrapperOnly, + }; + + struct ElementInfo + { + int busNr; + BusKind kind; + bool isInput; + OSStatus error; + }; + + ElementInfo getElementInfo (AudioUnitScope scope, AudioUnitElement element) noexcept + { + bool isInput = false; OSStatus err; - busIdx = static_cast (element); + if ((err = scopeToDirection (scope, isInput)) != noErr) + return { {}, {}, {}, err }; - if ((err = scopeToDirection (scope, isInput)) != noErr) return err; - if (isPositiveAndBelow (busIdx, AudioUnitHelpers::getBusCount (juceFilter.get(), isInput))) return noErr; + const auto busIdx = static_cast (element); - return kAudioUnitErr_InvalidElement; + if (isPositiveAndBelow (busIdx, AudioUnitHelpers::getBusCount (*juceFilter, isInput))) + return { busIdx, BusKind::processor, isInput, noErr }; + + if (isPositiveAndBelow (busIdx, AudioUnitHelpers::getBusCountForWrapper (*juceFilter, isInput))) + return { busIdx, BusKind::wrapperOnly, isInput, noErr }; + + return { {}, {}, {}, kAudioUnitErr_InvalidElement }; } //============================================================================== @@ -2069,22 +2104,25 @@ private: OSStatus syncAudioUnitWithProcessor() { OSStatus err = noErr; - const int enabledInputs = AudioUnitHelpers::getBusCount (juceFilter.get(), true); - const int enabledOutputs = AudioUnitHelpers::getBusCount (juceFilter.get(), false); + const auto numWrapperInputs = AudioUnitHelpers::getBusCountForWrapper (*juceFilter, true); + const auto numWrapperOutputs = AudioUnitHelpers::getBusCountForWrapper (*juceFilter, false); - if ((err = MusicDeviceBase::SetBusCount (kAudioUnitScope_Input, static_cast (enabledInputs))) != noErr) + if ((err = MusicDeviceBase::SetBusCount (kAudioUnitScope_Input, static_cast (numWrapperInputs))) != noErr) return err; - if ((err = MusicDeviceBase::SetBusCount (kAudioUnitScope_Output, static_cast (enabledOutputs))) != noErr) + if ((err = MusicDeviceBase::SetBusCount (kAudioUnitScope_Output, static_cast (numWrapperOutputs))) != noErr) return err; addSupportedLayoutTags(); - for (int i = 0; i < enabledInputs; ++i) + const auto numProcessorInputs = AudioUnitHelpers::getBusCount (*juceFilter, true); + const auto numProcessorOutputs = AudioUnitHelpers::getBusCount (*juceFilter, false); + + for (int i = 0; i < numProcessorInputs; ++i) if ((err = syncAudioUnitWithChannelSet (true, i, juceFilter->getChannelLayoutOfBus (true, i))) != noErr) return err; - for (int i = 0; i < enabledOutputs; ++i) + for (int i = 0; i < numProcessorOutputs; ++i) if ((err = syncAudioUnitWithChannelSet (false, i, juceFilter->getChannelLayoutOfBus (false, i))) != noErr) return err; @@ -2093,8 +2131,8 @@ private: OSStatus syncProcessorWithAudioUnit() { - const int numInputBuses = AudioUnitHelpers::getBusCount (juceFilter.get(), true); - const int numOutputBuses = AudioUnitHelpers::getBusCount (juceFilter.get(), false); + const int numInputBuses = AudioUnitHelpers::getBusCount (*juceFilter, true); + const int numOutputBuses = AudioUnitHelpers::getBusCount (*juceFilter, false); const int numInputElements = static_cast (GetScope(kAudioUnitScope_Input). GetNumberOfElements()); const int numOutputElements = static_cast (GetScope(kAudioUnitScope_Output).GetNumberOfElements()); @@ -2229,7 +2267,7 @@ private: { auto& layouts = isInput ? supportedInputLayouts : supportedOutputLayouts; layouts.clear(); - auto numBuses = AudioUnitHelpers::getBusCount (juceFilter.get(), isInput); + auto numBuses = AudioUnitHelpers::getBusCount (*juceFilter, isInput); for (int busNr = 0; busNr < numBuses; ++busNr) { @@ -2244,8 +2282,8 @@ private: { currentInputLayout.clear(); currentOutputLayout.clear(); - currentInputLayout. resize (AudioUnitHelpers::getBusCount (juceFilter.get(), true)); - currentOutputLayout.resize (AudioUnitHelpers::getBusCount (juceFilter.get(), false)); + currentInputLayout. resize (AudioUnitHelpers::getBusCountForWrapper (*juceFilter, true)); + currentOutputLayout.resize (AudioUnitHelpers::getBusCountForWrapper (*juceFilter, false)); addSupportedLayoutTagsForDirection (true); addSupportedLayoutTagsForDirection (false); diff --git a/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm b/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm index 13627c743b..80a10fedba 100644 --- a/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm +++ b/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm @@ -782,7 +782,7 @@ public: for (int dir = 0; dir < 2; ++dir) { const bool isInput = (dir == 0); - const int n = AudioUnitHelpers::getBusCount (&processor, isInput); + const int n = AudioUnitHelpers::getBusCount (processor, isInput); Array& channelSets = (isInput ? layouts.inputBuses : layouts.outputBuses); AUAudioUnitBusArray* auBuses = (isInput ? [getAudioUnit() inputBusses] : [getAudioUnit() outputBusses]); @@ -839,7 +839,7 @@ public: audioBuffer.prepare (totalInChannels, totalOutChannels, static_cast (maxFrames)); - double sampleRate = (jmax (AudioUnitHelpers::getBusCount (&processor, true), AudioUnitHelpers::getBusCount (&processor, false)) > 0 ? + double sampleRate = (jmax (AudioUnitHelpers::getBusCount (processor, true), AudioUnitHelpers::getBusCount (processor, false)) > 0 ? [[[([inputBusses.get() count] > 0 ? inputBusses.get() : outputBusses.get()) objectAtIndexedSubscript: 0] format] sampleRate] : 44100.0); processor.setRateAndBufferSizeDetails (sampleRate, static_cast (maxFrames)); @@ -1125,7 +1125,7 @@ private: { std::unique_ptr, NSObjectDeleter> array ([[NSMutableArray alloc] init]); AudioProcessor& processor = getAudioProcessor(); - const int n = AudioUnitHelpers::getBusCount (&processor, isInput); + const int n = AudioUnitHelpers::getBusCount (processor, isInput); for (int i = 0; i < n; ++i) { @@ -1383,7 +1383,7 @@ private: OwnedArray& busBuffers = isInput ? inBusBuffers : outBusBuffers; busBuffers.clear(); - const int n = AudioUnitHelpers::getBusCount (&getAudioProcessor(), isInput); + const int n = AudioUnitHelpers::getBusCount (getAudioProcessor(), isInput); const AUAudioFrameCount maxFrames = [getAudioUnit() maximumFramesToRender]; for (int busIdx = 0; busIdx < n; ++busIdx) diff --git a/modules/juce_audio_processors/format_types/juce_AU_Shared.h b/modules/juce_audio_processors/format_types/juce_AU_Shared.h index c311fdc0cb..570677aff0 100644 --- a/modules/juce_audio_processors/format_types/juce_AU_Shared.h +++ b/modules/juce_audio_processors/format_types/juce_AU_Shared.h @@ -43,8 +43,8 @@ struct AudioUnitHelpers void alloc() { - const int numInputBuses = AudioUnitHelpers::getBusCount (&processor, true); - const int numOutputBuses = AudioUnitHelpers::getBusCount (&processor, false); + const int numInputBuses = AudioUnitHelpers::getBusCount (processor, true); + const int numOutputBuses = AudioUnitHelpers::getBusCount (processor, false); initializeChannelMapArray (true, numInputBuses); initializeChannelMapArray (false, numOutputBuses); @@ -334,8 +334,8 @@ struct AudioUnitHelpers { Array channelInfo; - auto hasMainInputBus = (AudioUnitHelpers::getBusCount (&processor, true) > 0); - auto hasMainOutputBus = (AudioUnitHelpers::getBusCount (&processor, false) > 0); + auto hasMainInputBus = (AudioUnitHelpers::getBusCountForWrapper (processor, true) > 0); + auto hasMainOutputBus = (AudioUnitHelpers::getBusCountForWrapper (processor, false) > 0); if ((! hasMainInputBus) && (! hasMainOutputBus)) { @@ -471,9 +471,9 @@ struct AudioUnitHelpers } //============================================================================== - static int getBusCount (const AudioProcessor* juceFilter, bool isInput) + static int getBusCount (const AudioProcessor& juceFilter, bool isInput) { - int busCount = juceFilter->getBusCount (isInput); + int busCount = juceFilter.getBusCount (isInput); #ifdef JucePlugin_PreferredChannelConfigurations short configs[][2] = {JucePlugin_PreferredChannelConfigurations}; @@ -491,6 +491,17 @@ struct AudioUnitHelpers return busCount; } + static int getBusCountForWrapper (const AudioProcessor& juceFilter, bool isInput) + { + #if JucePlugin_IsMidiEffect + const auto numRequiredBuses = isInput ? 0 : 1; + #else + const auto numRequiredBuses = 0; + #endif + + return jmax (numRequiredBuses, getBusCount (juceFilter, isInput)); + } + static bool setBusesLayout (AudioProcessor* juceFilter, const AudioProcessor::BusesLayout& requestedLayouts) { #ifdef JucePlugin_PreferredChannelConfigurations