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