AAX Sidechain Issue

Hello friends, avid dev support suggested I post this here.

I’m trying to setup my AAX plugin to use mono or stereo with optional sidechain - (note: Pro Tools only accepts mono sidechain).

Looking to support:

  • Mono Input / Mono Output / Optional sidechain (mono)
  • Stereo Input / Stereo Output / Optional sidechain (mono)

Attempt 1: Stereo / Mono BusesProperties with Mono Sidechain
AudioProcessor (BusesProperties()
.withInput (“Input”, AudioChannelSet::stereo()) // NOTE: ALSO TRIED MONO HERE
.withOutput(“Output”, AudioChannelSet::stereo()) // NOTE: ALSO TRIED MONO HERE
.withInput (“Sidechain”, AudioChannelSet::mono())

Pro Tools / isBusesLayoutSupported output (re-ordered and duplicates removed):

ACCEPTED - Input: Stereo, Output: Stereo, Sidechain: Mono
ACCEPTED - Input: Stereo, Output: Stereo, Sidechain: Disabled
ACCEPTED - Input: Mono, Output: Mono, Sidechain: Mono
ACCEPTED - Input: Mono, Output: Mono, Sidechain: Disabled

Pro Tools Behaviour

  • Appears as Mono plugin with key input available
  • Does not appear as stereo plugin

Attempt 2: Stereo / Mono BusesProperties with NO Sidechain
AudioProcessor (BusesProperties()
.withInput (“Input”, AudioChannelSet::stereo())
.withOutput(“Output”, AudioChannelSet::stereo()))

Pro Tools / isBusesLayoutSupported output (re-ordered and duplicates removed):
ACCEPTED - Input: Stereo, Output: Stereo, Sidechain: Disabled
ACCEPTED - Input: Mono, Output: Mono, Sidechain: Disabled

Pro Tools Behaviour

  • Appears as stereo plugin without sidechain/key input
  • Appears as mono plugin without sidechain/key input
  • Sidechain: Disabled on every check to isBusesLayoutSupported

isBusesLayoutSupported


bool MyAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
{
    if (juce::PluginHostType().isProTools())
    {
        AudioChannelSet mainInput = layouts.getMainInputChannelSet();
        AudioChannelSet mainOutput = layouts.getMainOutputChannelSet();
        AudioChannelSet sidechainInput = layouts.getChannelSet(true, 1); // (true = input, 1 = second bus)

        String inputDescription = mainInput.isDisabled() ? "Disabled " : mainInput.getDescription();
        String outputDescription = mainOutput.isDisabled() ? "Disabled " : mainOutput.getDescription();
        String sidechainDescription = sidechainInput.isDisabled() ? "Disabled " : sidechainInput.getDescription();

        bool monoInput = mainInput == AudioChannelSet::mono();
        bool monoOutput = mainOutput == AudioChannelSet::mono();
        bool validSidechain = (sidechainInput == AudioChannelSet::mono()) || sidechainInput == AudioChannelSet::disabled();

// NOTE: also tried bool validSidechain = sidechainInput == AudioChannelSet::mono();

        if (monoInput && monoOutput && validSidechain)
        {
            DBG("ACCEPTED - Input: " + inputDescription +
                 ", Output: " + outputDescription +
                 ", Sidechain: " + sidechainDescription);
            return true;
        }

        bool stereoInput = mainInput == AudioChannelSet::stereo();
        bool stereoOutput = mainOutput == AudioChannelSet::stereo();

        if (stereoInput && stereoOutput && validSidechain)
        {
            DBG("ACCEPTED - Input: " + inputDescription +
                 ", Output: " + outputDescription +
                 ", Sidechain: " + sidechainDescription);
            return true;
        }

        DBG("REJECTED - Input: " + inputDescription +
             ", Output: " + outputDescription +
             ", Sidechain: " + sidechainDescription);

        return false;
    }

AAXClientExtensions

I tried leaving this as default (no luck), and then tried overwriting like so:

struct CompressorAAXClientExtensions : public juce::AAXClientExtensions
    {
        int32 getPluginIDForMainBusConfig(const juce::AudioChannelSet& mainInputLayout,
                                          const juce::AudioChannelSet& mainOutputLayout,
                                          bool idForAudioSuite) const override
        {
            DBG ("idForAudioSuite? : " << (idForAudioSuite ? "TRUE" : "FALSE"));
            int32 result = 0;

            auto inputChannelTypes = mainInputLayout.getChannelTypes();
            int inputIdx = 0;
            for (auto& inputType : inputChannelTypes)
            {
                DBG("Input : Index " << inputIdx << " " << AudioChannelSet::getChannelTypeName(inputType));
                inputIdx++;
            }

            auto outputChannelTypes = mainOutputLayout.getChannelTypes();
            int outputIdx = 0;
            for (auto& outputType : outputChannelTypes)
            {
                DBG("Output : Index " << outputIdx << " " << AudioChannelSet::getChannelTypeName(outputType));
                outputIdx++;
            }

            if (idForAudioSuite)
            {
                if (mainOutputLayout.size() == 1)
                {
                    result = 1;
                }
                if (mainOutputLayout.size() == 2)
                {
                    result = 2;
                }
            }
            else
            {
                if (mainOutputLayout.size() == 1)
                {
                    result = 3;
                }
                if (mainOutputLayout.size() == 2)
                {
                    result = 4;
                }
            }
            DBG ("getPluginIDForMainBusConfig result is " << result);
            return result;
        }
    };

Which gives me:

ACCEPTED - Input: Mono, Output: Mono, Sidechain: Mono
***********
idForAudioSuite? : FALSE
Input : Index 0 Centre
Output : Index 0 Centre
getPluginIDForMainBusConfig result is 3
***********
idForAudioSuite? : TRUE
Input : Index 0 Centre
Output : Index 0 Centre
getPluginIDForMainBusConfig result is 1

As well as:

ACCEPTED - Input: Stereo, Output: Stereo, Sidechain: Mono
***********
idForAudioSuite? : FALSE
Input : Index 0 Left
Input : Index 1 Right
Output : Index 0 Left
Output : Index 1 Right
getPluginIDForMainBusConfig result is 4
***********
idForAudioSuite? : TRUE
Input : Index 0 Left
Input : Index 1 Right
Output : Index 0 Left
Output : Index 1 Right
getPluginIDForMainBusConfig result is 2

But I still only see a mono plugin in Pro Tools


Am I missing anything here? Perhaps I need to supply the PageFile, but I thought this would be handled by JUCE if I correctly do everything above.

Would be great if bus layouts tutorial could be updated to include some AAX → Configuring the right bus layouts for your plugins - JUCE

Thanks!

if (juce::PluginHostType().isProTools())

Checking for ProTools in this way is not recommended. Newer versions of Avid hosts use a dedicated plugin scanner executable (not Pro Tools) to determine plugin capabilities, including bus layouts. Checking for Pro Tools like this will produce different results between the plugin scanner and host.

It’s a better idea to check whether AudioProcessor::wrapperType is wrapperType_AAX, as this will produce consistent results between Pro Tools and the plugin scanner executable.

1 Like

So to get it right, you’d like stereo side-chain?

If that’s the case, there’s no stereo side-chain in Pro Tools at least at this time.

It seems that my plugin channel layout is identical to you. I don’t add Pro Tools check or wrapperType check though.

bool PluginProcessor::isBusesLayoutSupported(const BusesLayout &layouts) const {
    if (layouts.getMainInputChannelSet() == juce::AudioChannelSet::stereo() &&
        layouts.getMainOutputChannelSet() == juce::AudioChannelSet::stereo() &&
        (layouts.getChannelSet(true, 1).isDisabled() ||
         layouts.getChannelSet(true, 1) == juce::AudioChannelSet::mono() ||
         layouts.getChannelSet(true, 1) == juce::AudioChannelSet::stereo())) {
        return true;
    }
    if (layouts.getMainInputChannelSet() == juce::AudioChannelSet::mono() &&
        layouts.getMainOutputChannelSet() == juce::AudioChannelSet::mono() &&
        (layouts.getChannelSet(true, 1).isDisabled() ||
         layouts.getChannelSet(true, 1) == juce::AudioChannelSet::mono() ||
         layouts.getChannelSet(true, 1) == juce::AudioChannelSet::stereo())) {
        return true;
    }
    return false;
}
1 Like

It’s a better idea to check whether AudioProcessor::wrapperType is wrapperType_AAX, as this will produce consistent results between Pro Tools and the plugin scanner executable.

Nailed it thanks!

The Pro Tools plugin scan was taking the non AAX path in isBusesSupported() since the isProTools check failed.

isBusesSupported() runs again when the plugin is loaded, which is why it looked like it was succeeding.