ATMOS channel layouts: How To?

Hi There,

I wonder how JUCE wants us to implement different channel layouts, especially with higher channel count. I’ve learned that AudioChannelSet stores all information of interest (used channel types, names,…) but ignores the order completely (channel enums are stored in a BigInteger bitmask).
I.e. e.g. AudioChannelSet::create9point1point4() is sorting the channels. Why is the channel order not stored anywhere?

Next fun fact is that Logic Pro returns a discrete layout so that the plugin can’t see any channel types used on the bus, just discrete numbers.

Coming to my question: How to get through this jungle? Ditch Juce::AudioChannelSet or extend it or is there something that I miss?

  • I want to support common speaker layouts 7.1.4, 9.1.4,.., which may have different channel orders (SMPTE, Film,..)
  • Auto detecting the bus layout of the current DAW would be cool
  • fallback is either having a lot of “presets” or a free routing matrix (like e.g. Virtuoso)

I’m sure e.g. @fiedleraudio went through this hell as well : )

Thanks!

Logic supports Atmos channel layouts, but it can be a prick about it. IIRC it may prefer to create discrete layouts if you let it. So if you forbid discrete layouts, it will use the named ones instead. It’s been a while, but I vaguely remember something along those lines.

I have this in my isBusesLayoutSupported():

    if(layouts.getMainInputChannelSet().isDiscreteLayout() && layouts.getMainInputChannelSet().getChannelIndexForType(AudioChannelSet::discreteChannel0) == -1)
    {
        return false;
    }

Don’t ask me why theres also that second check. I might have actually picked that up here on the forum.

The most important footgun to deal with however is that Logic is extremely sticky with caching plugin’s channel capabilities. So when you’re messing around with those, be sure to delete the cache before trying a new build (the other option is to increment the version number each build).

Here’s the script I use for that:

#!/bin/sh
sudo killall -9 AudioComponentRegistrar
rm ~/Library/Caches/AudioUnitCache/com.apple.audiounits.cache
rm ~/Library/Caches/com.apple.audiounits.cache
rm ~/Library/Preferences/com.apple.audio.InfoHelper.plist
rm ~/Library/Preferences/com.apple.audio.AudioComponentCache.plist

Not that not all of the files may always exist. Depends a bit on OS and Logic versions I think.

Ok, thanks. This already helps a lot. I will need to distinguish between hosts then to e.g. allow discrete layouts for Reaper (which only supports those afaik)

Yea, I also learned the sticky caching the hard way : )

You can always query the channel layout of the input and output buses. If juce recognizes the channel layout communicated to the plugin by the DAW the information will be available. You can always check each channel type with something like getTypeOfChannel(int channel) and thereby get juce’s channel order. That order is equal for a given format on all DAWs and plugin formats so be aware of the fact that it does not match what you see in the DAW. Some DAWs even allow rearranging the channel order in the mixer which does not affect the channel order of the plugin.

This, and I’d like to add that there is no way around doing some custom reordering/mapping if you want to have multichannel level meters that somehow look reasonable.

Yes, thanks! I now learned that the channels are somehow (host dependent or format dependent?) reordered in the wrapper code to match the juce layouts. The multichannel meters are clearly wrong then. I therefore added some manual mapping code per layout. Basically a std::vector<ChannelType> displayOrder;
using the AudioChannelSet::createXpointY() function implementation.
I still have to check a lot of DAWs to see if it works this way.

FML.
I hopped on here to see if anyone happened to make a list of the channel orders for every configuration offered in each of the multichannel DAWs because loading my plugin into every DAW in every available configuration to note the channel order manually is taking waaaaaaaay too long.
I’m using the meters on the Dolby RMU to send a signal through each channel in my plugin and see & hear which channel it arrives at in the 9.1.6 Dolby Atmos system.

Nuendo, Cubase Pro, and WaveLab Pro follow Dolby/SMPTE standard perfectly. So, there are no issues whatsoever. Reaper is a bitch because it only applies numerical values, but I resolved that by laying out what I want each channel to be, based on Dolby/SMPTE standard.

Then, I got to ProTools Ultimate and now I have another thing to add to the long list of why ProTools sucks. If you add a bus with more than 2 top channels or higher than 7.1.2, you can add it as an Atmos object and the mapping is only partly incorrect some of the time, but decent enough. Unfortunately, 7.1.2 (an essential bus for Atmos) does NOT follow the SMPTE standard that Dolby uses. It uses the same nonsensical order all of their surround busses use. Even that is giving them too much credit because the named order randomly changes for no logical reason from channel-width to channel-width. Officially, Avid says PT follows “Film” layout, which explains why it orders them all with LCR instead of LRC, but the rest is completely random. What kind of “professional” DAW doesn’t allow you to mix surround busses in Atmos because the layouts are incompatible??

I already suspected Logic was gonna be another annoying DAW that required its own special layout for the AU version, but from what I just read, it sounds like Logic is gonna be just as annoying as ProTools.
I’m really hoping that when I begin mapping Sequoia, FenderStudio (fka StudionOne), and Pyramix Premium’s layouts, after I send this reply, that they follow SMPTE in every configuration, like Nuendo/Cubase so I can finish mapping and wrap this up because this is the only thing standing in the way of me finally adding my licensing system to it and preparing for release.

If anyone had the channel mapping for all the multichannel DAWs typed up somewhere, I’d really appreciate it.

This is what I have so far:

Nuendo/Cubase/WaveLab [Dolby SMPTE Standard]
Mono : [L]
Stereo: [L, R]
LRC : [L, R, C]
Quadro: [L, R, LS, RS]
5.0 : [L, R, C, LS, RS]
5.1 : [L, R, C, LFE, LS, RS]
7.0 : [L, R, C, LS, RS, LRS, RRS]
5.0.2 : [L, R, C, LS, RS, LTM, RTM]
7.1 : [L, R, C, LFE, LS, RS, LRS, RRS]
5.1.2 : [L, R, C, LFE, LS ,RS, LTM, RTM]
5.0.4 : [L, R, C, LS, RS, LTF, RTF, LTR, RTR]
7.0.2 : [L, R, C, LS, RS, LRS, RRS, LTM, RTM]
7.1.2 : [L, R, C, LFE, LS, RS, LRS, RRS, LTM, RTM]
5.1.4 : [L, R, C, LFE, LS, RS, LTF, RTF, LTR, RTR]
7.0.4 : [L, R, C, LS, RS, LTF, RTF, LTR, RTR, LRS, RRS]
7.1.4 : [L, R, C, LFE, LS, RS, LTF, RTF, LTR, RTR, LRS, RRS]
9.0.4 : [L, R, C, LS, RS, LTF, RTF, LTR, RTR, LRS, RRS, LW, RW]
7.0.6 : [L, R, C, LS, RS, LTF, RTF, LTR, RTR, LRS, RRS, LTM, RTM]
9.1.4 : [L, R, C, LFE, LS, RS, LTF, RTF, LTR, RTR, LRS, RRS, LW, RW]
7.1.6 : [L, R, C, LFE, LS, RS, LTF, RTF, LTR, RTR, LRS, RRS, LTM, RTM]
9.1.6 : [L, R, C, LFE, LS, RS, LTF, RTF, LTR, RTR, LRS, RRS, LW, RW, LTM, RTM]

ProTools [Film]
Mono :
Stereo: [L, R,]
LCR : [L, C, R]
Quadro: [L, C, RS, LRS] (Quadro Aux in)
5.0 : [L, C, R, RS, LRS] (5.0 Aux in)
5.1 : [L, C, R, RRS, RS, LRS] (5.1 Aux in)
7.0 : [L, C, R, LFE, LS , RS, LRS] (7.0 Aux In)
7.1 : [L, C, R, RRS, LFE, LS, RS, LRS] (7.1 Aux In)
5.0.4 : [L, R, C, LRS, RRS, LTF, RTF, LTR, RTR] (5.0.4 Object)
5.1.4 : [L, R, C, LFE, LRS, RRS, LTF, RTF, LTR, RTR] (5.1.4 Object)
7.1.2 : [L, C, R, RRS, LFE, LS, RS, LRS, LTM, RTM] (7.1.2 Aux In)
7.0.4 : [L, R, C, LS, RS, LTF, RTF, LTR, RTR, LRS, RRS] (7.0.4 Object)
7.1.4 : [L, R, C, LFE, LS, RS, LTF, RTF, LTR, RTR, LRS, RRS] (7.1.4 Object)
7.0.6 : [L, R, C, LS, RS, LTF, RTF, LTR, RTR, LRS, RRS, LTM, RTM] (7.0.6 Object)
7.1.6 : [L, R, C, LFE, LS, RS, LTF, RTF, LTR, RTR, LRS, RRS, LTM, RTM] (7.1.6 Object)
9.0.6 : [L, R, C, LS, RS, LTF, RTF, LTR, RTR, LRS, RRS, LW, RW, LTM, RTM] (9.0.6 Object)
9.1.6 : [L, R, C, LFE, LS, RS, LTF, RTF, LTR, RTR, LRS, RRS, LW, RW, LTM, RTM] (9.1.6 Object)

Reaper [Microsoft WAVEXT]

01ch.:
02ch.: [L, R]
04ch.: [L, R, C, LFE]
06ch.: [L, R, C, LFE, LS, RS]
08ch.: [L, R, C, LFE, LRS, RRS, LS, RS
10ch.: [L, R, C, LFE, LS, RS, LRS, RRS, LTM, RTM]
12ch.: [L, R, C, LFE, LS, RS, LRS, RRS, LTM, RTM, LW, RW]
14ch.: [L, R, C, LFE, LS, RS, LRS, RRS, LTM, RTM, LW, RW, LTF, RTF]
16ch.: [L, R, C, LFE, LS, RS, LRS, RRS, LTM, RTM, LW, RW, LTF, RTF, LTR, RTR]

I’m pretty confident, after a week of mapping the same 3 DAWs, that this is correct now. However, if my method caused me to get any of that wrong and someone knows for absolute certainty that its wrong. I would very much love to learn of someone’s correct mapping, if its been proven through successful implementation.
If anyone has the order for every configuration in Logic Pro, it would save me a lot of trouble.

I think Sequoia uses the same Dolby/SMPTE standard as Steinberg DAWs, I’ll be able to confirm Pyramix Premium a lot sooner if somebody can help get Logic Pro, Sequoia, Audition, Abelton, Resolve Studio, and/or FenderStudio/StudioOne Pro out of the way for me.

As far as I understood is JUCE trying its best to remap the channels coming from the DAW into the “JUCE Channel Layout”. But JUCE maps to the channel type (e.g. LFE) not the channel index. I.e. the bus must somehow dispose a proper named layout.

For AAX have a look at e.g. juce_audio_plugin_client_AAX.cpp and search for aaxChannelOrder.

Every layout is documented there and the table strictly uses LCR for all layouts. So it looks like Aux In is not remapped correctly and Input Objects are remapped correctly to standard JUCE (because they have a proper layout w. named channel types?)

For AU you’ll find a ChannelRemapper class and fillLayoutChannelMaps() in juce_AU_shared.h. (Btw. it looks like Logic is not supporting higher than 7.1.4 for now).

For VST3 have a look at getVst3SpeakerArrangement() and the ClientBufferMapper class in juce_VST3_common.h.

Maybe a DBG() here and there helps to find layouts which are not mapped correctly.

Just for completeness:
Having said that, the next step is to not actually process e.g. channel 3 directly for LFE processing but call yourLayout.getChannelIndexForType (AudioChannelSet::LFE) to get the correct channel index.
juce::AudioChannelSets do not hold the speaker order, only the existence of a speaker (bit mask)!

At least this is how I understood it and it may certainly be wrong for some DAWs or configurations.

Check my post in Trouble finding index corresponding to Nth channel of AAX output in processBlock() - #2 by railjonrogut

Rail

1 Like