diff --git a/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp b/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp index 97bb15c4db..f7d74fd84a 100644 --- a/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp +++ b/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp @@ -544,49 +544,55 @@ Array AudioChannelSet::channelSetsWithNumberOfChannels (int num { retval.add (AudioChannelSet::discreteChannels (numChannels)); - if (numChannels == 1) + retval.addArray ([numChannels]() -> Array { - retval.add (AudioChannelSet::mono()); - } - else if (numChannels == 2) - { - retval.add (AudioChannelSet::stereo()); - } - else if (numChannels == 3) - { - retval.add (AudioChannelSet::createLCR()); - retval.add (AudioChannelSet::createLRS()); - } - else if (numChannels == 4) - { - retval.add (AudioChannelSet::quadraphonic()); - retval.add (AudioChannelSet::createLCRS()); - } - else if (numChannels == 5) - { - retval.add (AudioChannelSet::create5point0()); - retval.add (AudioChannelSet::pentagonal()); - } - else if (numChannels == 6) - { - retval.add (AudioChannelSet::create5point1()); - retval.add (AudioChannelSet::create6point0()); - retval.add (AudioChannelSet::create6point0Music()); - retval.add (AudioChannelSet::hexagonal()); - } - else if (numChannels == 7) - { - retval.add (AudioChannelSet::create7point0()); - retval.add (AudioChannelSet::create7point0SDDS()); - retval.add (AudioChannelSet::create6point1()); - retval.add (AudioChannelSet::create6point1Music()); - } - else if (numChannels == 8) - { - retval.add (AudioChannelSet::create7point1()); - retval.add (AudioChannelSet::create7point1SDDS()); - retval.add (AudioChannelSet::octagonal()); - } + switch (numChannels) + { + case 1: + return { AudioChannelSet::mono() }; + case 2: + return { AudioChannelSet::stereo() }; + case 3: + return { AudioChannelSet::createLCR(), + AudioChannelSet::createLRS() }; + case 4: + return { AudioChannelSet::quadraphonic(), + AudioChannelSet::createLCRS() }; + case 5: + return { AudioChannelSet::create5point0(), + AudioChannelSet::pentagonal() }; + case 6: + return { AudioChannelSet::create5point1(), + AudioChannelSet::create6point0(), + AudioChannelSet::create6point0Music(), + AudioChannelSet::hexagonal() }; + case 7: + return { AudioChannelSet::create7point0(), + AudioChannelSet::create7point0SDDS(), + AudioChannelSet::create6point1(), + AudioChannelSet::create6point1Music() }; + case 8: + return { AudioChannelSet::create7point1(), + AudioChannelSet::create7point1SDDS(), + AudioChannelSet::octagonal(), + AudioChannelSet::create5point1point2() }; + case 9: + return { AudioChannelSet::create7point0point2() }; + case 10: + return { AudioChannelSet::create5point1point4(), + AudioChannelSet::create7point1point2() }; + case 11: + return { AudioChannelSet::create7point0point4() }; + case 12: + return { AudioChannelSet::create7point1point4() }; + case 14: + return { AudioChannelSet::create7point1point6() }; + case 16: + return { AudioChannelSet::create9point1point6() }; + } + + return {}; + }()); auto order = getAmbisonicOrderForNumChannels (numChannels); if (order >= 0) diff --git a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm index b878bd0345..e17c7bfa20 100644 --- a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm +++ b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm @@ -312,6 +312,105 @@ namespace AudioUnitFormatHelpers void handleAsyncUpdate() override { resizeToFitView(); } }; #endif + + template + struct BasicOptional + { + BasicOptional() = default; + + explicit constexpr BasicOptional (Value&& v) : value (std::move (v)), isValid (true) {} + explicit constexpr BasicOptional (const Value& v) : value (v), isValid (true) {} + + explicit constexpr operator bool() const noexcept { return isValid; } + + Value value; + bool isValid { false }; + }; + + template + static BasicOptional tryGetProperty (AudioUnit inUnit, + AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement) + { + Value data; + auto size = (UInt32) sizeof (Value); + + if (AudioUnitGetProperty (inUnit, inID, inScope, inElement, &data, &size) == noErr) + return BasicOptional (data); + + return BasicOptional(); + } + + static UInt32 getElementCount (AudioUnit comp, AudioUnitScope scope) noexcept + { + const auto count = tryGetProperty (comp, kAudioUnitProperty_ElementCount, scope, 0); + jassert (count.isValid); + return count.value; + } + + /* The plugin may expect its channels in a particular order, reported to the host + using kAudioUnitProperty_AudioChannelLayout. + This remapper allows us to respect the channel order requested by the plugin, + while still using the JUCE channel ordering for the AudioBuffer argument + of AudioProcessor::processBlock. + */ + class SingleDirectionChannelMapping + { + public: + void setUpMapping (AudioUnit comp, bool isInput) + { + channels.clear(); + busOffset.clear(); + + const auto scope = isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output; + const auto n = getElementCount (comp, scope); + + for (UInt32 busIndex = 0; busIndex < n; ++busIndex) + { + std::vector busMap; + + if (const auto layout = tryGetProperty (comp, kAudioUnitProperty_AudioChannelLayout, scope, busIndex)) + { + const auto juceChannelOrder = CoreAudioLayouts::fromCoreAudio (layout.value); + const auto auChannelOrder = CoreAudioLayouts::getCoreAudioLayoutChannels (layout.value); + + for (auto juceChannelIndex = 0; juceChannelIndex < juceChannelOrder.size(); ++juceChannelIndex) + busMap.push_back ((size_t) auChannelOrder.indexOf (juceChannelOrder.getTypeOfChannel (juceChannelIndex))); + } + + busOffset.push_back (busMap.empty() ? unknownChannelCount : channels.size()); + channels.insert (channels.end(), busMap.begin(), busMap.end()); + } + } + + size_t getAuIndexForJuceChannel (size_t bus, size_t channel) const noexcept + { + const auto baseOffset = busOffset[bus]; + return baseOffset != unknownChannelCount ? channels[baseOffset + channel] + : channel; + } + + private: + static constexpr size_t unknownChannelCount = std::numeric_limits::max(); + + /* The index (in the channels vector) of the first channel in each bus. + e.g the index of the first channel in the second bus can be found at busOffset[1]. + It's possible for a bus not to report its channel layout, and in this case a value + of unknownChannelCount will be stored for that bus. + */ + std::vector busOffset; + + /* The index in a collection of JUCE channels of the AU channel with a matching channel + type. The mappings for all buses are stored in bus order. To find the start offset for a + particular bus, use the busOffset vector. + e.g. the index of the AU channel with the same type as the fifth channel of the third bus + in JUCE layout is found at channels[busOffset[2] + 4]. + If the busOffset for the bus is unknownChannelCount, then assume there is no mapping + between JUCE/AU channel layouts. + */ + std::vector channels; + }; } //============================================================================== @@ -624,8 +723,8 @@ public: bool canApplyBusCountChange (bool isInput, bool isAdding, BusProperties& outProperties) override { - int currentCount = getBusCount (isInput); - int newCount = currentCount + (isAdding ? 1 : -1); + auto currentCount = (UInt32) getBusCount (isInput); + auto newCount = (UInt32) ((int) currentCount + (isAdding ? 1 : -1)); AudioUnitScope scope = isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output; if (AudioUnitSetProperty (audioUnit, kAudioUnitProperty_ElementCount, scope, 0, &newCount, sizeof (newCount)) == noErr) @@ -980,7 +1079,6 @@ public: timeStamp.mHostTime = GetCurrentHostTime (0, newSampleRate, isAUv3); timeStamp.mFlags = kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid; - currentBuffer = nullptr; wasPlaying = false; resetBuses(); @@ -1003,6 +1101,12 @@ public: AudioUnitUninitialize (audioUnit); } } + + inMapping .setUpMapping (audioUnit, true); + outMapping.setUpMapping (audioUnit, false); + + inputBuffer.setSize (jmax (getTotalNumInputChannels(), getTotalNumOutputChannels()), + estimatedSamplesPerBlock); } } @@ -1015,7 +1119,6 @@ public: AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); outputBufferList.clear(); - currentBuffer = nullptr; prepared = false; } @@ -1030,6 +1133,14 @@ public: void processAudio (AudioBuffer& buffer, MidiBuffer& midiMessages, bool processBlockBypassedCalled) { + // If these are hit, we might allocate in the process block! + jassert (buffer.getNumChannels() <= inputBuffer.getNumChannels()); + jassert (buffer.getNumSamples() <= inputBuffer.getNumSamples()); + // Copy the input buffer to guard against the case where a bus has more output channels + // than input channels, so rendering the output for that bus might stamp over the input + // to the following bus. + inputBuffer.makeCopyOf (buffer, true); + auto numSamples = buffer.getNumSamples(); if (auSupportsBypass) @@ -1045,28 +1156,27 @@ public: if (prepared) { timeStamp.mHostTime = GetCurrentHostTime (numSamples, getSampleRate(), isAUv3); - int numOutputBuses; - int chIdx = 0; - numOutputBuses = getBusCount (false); + const auto numOutputBuses = getBusCount (false); for (int i = 0; i < numOutputBuses; ++i) { if (AUBuffer* buf = outputBufferList[i]) { AudioBufferList& abl = *buf; + const auto* bus = getBus (false, i); + const auto channelCount = bus != nullptr ? bus->getNumberOfChannels() : 0; - for (AudioUnitElement j = 0; j < abl.mNumberBuffers; ++j) + for (auto juceChannel = 0; juceChannel < channelCount; ++juceChannel) { - abl.mBuffers[j].mNumberChannels = 1; - abl.mBuffers[j].mDataByteSize = (UInt32) ((size_t) numSamples * sizeof (float)); - abl.mBuffers[j].mData = buffer.getWritePointer (chIdx++); + const auto auChannel = outMapping.getAuIndexForJuceChannel ((size_t) i, (size_t) juceChannel); + abl.mBuffers[auChannel].mNumberChannels = 1; + abl.mBuffers[auChannel].mDataByteSize = (UInt32) ((size_t) numSamples * sizeof (float)); + abl.mBuffers[auChannel].mData = buffer.getWritePointer (bus->getChannelIndexInProcessBlockBuffer (juceChannel)); } } } - currentBuffer = &buffer; - if (wantsMidiMessages) { for (const auto metadata : midiMessages) @@ -1142,10 +1252,10 @@ public: for (int dir = 0; dir < 2; ++dir) { - const bool isInput = (dir == 0); - const int n = getElementCount (comp, isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output); + const auto isInput = (dir == 0); + const auto n = AudioUnitFormatHelpers::getElementCount (comp, isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output); - for (int i = 0; i < n; ++i) + for (UInt32 i = 0; i < n; ++i) { String busName; AudioChannelSet currentLayout; @@ -1642,7 +1752,7 @@ private: OwnedArray outputBufferList; AudioTimeStamp timeStamp; - AudioBuffer* currentBuffer = nullptr; + AudioBuffer inputBuffer; Array> supportedInLayouts, supportedOutLayouts; int numChannelInfos; @@ -1655,6 +1765,7 @@ private: std::map paramIDToParameter; + AudioUnitFormatHelpers::SingleDirectionChannelMapping inMapping, outMapping; MidiDataConcatenator midiConcatenator; CriticalSection midiInLock; MidiBuffer incomingMidi; @@ -1811,30 +1922,33 @@ private: const AudioTimeStamp*, UInt32 inBusNumber, UInt32 inNumberFrames, - AudioBufferList* ioData) const + AudioBufferList* ioData) { - if (currentBuffer != nullptr) + if (inputBuffer.getNumChannels() <= 0) { - // if this ever happens, might need to add extra handling - jassert (inNumberFrames == (UInt32) currentBuffer->getNumSamples()); - auto buffer = static_cast (inBusNumber) < getBusCount (true) - ? getBusBuffer (*currentBuffer, true, static_cast (inBusNumber)) - : AudioBuffer(); + jassertfalse; + return noErr; + } - for (int i = 0; i < static_cast (ioData->mNumberBuffers); ++i) - { - if (i < buffer.getNumChannels()) - { - memcpy (ioData->mBuffers[i].mData, - buffer.getReadPointer (i), - sizeof (float) * inNumberFrames); - } - else - { - zeromem (ioData->mBuffers[i].mData, - sizeof (float) * inNumberFrames); - } - } + // if this ever happens, might need to add extra handling + if (inputBuffer.getNumSamples() != (int) inNumberFrames) + { + jassertfalse; + return noErr; + } + + const auto buffer = static_cast (inBusNumber) < getBusCount (true) + ? getBusBuffer (inputBuffer, true, static_cast (inBusNumber)) + : AudioBuffer(); + + for (int juceChannel = 0; juceChannel < buffer.getNumChannels(); ++juceChannel) + { + const auto auChannel = (int) inMapping.getAuIndexForJuceChannel (inBusNumber, (size_t) juceChannel); + + if (auChannel < buffer.getNumChannels()) + memcpy (ioData->mBuffers[auChannel].mData, buffer.getReadPointer (juceChannel), sizeof (float) * inNumberFrames); + else + zeromem (ioData->mBuffers[auChannel].mData, sizeof (float) * inNumberFrames); } return noErr; @@ -2017,28 +2131,16 @@ private: //============================================================================== int getElementCount (AudioUnitScope scope) const noexcept { - return static_cast (getElementCount (audioUnit, scope)); - } - - static int getElementCount (AudioUnit comp, AudioUnitScope scope) noexcept - { - UInt32 count; - UInt32 countSize = sizeof (count); - - auto err = AudioUnitGetProperty (comp, kAudioUnitProperty_ElementCount, scope, 0, &count, &countSize); - jassert (err == noErr); - ignoreUnused (err); - - return static_cast (count); + return static_cast (AudioUnitFormatHelpers::getElementCount (audioUnit, scope)); } //============================================================================== - void getBusProperties (bool isInput, int busIdx, String& busName, AudioChannelSet& currentLayout) const + void getBusProperties (bool isInput, UInt32 busIdx, String& busName, AudioChannelSet& currentLayout) const { getBusProperties (audioUnit, isInput, busIdx, busName, currentLayout); } - static void getBusProperties (AudioUnit comp, bool isInput, int busIdx, String& busName, AudioChannelSet& currentLayout) + static void getBusProperties (AudioUnit comp, bool isInput, UInt32 busIdx, String& busName, AudioChannelSet& currentLayout) { const AudioUnitScope scope = isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output; busName = (isInput ? "Input #" : "Output #") + String (busIdx + 1); @@ -2047,7 +2149,7 @@ private: CFObjectHolder busNameCF; UInt32 propertySize = sizeof (busNameCF.object); - if (AudioUnitGetProperty (comp, kAudioUnitProperty_ElementName, scope, static_cast (busIdx), &busNameCF.object, &propertySize) == noErr) + if (AudioUnitGetProperty (comp, kAudioUnitProperty_ElementName, scope, busIdx, &busNameCF.object, &propertySize) == noErr) if (busNameCF.object != nullptr) busName = nsStringToJuce ((NSString*) busNameCF.object); @@ -2055,7 +2157,7 @@ private: AudioChannelLayout auLayout; propertySize = sizeof (auLayout); - if (AudioUnitGetProperty (comp, kAudioUnitProperty_AudioChannelLayout, scope, static_cast (busIdx), &auLayout, &propertySize) == noErr) + if (AudioUnitGetProperty (comp, kAudioUnitProperty_AudioChannelLayout, scope, busIdx, &auLayout, &propertySize) == noErr) currentLayout = CoreAudioLayouts::fromCoreAudio (auLayout); } @@ -2064,7 +2166,7 @@ private: AudioStreamBasicDescription descr; propertySize = sizeof (descr); - if (AudioUnitGetProperty (comp, kAudioUnitProperty_StreamFormat, scope, static_cast (busIdx), &descr, &propertySize) == noErr) + if (AudioUnitGetProperty (comp, kAudioUnitProperty_StreamFormat, scope, busIdx, &descr, &propertySize) == noErr) currentLayout = AudioChannelSet::canonicalChannelSet (static_cast (descr.mChannelsPerFrame)); } }