Logic Pro wrong channel layouts

Ok, so i am doing some testing with Logic Pro (Rosetta) on M1 Max and hell, things seem to be wrong. Here it comes:

LCRS in Logic is identified as Quadraphonic in Juce
6.1 (ES/EX) in Logic is identified as 7.0 Surround in Juce
7.1 (SDDS) in Logic is identified as 7.1 Surround in Juce
5.1.2 in Logic is identified as 7.1 Surround in Juce
5.1.4 in Logic is identified as Discrete #10 in Juce
7.1.2 in Logic is identified as Discrete #10 in Juce
7.1.4 in Logic is identified as Discrete #12 in Juce

Logic 10.7.2 (Rosetta), M1 Max and Juce master 6.1.5

Just tested on Intel Mac as well, Logic 10.7.2 and Logic 10.6. Same errors occur.

I don’t know if that is all related, but to possibly funnel things together I am linking another currently discussed thread:

I’m aware of the issue and am currently investigating.

I’ve lost the best part of a day to this issue, but I think it does mostly work after all. Logic caches channel layouts of plugins in a slightly obscure way that doesn’t always seem to be cleared by running killall -9 AudioComponentRegistrar. This means if you modify the isBusesLayoutFunction and reload the plugin in Logic, it’s not guaranteed to query the allowed bus configurations. I think I must have accidentally tested and debugged with a plugin that had cached channel information. After changing the plugin code, and doing a full plugin reset from Logic, the channel layout tags seem to be applied correctly, with some caveats.

I’ve tested with Logic 10.6.3 on macOS 10.15.7 and Logic 10.7.2 on macOS 12.2.1. I used the SurroundPlugin demo from the examples folder to check the layouts. This plugin allows noise to be triggered on each named channel. By placing a Level Meter plugin on the same channel strip, it’s possible to verify that the channels match up between plugins. When changing the project surround format, you may need to remove and re-instantiate all plugin instances. For effect plugins, you might need to place a surround-format instrument on the channel strip before being able to instantiate the effect in surround-format.

My results are as follows:

  • Quadraphonic, LCRS, 5.1, 7.1 (SDDS): All of these formats work correctly.
  • 6.1: Plugin is instantiated as 6.0 instead. When debugging, I can see that Logic is explicitly requesting a 6_0 layout, so I think this issue is on their side. I also tested with some non-JUCE plugins to verify. Pro Q 3 also gets instantiated as 6_0, and Neutron 3 fails to instantiate at all.
  • 7.1 (3/4.1): On Logic 10.6.3 (Intel), plugin fails to instantiate before ever trying to set the channel layout. Again, I tested Neutron 3 and Pro Q 3 and both of those plugins fail to instantiate in this format too, so I suspect the issue is in Logic. On Logic 10.7.2 (Arm) the plugin opens and has the correct channel layout.

Logic 10.7.2 also has 4 Atmos layouts. All of those other than 7.1.4 work correctly. The issue with 7.1.4 is a known bug in Logic.

I’ve attached a gif to demonstrate that the SurroundPlugin demo really does report the channels correctly:

out

If you’re still having problems getting this to work, I’d recommend:

  • Change the plugin’s ID code in the Projucer/CMake to force the channel layouts to be re-scanned by Logic.
  • Force a Full Audio Unit Reset from the Plugin Manager window in Logic.
  • Test the SurroundPlugin demo yourself. If thie SurroundPlugin works, but your own plugin doesn’t, then you can take code from the implementation of the SurroundPlugin (especially isBusesLayoutSupported). Remember to run the previous two steps every time you modify the supported channel layouts of your plugin.
1 Like

Hi reuk,

now thanks for the tip with the SurroundTutorial.

Well here it comes again:

isBusesLayoutSupported originally contains which works fine in Logic:

    return ((! layouts.getMainInputChannelSet() .isDiscreteLayout())                // [1]
         && (! layouts.getMainOutputChannelSet().isDiscreteLayout())
         && (layouts.getMainInputChannelSet() == layouts.getMainOutputChannelSet()) // [2]
         && (! layouts.getMainInputChannelSet().isDisabled()));                     // [3]

If I replace it with:

    if ( PluginHostType().isLogic() )
        return ((! layouts.getMainInputChannelSet() .isDiscreteLayout())                // [1]
             && (! layouts.getMainOutputChannelSet().isDiscreteLayout())
             && (layouts.getMainInputChannelSet() == layouts.getMainOutputChannelSet()) // [2]
             && (! layouts.getMainInputChannelSet().isDisabled()));                     // [3]
    return true;

Things go totally south in Logic. Loading SurroundTutorial up on a LCRS channel shows me Quadraphonic in the plugin, which is clearly wrong.

I need to use the the above mentioned check in Logic ONLY as it breaks things for me in other hosts where I need discrete layouts for example.

What am I doing wrong?

Do you need discrete layouts in other AU hosts? Could you check whether the current plugin type is AU, rather than checking for Logic as a host?

I think the problem is that Logic uses a completely separate executable (com.apple.audio.InfoHelper) to scan and cache the supported channel layouts. To get the channel layouts showing up correctly in Logic, isBusesLayout would need to check for Logic and the InfoHelper tool, and potentially auval too. I think this is a bad approach, because it’s quite fragile. Checking whether the plugin is an AU feels a bit more robust, so I’d recommend that approach instead.

Well my plugin offers up to 256 inputs and outputs, which is currently only reached by Pyramix. But for example Reaper with AU can use 64 channels and i want to provide that. Other hosts might also change and offer AU with many channels as discrete layout.

I do not want to tell Reaper users (and possibly others) to not use AU if they want many channels. I rather prefer a programmatic solution. So, if there is a way to check for infohelper and auvaltool, please tell me how.

And by the way, Surround Tutorial doesn’t get past 8 in/out in Reaper.

So, another finding is that at least in Studio One, Logic and Reaper it always seams to be PluginHostType().pluginval which counts in isBusesLayoutSupported when it comes to scanning AU.

So it seems I have no choice.

What I do not understand is, that when returning always true, which I did until now for AU, the speaker layouts get garbled up in Logic so much while when restricting everything by
return ((! layouts.getMainInputChannelSet() .isDiscreteLayout()) // [1] && (! layouts.getMainOutputChannelSet().isDiscreteLayout()) && (layouts.getMainInputChannelSet() == layouts.getMainOutputChannelSet()) // [2] && (! layouts.getMainInputChannelSet().isDisabled()));
things seem to be alright in Logic.

Why is that?

Btw. when I always return true, in Reaper i have up to 64 channels with AU…

Oh and one more here:

6.1 (ES/EX) is now recognized as 6.0 surround by Juce.
5.1.4, 7.1.2 and 7.1.4 are not even loadable. Original SurroundTutorial cannot be instantiated, though I can select this format for SurroundTutorial!!! This concurs with my first post here where these 3 formats are recognized as Discrete.

Something is seriously wrong IN JUCE!

Please can you explain what you mean? Pluginval is different to auval. Pluginval is a plugin validation/testing tool developed by Tracktion, whereas auval is an operating system component on Apple devices. The PluginHostType should never be set to ‘pluginval’ in an Apple host. If it is, that’s probably a bug in JUCE.

When you return true unconditionally, the busIgnoresLayout check in juce_AU_Wrapper.mm determines that the channel layout and order does not matter to the plugin, and the plugin will report that the channel layout property is not writable.

It seems like Logic will only send channel layout information to plugins that report that the channel layout tag is writable, and that advertise some supported layouts.

I think that the following isBusesLayoutSupported implementation will cause busIgnoresLayout to return false, while still allowing discrete channel layouts in hosts such as REAPER. Could you try this in your plugin please?

bool isBusesLayoutSupported (const BusesLayout& layouts) const override
{
    const auto isSetValid = [] (const AudioChannelSet& set)
    {
        return ! set.isDisabled()
            && ! (set.isDiscreteLayout() && set.getChannelIndexForType (AudioChannelSet::discreteChannel0) == -1);
    };

    return isSetValid (layouts.getMainOutputChannelSet())
        && isSetValid (layouts.getMainInputChannelSet())
        && layouts.getMainInputChannelSet() == layouts.getMainOutputChannelSet();
}

Please can you explain what you mean? Pluginval is different to auval. Pluginval is a plugin validation/testing tool developed by Tracktion, whereas auval is an operating system component on Apple devices. The PluginHostType should never be set to ‘pluginval’ in an Apple host. If it is, that’s probably a bug in JUCE.

It seems so. Whenever I check for pluginval, the return value of the if statement is returned, otherwise true. That works in Reaper, Logic and Studio One.

When you return true unconditionally, the busIgnoresLayout check in juce_AU_Wrapper.mm determines that the channel layout and order does not matter to the plugin, and the plugin will report that the channel layout property is not writable.
It seems like Logic will only send channel layout information to plugins that report that the channel layout tag is writable, and that advertise some supported layouts.
I think that the following isBusesLayoutSupported implementation will cause busIgnoresLayout to return false, while still allowing discrete channel layouts in hosts such as REAPER. Could you try this in your plugin please?

Hell, how could I possibly know that the channel layout property is not writable…

I tried your suggestion and it worked in Logic and Reaper so far. In Logic the 6.1 (ES/EX) layout is still recognized as 6.0 which is an error.

How are you testing this? Have you verified with logging, or in a debugger?

Cool, I think this is probably the approach to use, in that case. I’ll look at updating the SurroundPlugin demo to match.

I was able to reproduce this behaviour in other third-party plugins, which leads me to believe that the issue is in Logic, and not in JUCE. If you’re able to provide an example of an non-built-in audiounit that receives LFE input in 6.1 mode then I can investigate this again, but at the moment I don’t think this is possible. Neutron 3 and Pro Q 3 both fail to receive LFE input, as far as I can tell.

How are you testing this? Have you verified with logging, or in a debugger?

I tested it by comparing manually the outcome of loading SurroundTutorial in each of the mentioned hosts, one time with the original isBusesLayoutSupported and the other time with the above mentioned check
if ( PluginHostType().pluginval ) return ((! layouts.getMainInputChannelSet() .isDiscreteLayout()) // [1] && (! layouts.getMainOutputChannelSet().isDiscreteLayout()) && (layouts.getMainInputChannelSet() == layouts.getMainOutputChannelSet()) // [2] && (! layouts.getMainInputChannelSet().isDisabled())); // [3] return true;

I was able to reproduce this behaviour in other third-party plugins, which leads me to believe that the issue is in Logic, and not in JUCE. If you’re able to provide an example of an non-built-in audiounit that receives LFE input in 6.1 mode then I can investigate this again, but at the moment I don’t think this is possible. Neutron 3 and Pro Q 3 both fail to receive LFE input, as far as I can tell.

Got it. Neutron 3 and Pro Q 3 are not Juce plugins, right?

Btw. could you please have a look at Nuendo 11 wrong channel layouts?

This isn’t calling isPluginval(), it’s passing the enum value of pluginval, so it will always evaluate to true.

As far as I’m aware, they’re not.

I’m currently working on this. I think the issue is likely in JUCE.

I am currently experiencing similar issues with 6.1 and 7.1 formats in Logic (also testing with the JUCE Surround example to verify). 6.1 is detected as 6.0 and a Stereo->7.1 instance does not appear (7.1 SDDS works fine).

My previous post regarding AudioChannelSet::getTypeOfChannel not working correctly was due to 2 factors: 1) Seems I need to perform a Full Audio Unit Reset every time I change isBusesLayoutSupported (and not just reset a specific plug-in) 2) As as test - always returning true from isBusesLayoutSupported does cause a Stereo->7.1 instance to appear in the mixer, but the channels are incorrect.

So, currently in my own plug-in I have copied isBusesLayoutSupported from the JUCE surround example and after a Full Audio Unit reset I can query all channel types and buffer indices correctly for all formats except 6.1 and 7.1. I am on a M1 Mac running Logic 10.7.4, MacOS 12.4. JUCE 7.0.2.

Has JUCE concluded this to be a bug with Logic? If so, has this issue been reported to their development team?

Final question: is the appropriate way to query if the plug-in is an AU in isBusesLayoutSupported using hostType.jucePlugInClientCurrentWrapperType == wrapperType_AudioUnit? Since isBusesLayoutSupported is called by some AUVal/Service (whatever Logic does differently here with their intermediary host) and not refreshed each time the plug-in is loaded, it also makes me nervous that hostType would be fully initialized by the time this AU loader queries bus layouts.

Thank You!