Logic X - AU - Channel list incorrect

AU Plugins created with Juce when used in Logic X are coming up with the wrong channels when creating the plugin.

Creating a new synth plugin or using an example such as Sampler act in the same way.

In Auval sampler looks good, supporting mono or stereo:

VERIFYING DEFAULT SCOPE FORMATS:
Input Scope Bus Configuration:
 Default Bus Count:0

Output Scope Bus Configuration:
 Default Bus Count:1
    Bus Name: Output
    Default Format: AudioStreamBasicDescription:  2 ch,  44100 Hz, 'lpcm' (0x00000029) 32-bit little-endian float, deinterleaved
    Has Channel Layouts: 0x640001 0x650002 0x660002 0x670002 0x6A0002 
    Default Layout:
	Tag=0x650002, Num Chan Descs=0


FORMAT TESTS:

Reported Channel Capabilities (explicit):
      [0, 1]  [0, 2]  

No Input, Output Chans:
0-1   0-2   0-4   0-5   0-6   0-7   0-8
X     X                                   

But in Logic X when creating the plugin you get a choice of:

Mono
Stereo
Multi-Output (16xMono)
Multi-Output (25xMono)
Multi-Output (8xStereo, 8xMono)
Multi-Output (16xStereo)
Multi-Output (12xStereo, 13xMono)
Multi-Output (25xStereo)

Choosing anything but Mono or Stereo causes the plugin to not be instantiated and and error : ā€œFailed to load Audio Unit ā€œSamplerPluginā€, please contact the manufacturer for an updated version for further assistance.ā€

This is quite easy to replicate, download the most recent version of Juce, open producer and open the example ā€œSamplePluginDemoā€, set ā€œPlugin is a synthā€ and ā€œPlugin MIDI Inputā€, generate for Xcode and build and test in Logic X.

Is there some workaround for this issue?

Cheers

Andy

Ah so it is a problem with version 10.5.0: Iā€™m not the only one: Multibus plugins and Logic 10.5

Seems to be fixed so far using 10.5.1ā€¦

have you tried it in logic 10.6 ?

Not yet, I do have a machine running 10.6 to try on thoughā€¦

Iā€™m still having a bit of difficulty with multi-out plugins in Logic 10.5, once I sort that I will try on 10.6

Iā€™ll put the following here, Iā€™m sure lots of people know this but I didnā€™t and it might help some:

What was causing me a lot of issues is that Logic is cacheing away the channel config of plugins somewhere, this is not in the normal audio unit cache, you can delete that and logic still remembers the channel layout.

It is imperative to increase the AudioComponent/Version in the plist file for it to change the cached channel layout.

Increasing the plugin version seems to be the only way to force logic to reevaluate the plugin bus layout. Weird thing for sure.

Iā€™m trying to implement a basic multi-out virtual instrument, and even with just 16 stereo buses it shows as 16xStereo, 25xStereo in logic 10.6.1.
Iā€™m stuck at the moment, so any help will be great =)

BTW - the code from this topic

gives me 16xstereo , 25xstereo in logic, in 10.6.0 and 10.6.1 - Iā€™m not sure if something is wrong with the code (it looks totally ok to me though) or it is something wrong with Logic again

PS it works ok on some older logic version.

Yeah, Iā€™m getting the 25 stereo out in 10.5.1 as well with 16 stereo channels.

I need to dig down into the Juce AU wrapper code and look at what is going wrong.

1 Like

OK, I have been looking at the internals a little surrounding hosting plugins.

I have taken an example of NI battery, if you do a getProperty of kAudioUnitProperty_SupportedNumChannels it supports a single channel layout of (0, -32) so no inputs and up to 32 outputs. It also supports adding buses fo we can have multiples of these.

From the Juce world though this plugin has multiple channel layouts: (0,1), (0,2), (0,3), (0,4), (0,5), (0,6), (0,7) and (0,8). So the maximum channels on a single output bus is 8. You can see this by loading the plugin into AudioPluginHost.

You can also see it in Waveform, actually in Waveform you donā€™t seem to be able to add extra buses so the max output count is 8 channels! I could be missing something in waveform though I donā€™t know it too well.

So there is some issue here with juce, I guess something to do with taking layoutTags data at a higher precedence than the kAudioUnitProperty_SupportedNumChannels, Iā€™m looking into what is going on hereā€¦

Good work.

Iā€™m in the same boat with Logic. Iā€™m working on a synth plugin with multiple outputs (all stereo currently) and want to support Logicā€™s way of working where the user can add and remove output busses in the mixer. Iā€™ve written AU only plugins in the past where this works fine.

Everything works as expected in Ableton Live too.

I donā€™t have much time to dig into this at the moment but if I discover anything new Iā€™ll add it here.

Iā€™ve looked into this a bit, and at the moment I think this is an issue with Logic. If I override isBusesLayoutSupported in the MultiOutSynthDemo, then Logic will provide options to instantiate the plugin with 1, 16, or 25 stereo output buses:

bool isBusesLayoutSupported (const BusesLayout& layout) const override
{
    for (const auto& bus : layout.outputBuses)
        if (bus != AudioChannelSet::stereo())
            return false;

    return layout.inputBuses.isEmpty()
        && 1 <= layout.outputBuses.size()
        && layout.outputBuses.size() <= 16;
}

Now, in juce_AU_Wrapper.mm, if I change the implementation of BusCountWritable to always return false, then Logic will only provide options for 1 or 16 stereo output channels. It seems that Logic only shows the option for 25 stereo output buses when BusCountWritable returns true. I donā€™t think thereā€™s a way to hide the option for 25 stereo outputs while keeping the bus count writable. At the moment, Iā€™m inclined to think this is a bug in Logic.

The interesting thing though is that other plugins (non juce) that also allow dynamic buses do not give this 25 stereo bus.

There must be something else going on somewhere.

Are you sure that they allow dynamic buses? Could you try sticking a breakpoint in isBusCountWritable in juce_AudioUnitPluginFormat.mm and checking whether kAudioUnitProperty_ElementCount is a writable property for one of those plugins?

The reason I ask is that the DrumSynth example from iPlug2 has four stereo outs, and Logic displays the add/remove bus buttons for it in the mixer view - however, the ElementCount property is not writable for this plugin.

Yes Iā€™m sure, I debugged it today.

Just instantiate NI Battery, this has dynamic buses Logic doesnā€™t give 25 stereo. To prove it just load it up in AudioPluginHost and create some buses.

The main difference I can see is that Battery is providing a channel map of (0, -32), I think it has something to do with this.

Maybe it there is a single channel map Logic says ā€œok with negative counts thatā€™s the maximum channels supported by all busesā€, it then creates multiple buses in Battery for the channels you choose.

When there is a list of channel maps (0,1), (0,2) etc Logic then thinks that this is per layout/bus so it gives you the max 25 stereo outs that Logic supports. Who knows though!

Also I think the + button in the mixer is not adding buses, it is just adding channels in the mixer.

I looked today with breakpoints and the buses are created at instantiation, not when you click the +

The + will allow you to add mixer channels up to the count of buses created at instantiation.

Thanks, I think youā€™re right. If the channel map includes a negative number smaller than -2, then the abs of that number acts as a maximum number of channels across all buses. Perhaps we can get the AU wrapper to be a bit smarter about picking a maximum number here, rather than picking -1 or -2 (for ā€œany number of channelsā€). Iā€™ll take another look at this on Monday.

Ok, I have had a little look/hack over a coffee this morning, first the good news, I got a plugin limited to 32 channels, unfortunately with a mix of mono and stereo, more on that later. I based this on what NI battery is doing.

screenshot_703

And now the insanity:

To get Logic to act like this we need:

  1. single default stereo output;
    return BusesProperties()
    .withOutput ("Output #1",  juce::AudioChannelSet::stereo(), true);
  1. isBusesLayoutSupported() supporting (0, -32):
  const auto numInputs = layouts.getMainInputChannelSet().size();
  const auto numOutputs = layouts.getMainOutputChannelSet().size();

  return (numInputs == 0) && (numOutputs <= 32);
  1. SupportedNumChannels() in the juce_au_wrapper supporting one channel layout (0, -32)
UInt32 SupportedNumChannels (const AUChannelInfo** outInfo) override
  {
    if (outInfo != nullptr)
    {
      AUChannelInfo *pChannelInfo = new AUChannelInfo();
      pChannelInfo->inChannels = 0;
      pChannelInfo->outChannels = -32;

      *outInfo = pChannelInfo;
    }

    return(1);
}

Now when the plugin is instantiated with one of the multichannel formats in Logic it creates multiple stereo buses:

20:02 08:41:36.187654 <Info > PluginProcessor.cpp              [01263] (5838)(0x102b1e5c0) SUCESS canApplyBusCountChange(OUT, Adding) [Output #1, Stereo, Activated] = 2676169413
0x109deaa00 CACHED_IN = 0, CACHED_OUT = 4
20:02 08:41:40.831633 <Info > PluginProcessor.cpp              [01263] (5838)(0x102b1e5c0) SUCESS canApplyBusCountChange(OUT, Adding) [Output #2, Stereo, Activated] = 2676169413
0x109deaa00 CACHED_IN = 0, CACHED_OUT = 6
20:02 08:41:40.831771 <Info > PluginProcessor.cpp              [01263] (5838)(0x102b1e5c0) SUCESS canApplyBusCountChange(OUT, Adding) [Output #3, Stereo, Activated] = 2676169413
0x109deaa00 CACHED_IN = 0, CACHED_OUT = 8
20:02 08:41:40.831823 <Info > PluginProcessor.cpp              [01263] (5838)(0x102b1e5c0) SUCESS canApplyBusCountChange(OUT, Adding) [Output #4, Stereo, Activated] = 2676169413
0x109deaa00 CACHED_IN = 0, CACHED_OUT = 10
20:02 08:41:40.831865 <Info > PluginProcessor.cpp              [01263] (5838)(0x102b1e5c0) SUCESS canApplyBusCountChange(OUT, Adding) [Output #5, Stereo, Activated] = 2676169413
0x109deaa00 CACHED_IN = 0, CACHED_OUT = 12
20:02 08:41:40.831912 <Info > PluginProcessor.cpp              [01263] (5838)(0x102b1e5c0) SUCESS canApplyBusCountChange(OUT, Adding) [Output #6, Stereo, Activated] = 2676169413
0x109deaa00 CACHED_IN = 0, CACHED_OUT = 14
20:02 08:41:40.831951 <Info > PluginProcessor.cpp              [01263] (5838)(0x102b1e5c0) SUCESS canApplyBusCountChange(OUT, Adding) [Output #7, Stereo, Activated] = 2676169413
0x109deaa00 CACHED_IN = 0, CACHED_OUT = 16
20:02 08:41:40.832220 <Info > PluginProcessor.cpp              [01263] (5838)(0x102b1e5c0) SUCESS canApplyBusCountChange(OUT, Adding) [Output #8, Stereo, Activated] = 2676169413
0x109deaa00 CACHED_IN = 0, CACHED_OUT = 18
20:02 08:41:40.832273 <Info > PluginProcessor.cpp              [01263] (5838)(0x102b1e5c0) SUCESS canApplyBusCountChange(OUT, Adding) [Output #9, Stereo, Activated] = 2676169413
0x109deaa00 CACHED_IN = 0, CACHED_OUT = 20
20:02 08:41:40.832319 <Info > PluginProcessor.cpp              [01263] (5838)(0x102b1e5c0) SUCESS canApplyBusCountChange(OUT, Adding) [Output #10, Stereo, Activated] = 2676169413
0x109deaa00 CACHED_IN = 0, CACHED_OUT = 22
20:02 08:41:40.832359 <Info > PluginProcessor.cpp              [01263] (5838)(0x102b1e5c0) SUCESS canApplyBusCountChange(OUT, Adding) [Output #11, Stereo, Activated] = 2676169413
0x109deaa00 CACHED_IN = 0, CACHED_OUT = 24
20:02 08:41:40.832403 <Info > PluginProcessor.cpp              [01263] (5838)(0x102b1e5c0) SUCESS canApplyBusCountChange(OUT, Adding) [Output #12, Stereo, Activated] = 2676169413
0x109deaa00 CACHED_IN = 0, CACHED_OUT = 26
20:02 08:41:40.832489 <Info > PluginProcessor.cpp              [01263] (5838)(0x102b1e5c0) SUCESS canApplyBusCountChange(OUT, Adding) [Output #13, Stereo, Activated] = 2676169413
0x109deaa00 CACHED_IN = 0, CACHED_OUT = 28
20:02 08:41:40.832537 <Info > PluginProcessor.cpp              [01263] (5838)(0x102b1e5c0) SUCESS canApplyBusCountChange(OUT, Adding) [Output #14, Stereo, Activated] = 2676169413
0x109deaa00 CACHED_IN = 0, CACHED_OUT = 30
20:02 08:41:40.832584 <Info > PluginProcessor.cpp              [01263] (5838)(0x102b1e5c0) SUCESS canApplyBusCountChange(OUT, Adding) [Output #15, Stereo, Activated] = 2676169413
0x109deaa00 CACHED_IN = 0, CACHED_OUT = 32

So from this using a single (0,-32) channel map, Logic definitely seems to be using isBusesLayoutSupported() to determine the total number of channels across all buses, it then creates multiple stereo buses to create the required number of channels. If you set isBusesLayoutSupported to only support 2 channels auval throws a wobbly.

A side effect of all this is that mono channels are also used (-1).

I will have a play with what happens with lists of channel maps in a bitā€¦

Ok:

  1. With [0,-1]
    Mono
    Stereo
    Multi Output (16xMono)
    Multi Output (25xMono)
    Multi Output (8xStereo, 8xMono)
    Multi Output (16xStereo)
    Multi Output (12xStereo, 13xMono)
    Multi Output (25xStereo)

  2. With [0, 2]
    Stereo
    Multi Output (16xStereo)
    Multi Output (25xStereo)

  3. With [0,1] (and juce::AudioChannelSet::mono())
    Stereo
    Multi Output (16xMono)
    Multi Output (25xMono)

  4. With [0, 1] [0, 2]
    Mono
    Stereo
    Multi Output (16xMono)
    Multi Output (25xMono)
    Multi Output (8xStereo, 8xMono)
    Multi Output (16xStereo)
    Multi Output (12xStereo, 13xMono)
    Multi Output (25xStereo)

  5. With [0, 1] [0, 2] [0, 3] [0, 4] [0, 5] [0, 6] [0, 7] [0, 8]
    Mono
    Stereo
    Multi Output (16xMono)
    Multi Output (25xMono)
    Multi Output (8xStereo, 8xMono)
    Multi Output (16xStereo)
    Multi Output (12xStereo, 13xMono)
    Multi Output (25xStereo)

  6. with [0, 2] [0, 6]
    Stereo
    Multi Output (16xStereo)
    Multi Output (25xStereo)

All makes total sense!!

1 Like

At the moment Iā€™m not sure thereā€™s a perfect solution. If we allow the kAudioUnitProperty_ElementCount to be writable, it seems that the host is allowed to attempt to instantiate the plugin with any number of buses. I havenā€™t been able to find a way of specifying a maximum number of buses in the docs or AU headers. Itā€™s not until the host actually tries setting the ElementCount property that it finds out whether or not the plugin really supports the requested number of buses.

However, if we go with the alternative approach of setting a large negative number as the ā€˜max channel count across all busesā€™ (like Battery does) then we lose the ability to specify valid channel layouts on each bus. I think this is why Logic gives us options to instantiate Battery in 5.1 or 8xStereo,8xMono configurations, even though these setups donā€™t necessarily make sense. Even if I pick the 5.1 configuration, Batteryā€™s internal routing menu still allows routing to 16 stereo outputs.

I think, in the multi output case, the best solution may be to gracefully accept any number of buses (even if you donā€™t actually send audio to buses past a certain limit). In the AudioProcessor, you can override numBusesChanged to receive notifications when buses are added or removed in order to allocate any necessary resources. You can also query getBusCount in your processBlock, and simply avoid processing on unexpected buses. This approach will at least allow restricting the layout of each bus to sane layouts (e.g. stereo, mono, 5.1 etc. only).

1 Like

The behaviour of Logic with respect to multichannel has changed a few times in recent releases. 10.5.0 broke multichannel for my plugin (which has up to five outputs). It was fixed in 10.5.1. It has been broken again by 10.6.0 and 10.6.1, but Iā€™ve been told by logic support that they will fix it, not sure when.

2 Likes

The valid channel layouts are defined by kAudioUnitProperty_SupportedChannelLayoutTags though arenā€™t they?

So for battery in auval when with a channel map of [0, -32] we still see:

The Unit publishes the following Channel Layouts:
  0x640001, 0x650002, 0x710003, 0x6C0004, 0x750005, 0x790006, 0x7D0007, 0x7E0008, 

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                                                                       X       X               X       

Granted Logic is ignoring this :slight_smile: