mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +00:00
AU Client: Improve AUChannelInfo reporting and add tests
The previous implementation could emit 'wildcard' channel layouts in too many scenarios. A wildcard channel count is -1 or -2, and indicates that any number of channels is supported on the bus. If the input and output layouts must match, then a layout of [-1, -1] should be returned. If any layout is valid in both directions, then a layout of [-1, -2] should be returned. In the case where we have a bus A and opposite bus B, we will now only emit a wildcard count for bus A if every bus standard bus layout up to a channel count of 16 can be applied successfully without changing the bus count of B.
This commit is contained in:
parent
7e3aae3cb9
commit
35d3fab960
34 changed files with 547 additions and 115 deletions
|
|
@ -172,7 +172,12 @@ public:
|
|||
channelInfo.add (info);
|
||||
}
|
||||
#else
|
||||
channelInfo = AudioUnitHelpers::getAUChannelInfo (*juceFilter);
|
||||
auto channelInfoSet = AudioUnitHelpers::getAUChannelInfo (*juceFilter);
|
||||
channelInfo.resize ((int) channelInfoSet.size());
|
||||
std::transform (channelInfoSet.begin(),
|
||||
channelInfoSet.end(),
|
||||
channelInfo.begin(),
|
||||
[] (auto x) { return x.makeChannelInfo(); });
|
||||
#endif
|
||||
|
||||
AddPropertyListener (kAudioUnitProperty_ContextName, auPropertyListenerDispatcher, this);
|
||||
|
|
|
|||
|
|
@ -194,7 +194,13 @@ public:
|
|||
channelInfos.add (channelInfo);
|
||||
}
|
||||
#else
|
||||
Array<AUChannelInfo> channelInfos = AudioUnitHelpers::getAUChannelInfo (processor);
|
||||
auto channelInfoSet = AudioUnitHelpers::getAUChannelInfo (processor);
|
||||
Array<AUChannelInfo> channelInfos;
|
||||
channelInfos.resize ((int) channelInfoSet.size());
|
||||
std::transform (channelInfoSet.begin(),
|
||||
channelInfoSet.end(),
|
||||
channelInfos.begin(),
|
||||
[] (auto x) { return x.makeChannelInfo(); });
|
||||
#endif
|
||||
|
||||
processor.setPlayHead (this);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** @cond */
|
||||
// This macro can be set if you need to override this internal name for some reason..
|
||||
#ifndef JUCE_STATE_DICTIONARY_KEY
|
||||
|
|
@ -363,7 +365,40 @@ struct AudioUnitHelpers
|
|||
return false;
|
||||
}
|
||||
|
||||
static Array<AUChannelInfo> getAUChannelInfo (const AudioProcessor& processor)
|
||||
struct Channels
|
||||
{
|
||||
SInt16 ins, outs;
|
||||
|
||||
std::pair<SInt16, SInt16> makePair() const noexcept { return std::make_pair (ins, outs); }
|
||||
AUChannelInfo makeChannelInfo() const noexcept { return { ins, outs }; }
|
||||
|
||||
bool operator< (const Channels& other) const noexcept { return makePair() < other.makePair(); }
|
||||
bool operator== (const Channels& other) const noexcept { return makePair() == other.makePair(); }
|
||||
|
||||
// The 'standard' layout with the most channels defined is AudioChannelSet::create9point1point6().
|
||||
// This value should be updated if larger standard channel layouts are added in the future.
|
||||
static constexpr auto maxNumChanToCheckFor = 16;
|
||||
};
|
||||
|
||||
/* Removes non-wildcard layouts that are already included by other wildcard layouts.
|
||||
*/
|
||||
static void removeNonWildcardLayouts (std::set<Channels>& layouts)
|
||||
{
|
||||
for (auto it = layouts.begin(); it != layouts.end();)
|
||||
{
|
||||
if ((it->ins != -1 && layouts.find ({ -1, it->outs }) != layouts.end())
|
||||
|| (it->outs != -1 && layouts.find ({ it->ins, -1 }) != layouts.end()))
|
||||
{
|
||||
it = layouts.erase (it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::set<Channels> getAUChannelInfo (const AudioProcessor& processor)
|
||||
{
|
||||
#ifdef JucePlugin_AUMainType
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfour-char-constants")
|
||||
|
|
@ -372,131 +407,130 @@ struct AudioUnitHelpers
|
|||
// A MIDI effect requires an output bus in order to determine the sample rate.
|
||||
// No audio will be written to the output bus, so it can have any number of channels.
|
||||
// No input bus is required.
|
||||
return { AUChannelInfo { 0, -1 } };
|
||||
return { Channels { 0, -1 } };
|
||||
}
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
#endif
|
||||
|
||||
Array<AUChannelInfo> channelInfo;
|
||||
const auto defaultInputs = processor.getChannelCountOfBus (true, 0);
|
||||
const auto defaultOutputs = processor.getChannelCountOfBus (false, 0);
|
||||
const auto hasMainInputBus = (AudioUnitHelpers::getBusCountForWrapper (processor, true) > 0);
|
||||
const auto hasMainOutputBus = (AudioUnitHelpers::getBusCountForWrapper (processor, false) > 0);
|
||||
|
||||
auto hasMainInputBus = (AudioUnitHelpers::getBusCountForWrapper (processor, true) > 0);
|
||||
auto hasMainOutputBus = (AudioUnitHelpers::getBusCountForWrapper (processor, false) > 0);
|
||||
|
||||
auto layout = processor.getBusesLayout();
|
||||
|
||||
// The 'standard' layout with the most channels defined is AudioChannelSet::create9point1point6().
|
||||
// This value should be updated if larger standard channel layouts are added in the future.
|
||||
constexpr auto maxNumChanToCheckFor = 16;
|
||||
|
||||
auto defaultInputs = processor.getChannelCountOfBus (true, 0);
|
||||
auto defaultOutputs = processor.getChannelCountOfBus (false, 0);
|
||||
|
||||
struct Channels
|
||||
{
|
||||
SInt16 ins, outs;
|
||||
|
||||
std::pair<SInt16, SInt16> makePair() const noexcept { return std::make_pair (ins, outs); }
|
||||
|
||||
bool operator< (const Channels& other) const noexcept { return makePair() < other.makePair(); }
|
||||
bool operator== (const Channels& other) const noexcept { return makePair() == other.makePair(); }
|
||||
};
|
||||
|
||||
SortedSet<Channels> supportedChannels;
|
||||
std::set<Channels> supportedChannels;
|
||||
|
||||
// add the current configuration
|
||||
if (defaultInputs != 0 || defaultOutputs != 0)
|
||||
supportedChannels.add ({ static_cast<SInt16> (defaultInputs),
|
||||
static_cast<SInt16> (defaultOutputs) });
|
||||
supportedChannels.insert ({ static_cast<SInt16> (defaultInputs),
|
||||
static_cast<SInt16> (defaultOutputs) });
|
||||
|
||||
for (auto inChanNum = hasMainInputBus ? 1 : 0; inChanNum <= (hasMainInputBus ? maxNumChanToCheckFor : 0); ++inChanNum)
|
||||
static const auto layoutsToTry = std::invoke ([&]
|
||||
{
|
||||
auto inLayout = layout;
|
||||
std::vector<AudioChannelSet> sets;
|
||||
|
||||
if (auto* inBus = processor.getBus (true, 0))
|
||||
if (! isNumberOfChannelsSupported (inBus, inChanNum, inLayout))
|
||||
continue;
|
||||
|
||||
for (auto outChanNum = hasMainOutputBus ? 1 : 0; outChanNum <= (hasMainOutputBus ? maxNumChanToCheckFor : 0); ++outChanNum)
|
||||
for (auto i = 1; i <= Channels::maxNumChanToCheckFor; ++i)
|
||||
{
|
||||
auto outLayout = inLayout;
|
||||
|
||||
if (auto* outBus = processor.getBus (false, 0))
|
||||
if (! isNumberOfChannelsSupported (outBus, outChanNum, outLayout))
|
||||
continue;
|
||||
|
||||
supportedChannels.add ({ static_cast<SInt16> (hasMainInputBus ? outLayout.getMainInputChannels() : 0),
|
||||
static_cast<SInt16> (hasMainOutputBus ? outLayout.getMainOutputChannels() : 0) });
|
||||
const auto setsWithSizeI = AudioChannelSet::channelSetsWithNumberOfChannels (i);
|
||||
std::copy (setsWithSizeI.begin(), setsWithSizeI.end(), std::back_inserter (sets));
|
||||
}
|
||||
}
|
||||
|
||||
const auto hasInOutMismatch = std::any_of (supportedChannels.begin(),
|
||||
supportedChannels.end(),
|
||||
[] (const Channels& x) { return x.ins != x.outs; });
|
||||
return sets;
|
||||
});
|
||||
|
||||
const auto computeHasUnsupportedLayout = [&] (bool isInput)
|
||||
std::vector<char> inputHasOutputRestrictions (layoutsToTry.size()), outputHasInputRestrictions (layoutsToTry.size());
|
||||
|
||||
for (const auto [inputIndex, inputLayout] : enumerate (layoutsToTry))
|
||||
{
|
||||
const auto hasMainBus = isInput ? hasMainInputBus : hasMainOutputBus;
|
||||
const auto begin = hasMainBus ? 1 : 0;
|
||||
const auto end = hasMainBus ? maxNumChanToCheckFor : 0;
|
||||
|
||||
for (auto chan = begin; chan <= end; ++chan)
|
||||
for (const auto [outputIndex, outputLayout] : enumerate (layoutsToTry))
|
||||
{
|
||||
const Channels channelConfig
|
||||
auto copy = processor.getBusesLayout();
|
||||
|
||||
if (! copy.inputBuses.isEmpty())
|
||||
copy.inputBuses.getReference (0) = inputLayout;
|
||||
|
||||
if (! copy.outputBuses.isEmpty())
|
||||
copy.outputBuses.getReference (0) = outputLayout;
|
||||
|
||||
if (processor.checkBusesLayoutSupported (copy))
|
||||
{
|
||||
static_cast<SInt16> (! isInput && hasInOutMismatch ? defaultInputs : chan),
|
||||
static_cast<SInt16> ( isInput && hasInOutMismatch ? defaultOutputs : chan)
|
||||
};
|
||||
|
||||
if (! supportedChannels.contains (channelConfig))
|
||||
return true;
|
||||
supportedChannels.insert ({ (SInt16) inputLayout.size(), (SInt16) outputLayout.size() });
|
||||
}
|
||||
else
|
||||
{
|
||||
inputHasOutputRestrictions[(size_t) inputIndex] = true;
|
||||
outputHasInputRestrictions[(size_t) outputIndex] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ! hasMainBus;
|
||||
};
|
||||
|
||||
const auto hasUnsupportedInput = computeHasUnsupportedLayout (true);
|
||||
const auto hasUnsupportedOutput = computeHasUnsupportedLayout (false);
|
||||
|
||||
for (const auto& supported : supportedChannels)
|
||||
{
|
||||
AUChannelInfo info;
|
||||
|
||||
// see here: https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html
|
||||
info.inChannels = static_cast<SInt16> (hasMainInputBus ? (hasUnsupportedInput ? supported.ins : (hasInOutMismatch && (! hasUnsupportedOutput) ? -2 : -1)) : 0);
|
||||
info.outChannels = static_cast<SInt16> (hasMainOutputBus ? (hasUnsupportedOutput ? supported.outs : (hasInOutMismatch && (! hasUnsupportedInput) ? -2 : -1)) : 0);
|
||||
|
||||
if (info.inChannels == -2 && info.outChannels == -2)
|
||||
info.inChannels = -1;
|
||||
|
||||
int j;
|
||||
for (j = 0; j < channelInfo.size(); ++j)
|
||||
if (info.inChannels == channelInfo.getReference (j).inChannels
|
||||
&& info.outChannels == channelInfo.getReference (j).outChannels)
|
||||
break;
|
||||
|
||||
if (j >= channelInfo.size())
|
||||
channelInfo.add (info);
|
||||
}
|
||||
|
||||
return channelInfo;
|
||||
}
|
||||
static constexpr auto identity = [] (auto x) { return x; };
|
||||
const auto noRestrictions = std::none_of (inputHasOutputRestrictions.begin(), inputHasOutputRestrictions.end(), identity)
|
||||
&& std::none_of (outputHasInputRestrictions.begin(), outputHasInputRestrictions.end(), identity);
|
||||
|
||||
static bool isNumberOfChannelsSupported (const AudioProcessor::Bus* b, int numChannels, AudioProcessor::BusesLayout& inOutCurrentLayout)
|
||||
{
|
||||
auto potentialSets = AudioChannelSet::channelSetsWithNumberOfChannels (static_cast<int> (numChannels));
|
||||
|
||||
for (auto set : potentialSets)
|
||||
if (noRestrictions)
|
||||
{
|
||||
auto copy = inOutCurrentLayout;
|
||||
|
||||
if (b->isLayoutSupported (set, ©))
|
||||
if (hasMainInputBus)
|
||||
{
|
||||
inOutCurrentLayout = copy;
|
||||
return true;
|
||||
if (hasMainOutputBus)
|
||||
return { Channels { -1, -2 } };
|
||||
|
||||
return { Channels { -1, 0 } };
|
||||
}
|
||||
|
||||
return { Channels { 0, -1 } };
|
||||
}
|
||||
|
||||
return false;
|
||||
const auto allMatchedLayoutsExclusivelySupported = std::invoke ([&]
|
||||
{
|
||||
for (SInt16 i = 1; i <= Channels::maxNumChanToCheckFor; ++i)
|
||||
if (supportedChannels.find ({ i, i }) == supportedChannels.end())
|
||||
return false;
|
||||
|
||||
return std::all_of (supportedChannels.begin(), supportedChannels.end(), [] (auto x) { return x.ins == x.outs; });
|
||||
});
|
||||
|
||||
if (allMatchedLayoutsExclusivelySupported)
|
||||
return { Channels { -1, -1 } };
|
||||
|
||||
std::set<Channels> filteredChannels;
|
||||
|
||||
for (auto& c : supportedChannels)
|
||||
{
|
||||
const auto findDistance = [&] (auto channelCount)
|
||||
{
|
||||
return std::distance (layoutsToTry.begin(),
|
||||
std::lower_bound (layoutsToTry.begin(),
|
||||
layoutsToTry.end(),
|
||||
channelCount,
|
||||
[] (auto a, auto b) { return a.size() < b; }));
|
||||
};
|
||||
|
||||
const auto findChannelCount = [&] (auto& restrictions,
|
||||
auto thisChannelCount,
|
||||
auto otherChannelCount,
|
||||
auto hasMainBus)
|
||||
{
|
||||
const auto lower = findDistance (otherChannelCount);
|
||||
const auto upper = findDistance (otherChannelCount + 1);
|
||||
|
||||
const auto wildcard = std::all_of (restrictions.begin() + lower,
|
||||
restrictions.begin() + upper,
|
||||
identity)
|
||||
? thisChannelCount
|
||||
: (SInt16) -1;
|
||||
return hasMainBus ? wildcard : (SInt16) 0;
|
||||
};
|
||||
|
||||
const auto ins = findChannelCount (outputHasInputRestrictions, c.ins, c.outs, hasMainInputBus);
|
||||
const auto outs = findChannelCount (inputHasOutputRestrictions, c.outs, c.ins, hasMainOutputBus);
|
||||
|
||||
const Channels layout { ins, outs };
|
||||
filteredChannels.insert (layout == Channels { -1, -1 } ? c : layout);
|
||||
}
|
||||
|
||||
removeNonWildcardLayouts (filteredChannels);
|
||||
|
||||
return filteredChannels;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE framework.
|
||||
Copyright (c) Raw Material Software Limited
|
||||
|
||||
JUCE is an open source framework subject to commercial or open source
|
||||
licensing.
|
||||
|
||||
By downloading, installing, or using the JUCE framework, or combining the
|
||||
JUCE framework with any other source code, object code, content or any other
|
||||
copyrightable work, you agree to the terms of the JUCE End User Licence
|
||||
Agreement, and all incorporated terms including the JUCE Privacy Policy and
|
||||
the JUCE Website Terms of Service, as applicable, which will bind you. If you
|
||||
do not agree to the terms of these agreements, we will not license the JUCE
|
||||
framework to you, and you must discontinue the installation or download
|
||||
process and cease use of the JUCE framework.
|
||||
|
||||
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
|
||||
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
|
||||
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
|
||||
|
||||
Or:
|
||||
|
||||
You may also use this code under the terms of the AGPLv3:
|
||||
https://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
|
||||
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
|
||||
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
|
||||
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "juce_AU_Shared.h"
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class AudioUnitPluginFormatTests final : public UnitTest
|
||||
{
|
||||
public:
|
||||
AudioUnitPluginFormatTests()
|
||||
: UnitTest ("AU Hosting", UnitTestCategories::audioProcessors)
|
||||
{
|
||||
}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("Permissive audio processor produces layout [-1, -2]");
|
||||
{
|
||||
struct ProcA : public MockAudioProcessor
|
||||
{
|
||||
ProcA()
|
||||
: MockAudioProcessor (BusesProperties().withInput ("Input", AudioChannelSet::stereo())
|
||||
.withOutput ("Output", AudioChannelSet::stereo())) {}
|
||||
|
||||
bool isBusesLayoutSupported (const BusesLayout&) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
ProcA processor;
|
||||
const auto layouts = AudioUnitHelpers::getAUChannelInfo (processor);
|
||||
expect (layouts == std::set { AudioUnitHelpers::Channels { -1, -2 } });
|
||||
}
|
||||
|
||||
beginTest ("Audio processor with matched I/O produces layout [-1, -1]");
|
||||
{
|
||||
struct ProcB : public MockAudioProcessor
|
||||
{
|
||||
ProcB()
|
||||
: MockAudioProcessor (BusesProperties().withInput ("Input", AudioChannelSet::stereo())
|
||||
.withOutput ("Output", AudioChannelSet::stereo())) {}
|
||||
|
||||
bool isBusesLayoutSupported (const BusesLayout& l) const override
|
||||
{
|
||||
return l.getMainInputChannelSet() == l.getMainOutputChannelSet();
|
||||
}
|
||||
};
|
||||
|
||||
ProcB processor;
|
||||
const auto layouts = AudioUnitHelpers::getAUChannelInfo (processor);
|
||||
expect (layouts == std::set { AudioUnitHelpers::Channels { -1, -1 } });
|
||||
}
|
||||
|
||||
beginTest ("Audio processor that supports any input with a two-channel output produces layout [-1, 2]");
|
||||
{
|
||||
struct ProcC : public MockAudioProcessor
|
||||
{
|
||||
ProcC()
|
||||
: MockAudioProcessor (BusesProperties().withInput ("Input", AudioChannelSet::stereo())
|
||||
.withOutput ("Output", AudioChannelSet::stereo())) {}
|
||||
|
||||
bool isBusesLayoutSupported (const BusesLayout& l) const override
|
||||
{
|
||||
return l.getMainOutputChannelSet() == AudioChannelSet::stereo();
|
||||
}
|
||||
};
|
||||
|
||||
ProcC processor;
|
||||
const auto layouts = AudioUnitHelpers::getAUChannelInfo (processor);
|
||||
expect (layouts == std::set { AudioUnitHelpers::Channels { -1, 2 } });
|
||||
}
|
||||
|
||||
beginTest ("Audio processor that supports any output with a 6-channel input produces layout [6, -1]");
|
||||
{
|
||||
struct ProcD : public MockAudioProcessor
|
||||
{
|
||||
ProcD()
|
||||
: MockAudioProcessor (BusesProperties().withInput ("Input", AudioChannelSet::create5point1())
|
||||
.withOutput ("Output", AudioChannelSet::create5point1())) {}
|
||||
|
||||
bool isBusesLayoutSupported (const BusesLayout& l) const override
|
||||
{
|
||||
return l.getMainInputChannelSet() == AudioChannelSet::create5point1();
|
||||
}
|
||||
};
|
||||
|
||||
ProcD processor;
|
||||
const auto layouts = AudioUnitHelpers::getAUChannelInfo (processor);
|
||||
expect (layouts == std::set { AudioUnitHelpers::Channels { 6, -1 } });
|
||||
}
|
||||
|
||||
beginTest ("Audio processor that supports both above layouts produces [-1, 2] and [6, -1]");
|
||||
{
|
||||
struct ProcE : public MockAudioProcessor
|
||||
{
|
||||
ProcE()
|
||||
: MockAudioProcessor (BusesProperties().withInput ("Input", AudioChannelSet::create5point1())
|
||||
.withOutput ("Output", AudioChannelSet::stereo())) {}
|
||||
|
||||
bool isBusesLayoutSupported (const BusesLayout& l) const override
|
||||
{
|
||||
return l.getMainOutputChannelSet() == AudioChannelSet::stereo()
|
||||
|| l.getMainInputChannelSet() == AudioChannelSet::create5point1();
|
||||
}
|
||||
};
|
||||
|
||||
ProcE processor;
|
||||
const auto layouts = AudioUnitHelpers::getAUChannelInfo (processor);
|
||||
expect (layouts == std::set { AudioUnitHelpers::Channels { -1, 2 },
|
||||
AudioUnitHelpers::Channels { 6, -1 } });
|
||||
}
|
||||
|
||||
beginTest ("Audio processor that supports only stereo and 5.1 produces [2, 2], [6, 6], [2, 6], and [6, 2]");
|
||||
{
|
||||
struct ProcF : public MockAudioProcessor
|
||||
{
|
||||
ProcF()
|
||||
: MockAudioProcessor (BusesProperties().withInput ("Input", AudioChannelSet::create5point1())
|
||||
.withOutput ("Output", AudioChannelSet::stereo())) {}
|
||||
|
||||
bool isBusesLayoutSupported (const BusesLayout& l) const override
|
||||
{
|
||||
const std::array supported { AudioChannelSet::stereo(), AudioChannelSet::create5point1() };
|
||||
|
||||
return std::find (supported.begin(), supported.end(), l.getMainInputChannelSet()) != supported.end()
|
||||
&& std::find (supported.begin(), supported.end(), l.getMainOutputChannelSet()) != supported.end();
|
||||
}
|
||||
};
|
||||
|
||||
ProcF processor;
|
||||
const auto layouts = AudioUnitHelpers::getAUChannelInfo (processor);
|
||||
expect (layouts == std::set { AudioUnitHelpers::Channels { 2, 2 },
|
||||
AudioUnitHelpers::Channels { 2, 6 },
|
||||
AudioUnitHelpers::Channels { 6, 2 },
|
||||
AudioUnitHelpers::Channels { 6, 6 } });
|
||||
}
|
||||
|
||||
beginTest ("Complex layout is supported");
|
||||
{
|
||||
struct ProcG : public MockAudioProcessor
|
||||
{
|
||||
ProcG()
|
||||
: MockAudioProcessor (BusesProperties().withInput ("Input", AudioChannelSet::create9point1point6())
|
||||
.withOutput ("Output", AudioChannelSet::create9point1point6())) {}
|
||||
|
||||
bool isBusesLayoutSupported (const BusesLayout& l) const override
|
||||
{
|
||||
using ACS = juce::AudioChannelSet;
|
||||
|
||||
const auto input = l.getMainInputChannelSet();
|
||||
const auto output = l.getMainOutputChannelSet();
|
||||
|
||||
if (output == ACS::mono())
|
||||
return input == ACS::mono();
|
||||
|
||||
if (output == ACS::stereo())
|
||||
return input == ACS::mono() || input == ACS::stereo();
|
||||
|
||||
if (output == ACS::create9point1point6())
|
||||
{
|
||||
return input == ACS::mono()
|
||||
|| input == ACS::stereo()
|
||||
|| input == ACS::createLCR()
|
||||
|| input == ACS::quadraphonic()
|
||||
|| input == ACS::create5point0()
|
||||
|| input == ACS::create5point1()
|
||||
|| input == ACS::create7point0()
|
||||
|| input == ACS::create7point1()
|
||||
|| input == ACS::create7point0point2()
|
||||
|| input == ACS::create5point1point4()
|
||||
|| input == ACS::create7point0point4()
|
||||
|| input == ACS::create7point1point4()
|
||||
|| input == ACS::create7point0point6()
|
||||
|| input == ACS::create7point1point6()
|
||||
|| input == ACS::create9point0point6()
|
||||
|| input == ACS::create9point1point6();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
ProcG processor;
|
||||
const auto layouts = AudioUnitHelpers::getAUChannelInfo (processor);
|
||||
expect (layouts == std::set { AudioUnitHelpers::Channels { 1, 1 },
|
||||
AudioUnitHelpers::Channels { 1, 2 },
|
||||
AudioUnitHelpers::Channels { 2, 2 },
|
||||
AudioUnitHelpers::Channels { 1, 16 },
|
||||
AudioUnitHelpers::Channels { 2, 16 },
|
||||
AudioUnitHelpers::Channels { 3, 16 },
|
||||
AudioUnitHelpers::Channels { 4, 16 },
|
||||
AudioUnitHelpers::Channels { 5, 16 },
|
||||
AudioUnitHelpers::Channels { 6, 16 },
|
||||
AudioUnitHelpers::Channels { 7, 16 },
|
||||
AudioUnitHelpers::Channels { 8, 16 },
|
||||
AudioUnitHelpers::Channels { 9, 16 },
|
||||
AudioUnitHelpers::Channels { 10, 16 },
|
||||
AudioUnitHelpers::Channels { 11, 16 },
|
||||
AudioUnitHelpers::Channels { 12, 16 },
|
||||
AudioUnitHelpers::Channels { 13, 16 },
|
||||
AudioUnitHelpers::Channels { 14, 16 },
|
||||
AudioUnitHelpers::Channels { 15, 16 },
|
||||
AudioUnitHelpers::Channels { 16, 16 } });
|
||||
}
|
||||
|
||||
beginTest ("Audio processor that supports only stereo out reports [0, 2]");
|
||||
{
|
||||
struct ProcH : public MockAudioProcessor
|
||||
{
|
||||
ProcH()
|
||||
: MockAudioProcessor (BusesProperties().withOutput ("Output", AudioChannelSet::stereo())) {}
|
||||
|
||||
bool isBusesLayoutSupported (const BusesLayout& l) const override
|
||||
{
|
||||
return l.inputBuses.isEmpty() && l.getMainOutputChannelSet() == AudioChannelSet::stereo();
|
||||
}
|
||||
};
|
||||
|
||||
ProcH processor;
|
||||
const auto layouts = AudioUnitHelpers::getAUChannelInfo (processor);
|
||||
expect (layouts == std::set { AudioUnitHelpers::Channels { 0, 2 } });
|
||||
}
|
||||
|
||||
beginTest ("Audio processor that supports any out but no in reports [0, -1]");
|
||||
{
|
||||
struct ProcI : public MockAudioProcessor
|
||||
{
|
||||
ProcI()
|
||||
: MockAudioProcessor (BusesProperties().withOutput ("Output", AudioChannelSet::stereo())) {}
|
||||
|
||||
bool isBusesLayoutSupported (const BusesLayout& l) const override
|
||||
{
|
||||
return l.inputBuses.isEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
ProcI processor;
|
||||
const auto layouts = AudioUnitHelpers::getAUChannelInfo (processor);
|
||||
expect (layouts == std::set { AudioUnitHelpers::Channels { 0, -1 } });
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct MockAudioProcessor : public AudioProcessor
|
||||
{
|
||||
using AudioProcessor::AudioProcessor;
|
||||
|
||||
const String getName() const override { return "Basic Processor"; }
|
||||
double getTailLengthSeconds() const override { return {}; }
|
||||
bool acceptsMidi() const override { return {}; }
|
||||
bool producesMidi() const override { return {}; }
|
||||
AudioProcessorEditor* createEditor() override { return {}; }
|
||||
bool hasEditor() const override { return {}; }
|
||||
int getNumPrograms() override { return 1; }
|
||||
int getCurrentProgram() override { return {}; }
|
||||
void setCurrentProgram (int) override {}
|
||||
const String getProgramName (int) override { return {}; }
|
||||
void changeProgramName (int, const String&) override {}
|
||||
void getStateInformation (MemoryBlock&) override {}
|
||||
void setStateInformation (const void*, int) override {}
|
||||
void prepareToPlay (double, int) override {}
|
||||
void releaseResources() override {}
|
||||
bool supportsDoublePrecisionProcessing() const override { return {}; }
|
||||
bool isMidiEffect() const override { return {}; }
|
||||
void reset() override {}
|
||||
void setNonRealtime (bool) noexcept override {}
|
||||
void processBlock (AudioBuffer<float>&, MidiBuffer&) override {}
|
||||
using AudioProcessor::processBlock;
|
||||
};
|
||||
};
|
||||
|
||||
static AudioUnitPluginFormatTests auPluginFormatTests;
|
||||
|
||||
} // namespace juce
|
||||
|
|
@ -221,6 +221,10 @@ private:
|
|||
#include "format_types/juce_VST3PluginFormat_test.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_PLUGINHOST_AU && (JUCE_MAC || JUCE_IOS)
|
||||
#include "format_types/juce_AudioUnitPluginFormat_test.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_PLUGINHOST_LV2 && (! (JUCE_ANDROID || JUCE_IOS))
|
||||
#include "format_types/juce_LV2PluginFormat_test.cpp"
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue