Browse Source

AU Host: Correctly reorder hosted audiounit channels

This change was tested with FabFilter Pro Q 3, which supports new AU
channel layouts for Atmos.
v6.1.6
reuk 3 years ago
parent
commit
3d1818f5bd
No known key found for this signature in database GPG Key ID: FCB43929F012EE5C
2 changed files with 206 additions and 98 deletions
  1. +48
    -42
      modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp
  2. +158
    -56
      modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm

+ 48
- 42
modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp View File

@@ -544,49 +544,55 @@ Array<AudioChannelSet> AudioChannelSet::channelSetsWithNumberOfChannels (int num
{
retval.add (AudioChannelSet::discreteChannels (numChannels));
if (numChannels == 1)
retval.addArray ([numChannels]() -> Array<AudioChannelSet>
{
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)


+ 158
- 56
modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm View File

@@ -312,6 +312,105 @@ namespace AudioUnitFormatHelpers
void handleAsyncUpdate() override { resizeToFitView(); }
};
#endif
template <typename Value>
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 <typename Value>
static BasicOptional<Value> 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<Value> (data);
return BasicOptional<Value>();
}
static UInt32 getElementCount (AudioUnit comp, AudioUnitScope scope) noexcept
{
const auto count = tryGetProperty<UInt32> (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<size_t> busMap;
if (const auto layout = tryGetProperty<AudioChannelLayout> (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<size_t>::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<size_t> 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<size_t> 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<float>& 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<AUBuffer> outputBufferList;
AudioTimeStamp timeStamp;
AudioBuffer<float>* currentBuffer = nullptr;
AudioBuffer<float> inputBuffer;
Array<Array<AudioChannelSet>> supportedInLayouts, supportedOutLayouts;
int numChannelInfos;
@@ -1655,6 +1765,7 @@ private:
std::map<UInt32, AUInstanceParameter*> 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<int> (inBusNumber) < getBusCount (true)
? getBusBuffer (*currentBuffer, true, static_cast<int> (inBusNumber))
: AudioBuffer<float>();
jassertfalse;
return noErr;
}
for (int i = 0; i < static_cast<int> (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<int> (inBusNumber) < getBusCount (true)
? getBusBuffer (inputBuffer, true, static_cast<int> (inBusNumber))
: AudioBuffer<float>();
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<int> (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<int> (count);
return static_cast<int> (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<CFStringRef> busNameCF;
UInt32 propertySize = sizeof (busNameCF.object);
if (AudioUnitGetProperty (comp, kAudioUnitProperty_ElementName, scope, static_cast<UInt32> (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<UInt32> (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<UInt32> (busIdx), &descr, &propertySize) == noErr)
if (AudioUnitGetProperty (comp, kAudioUnitProperty_StreamFormat, scope, busIdx, &descr, &propertySize) == noErr)
currentLayout = AudioChannelSet::canonicalChannelSet (static_cast<int> (descr.mChannelsPerFrame));
}
}


Loading…
Cancel
Save