BR: Wrong AUChannelInfo reported for some bus layout combinations

I’m having a problem with a plugin failing auval validation. I bumped into this while upgrading to JUCE 8, but I think the underlying issue was lurking in JUCE 7, as well.

Here’s a minimal example of a plugin processor constructor and isBusesLayoutSupported . I can produce the error using the JUCE 8 develop branch.

const auto defaultFormat = juce::AudioChannelSet::create9point1point6();

PluginProcessor::PluginProcessor()
     : AudioProcessor (BusesProperties()
                       .withInput  ("Input",  defaultFormat, true)
                       .withOutput ("Output", defaultFormat, true)
                       )
{
}

bool PluginProcessor::isBusesLayoutSupported(const BusesLayout& layouts) const
{
    using ACS = juce::AudioChannelSet;
    
    auto input = layouts.getMainInputChannelSet();
    auto output = layouts.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()                || // 1
               input == ACS::stereo()              || // 2
               input == ACS::createLCR()           || // 3
               input == ACS::quadraphonic()        || // 4
               input == ACS::create5point0()       || // 5
               input == ACS::create5point1()       || // 6
               input == ACS::create7point0()       || // 7
               input == ACS::create7point1()       || // 8
               input == ACS::create7point0point2() || // 9
               input == ACS::create5point1point4() || // 10
               input == ACS::create7point0point4() || // 11
               input == ACS::create7point1point4() || // 12
               input == ACS::create7point0point6() || // 13
               input == ACS::create7point1point6() || // 14
               input == ACS::create9point0point6() || // 15
               input == ACS::create9point1point6();   // 16
    }

    return false;
}

Running auval on this plugin results in these channel layouts being reported:
[-1, 1] [-1, 2] [-1, 16]

However, those aren’t quite right, and it leads to the plugin failing to be validated. You can see that mono output should only be allowed with mono input, and stereo output should only be allowed with mono/stereo input.

I think the issue lies in AudioUnitHelpers::getAUChannelInfo. I spent some time debugging it, and the supportedChannelsvariable seems to be reasonably populated. However, the hasUnsupportedInput variable ends up being false since there are valid inputs with channels numbering 1 thru 16, so when these are converted to AUChannelInfo, the .inChannels member is always set to -1. This seems like it might be a bug. Even though there are valid layouts with all of those inputs, they’re not necessarily able to be paired with every number of output channels. For example, stereo outputs are only compatible with mono/stereo inputs, but it’s still reported as [-1, 2].

I think the reason this has only become a problem for me in JUCE 8 is that there were changes to AudioChannelSet::channelSetsWithNumberOfChannels . It now reports more channel sets than it used to, so now when getAUChannelInfo is creating its list of supported channels, its able to fill in some gaps that were previously missing, so it’s now more likely to end up with a contiguous list of supported input channels, 1 to 16.

1 Like

Would this would be a better way to determine when to set hasUnsupportedInput and hasUnsupportedOutput ?

juce::SortedSet<int> fullOutputChannelSet, fullInputChannelSet;

for (auto chanNum = hasMainInputBus ? 1 : 0; chanNum <= (hasMainInputBus ? maxNumChanToCheckFor : 0); ++chanNum)
{
    fullInputChannelSet.add(chanNum);
}

for (auto chanNum = hasMainOutputBus ? 1 : 0; chanNum <= (hasMainOutputBus ? maxNumChanToCheckFor : 0); ++chanNum)
{
    fullOutputChannelSet.add(chanNum);
}

juce::HashMap<int, juce::SortedSet<int>> inputsToOutputs, outputsToInputs;
juce::SortedSet<int> reportedInputs, reportedOutputs;

for (const auto& supportedChannel : supportedChannels)
{
    const auto& ins = supportedChannel.ins;
    const auto& outs = supportedChannel.outs;
    inputsToOutputs.getReference(ins).add(outs);
    outputsToInputs.getReference(outs).add(ins);
    reportedInputs.add(ins);
    reportedOutputs.add(outs);
}

for (const auto& input : reportedInputs)
{
    const auto& outputsForThisInput {inputsToOutputs.getReference(input)};
    if (outputsForThisInput != fullOutputChannelSet)
    {
        hasUnsupportedInput = true;
        break;
    }
}

for (const auto& output : reportedOutputs)
{
    const auto& inputsForThisOutput {outputsToInputs.getReference(output)};
    if (inputsForThisOutput != fullInputChannelSet)
    {
        hasUnsupportedOutput = true;
        break;
    }
}

2 Likes

JUCE team, any thoughts on this? It appears to be a bug that will affect any AU plugins targeting certain channel layouts.

Thanks, it looks like there are a few edge-cases that aren’t handled correctly by the current implementation of getAUChannelInfo(). I’ve added some new tests and had a go at improving the implementation - please could you try out the attached patch and check whether it improves things in your plugin(s)?

au-channel-layout.patch (37.4 KB)

1 Like

Thanks! I believe I applied the patch correctly. It didn’t seem to patch smoothly onto the develop branch, but I think I was able to apply it correctly manually.

It appears to solve the problem for me. Big thanks to the JUCE team! I’ll keep an eye out for the fix to get committed.

1 Like

Thanks again for reporting this issue and testing out the fix. We’ve now pushed this change to the develop branch:

2 Likes

I’m having a similar issue, where my plugin fails auval’s channel configuration tests and Logic offers the plugin only as a stereo device despite my intention for it to also work in mono.

I’m on Juce 8.0.14 (would moving to the develop branch help?) and the “plugin channel configurations” field in Projucer is blank.

My AudioProcessor constructor initializer list looks like this:

SpectrumAnalyzerAudioProcessor::SpectrumAnalyzerAudioProcessor() :
AudioProcessor (AudioProcessor::BusesProperties()
.withInput ("Input",     AudioChannelSet::stereo(), true)
.withOutput("Output",    AudioChannelSet::stereo(), true)
.withInput ("Sidechain", AudioChannelSet::stereo(), false)
),
parameters(*this, nullptr, "PARAMETERS", createParameterLayout())
{
// UNRELATED STUFF
}

And my isBusesLayoutSupported():

bool SpectrumAnalyzerAudioProcessor::isBusesLayoutSupported(const BusesLayout& layouts) const
{
const auto mono    = AudioChannelSet::mono();
const auto stereo  = AudioChannelSet::stereo();
const auto mainIn  = layouts.getMainInputChannelSet();
const auto mainOut = layouts.getMainOutputChannelSet();
const auto SCin    = layouts.getChannelSet(true, 1);

if(mainOut!=mono && mainOut!=stereo) return false;
if(mainOut!=mainIn)				     return false;
if(!SCin.isDisabled())
{
	if(SCin!=mono && SCin!=stereo)   return false;
	if(SCin==stereo && mainIn==mono) return false;
}
return true;
}

The relevant output from auval looks like this:

FORMAT TESTS:



Reported Channel Capabilities (explicit):

      [2, 2]  



Input/Output Channel Handling:

1-1   1-2   1-4   1-5   1-6   1-7   1-8   2-2   2-4   2-5   2-6   2-7   2-8   4-4   4-5   5-5   6-6   7-7   8-8

                                          X                                                                       



# # AudioChannelLayouts (5), Input Scope:

ChannelLayout is Writable: T

The Unit publishes the following Channel Layouts:

  0x640001, 0x650002, 0x660002, 0x670002, 0x6A0002, 



Is Audio Channel Layout Available:

Mono    Stereo  Binau.  AU_4    Ambi.   AU_5    AU_5_0  AU_6    AU_6_0  AU_7_0  AU_7_0F AU_8    AU_5_1  AU_6_1  AU_7_1  AU_7_1F 

X       X       X                                                                                                               

ERROR: Mismatch between reported channel layouts and reported numChannels



Current Format:AudioStreamBasicDescription:  2 ch,  44100 Hz, Float32, deinterleaved

Current Format Tag = 0x650002, New Format Tag = 0x640001, Success - did not reset format with mis-matched layout



Current Format:AudioStreamBasicDescription:  2 ch,  44100 Hz, Float32, deinterleaved

Current Format Tag = 0x650002, New Format Tag = 0x650002, Successsful



Current Format:AudioStreamBasicDescription:  2 ch,  44100 Hz, Float32, deinterleaved

Current Format Tag = 0x650002, New Format Tag = 0x6A0002, Successsful



ERROR: Cannot verify Audio Channel Layouts as Format handling has problems



* * FAIL

I reviewed the Juce channel config tutorial and several other online resources. What am I doing wrong? Do I need to mention AudioChannelSet::mono() somewhere in the constructor? (I want it to work in stereo & mono, sidechain or not, as long as the sidechain doesn’t have more channels than the main bus.)

Note, this was working fine a year or so ago on an earlier version of Juce, with substantially the same source code as shown above except for the line “if(SCin==stereo && mainIn==mono) return false;”

Edit: in case this helps, if I delete the sidechain line from my constructor, the validation issue goes away and auval sees the mono configuration.

Update: my issue was resolved by removing a check that I had inside isBusesLayoutSupported() that we’re not getting a stereo sidechain on a mono signal. (I moved this check elsewhere in the code.) I also had to increment the plugin version number in order for Logic to properly re-scan it.