Feature Request: High Order Ambisonics Support


Hi JUCE-team,

our customers ask us to implement recently introduced Ambisonics Support in Pro Tools in our Plugin. Could you integrate this with the AAX wrapper. The names of the added formats are:

AAX_eStemFormat_Ambi_1_ACN (4 Channels, Ambisonics First Order)
AAX_eStemFormat_Ambi_2_ACN (9 Channels, Ambisonics Second Order)
AAX_eStemFormat_Ambi_3_ACN (16 Channels, Ambisonics Third Order)

Thanks Johannes

PS: As soon, as this is integrated into Master, we will upgrade to JUCE 5

EDIT: since this is now also part of the newest VST3 SDK changed the title.


Any chance that you will look into this? Should be a quick fix…


I could use this as well.


I’m happy to implement this but I’m not really a surround format export. So, if you can tell me exactly which channels each layout is composed of and which order ProTools expects the channels to be in then I’ll add it to JUCE. If possible, use the channel names found here in your response.

For extra points :yum:, let me know which CoreAudio layout tags and VST2 layouts corresponds to the three ambisonic AAX layouts.


Hi Fabian, thanks!

I would suggest to skip ambisonicW, ambisonicX, ambisonicY, ambisonicZ and replace them with ambisonic0, ambisonic1, .... ambisonic15 Here is why:

The problem is, Ambisonics itself doesn’t imply a specific channel order. There are different conventions. The most commonly used ones are ambiX and FuMa. FuMa channel order is W,X,Y,Z whereas ambiX is W,Y,Z,Y. (for 1st order as an example) It’s not only the channel order that’s specific to the convention, but also the level/normalization of each channel. This is why no DAW that I’m aware of will convert between these conventions automatically and therefore doesn’t need to know the order of the channels. That’s probably also the reason, why CoreAudio doesn’t specify them in the docs. The user of the plugins has to make sure he manually sets all of her/his ambisonics plugins to the correct convention or use a converter plugin like this:

Almost all Ambisonics have a little switch to chose between ambiX and FuMa (ours does, too). Now Pro Tools does specify the order as ACN(ambiX) in their docs, but I’m pretty sure, many plugin manufacturers will still have the option to switch between FuMa and ambiX.

Long story short: No need to have lettered names for each channel, ambisonics0 to ambisonics 15 should be fine. Ambisonics is a generic multi-channel-bus and the user has to take care of the order for each plugin and after exporting.

AAX_eStemFormat_Ambi_1_ACN (Ambisonics0 to Ambisonics 3)
AAX_eStemFormat_Ambi_2_ACN (Ambisonics0 to Ambisonics 8)
AAX_eStemFormat_Ambi_3_ACN (Ambisonics0 to Ambisonics 15)



CoreAudio has 254th order ambisonics definitions, as of the latest SDK at least:
kAudioChannelLabel_HOA_ACN_0 until 65024 (though over 15 the enum value is to be computed, there is no label -> see the code in the CoreAudio headers)

  • First order ambisonics uses 0 until 3
  • Second order ambisonics uses 0 until 3+5 = 8
  • Third order ambisonics uses 0 until 8+7 = 15
  • Fourth order ambisonics uses 0 until 15+9 = 24
  • Fifth order ambisonics uses 0 until 24+11 = 35

Probably 5th order is enough for these definitions.


Actually VST 3.6.8. now implemented the same features:


kAmbi1stOrderACN, kAmbi2cdOrderACN, kAmbi3rdOrderACN

This should confirm to skip the W, X, Y, Z channels and replace with ambisonics0 to ambisonics15 or ambisonicsACN0... ambisonicsACN15

No flock.cpp in VST SDK?

Do you think that these should be replaced with ambisonic0, ambisonic1, ambisonic2, ambisonic3? We then additionally add ambisonic4 to ambisonic15? CoreAudio specifically mentions the WXYZ channels in their docs.


I agree go Ambix (ACN, SN3D) ! not so familiar with the JUCE code for this but might be possible to keep the FuMa WXYZ as well. ambisonicsACN0…35 sounds good to me


Hm seems like they have both in their docs:



I wasn’t aware of that, so in that case it might make sense to keep the old ambisonic channels and just add new ones with the name ambisonicsACN0 ... xxx


Yeah, I think that’s what I’ll do. It will need to wait after ADC though.


Sure thing, I see you there! Looking forwad :slight_smile:


any chance of this being looked at? Thanks!


Looking at this right now. I have the following idea. Please let me know if this makes sense:

As only CoreAudio seems to explicitly mention the FuMa channels, I will replace JUCE’s ambisonic W, X, Y, Z with the 1st order ACN channels and then add all the other higher order ACN channels to JUCE. So:

ambisonicACN0          = 24,  // <- used to be ambisonicW
ambisonicACN1          = 25,  // <- used to be ambisonicX 
ambisonicACN2          = 26,  // <- used to be ambisonicY
ambisonicACN3          = 27   // <- used to be ambisonicZ

For backward compatibility, I will add defines for the FuMa channel labels ambisonicW, … as follows

ambisonicW = ambisonicACN0,
ambisonicX = ambisonicACN3,
ambisonicY = ambisonicACN1,
ambisonicZ = ambisonicACN2,

As you see I’m ordering so that the channels match with ACN (according to this wiki article). This is actually a breaking change, as ambisonicXamisonicZ will now have a different channel order. This ensures that the channels in any ambisonic JUCE audio buffer (FuMa or ACN) will always be in ACN order (if you access the channel buffer with a raw int like buffer[2] etc). The backends will ensure the channel re-ordering from FuMa 1st order to ACN 1st order. JUCE won’t support any higher order FuMa formats.

This is a slight breaking change if anybody relied on, the ordering of the FuMa channels, like this code, for example:

AudioChannelSet ambisonic = AudioChannelSet::ambisonic();
const int channelIdxOfWChannel = ambisonic.getChannelIndexForType (ambisonicW);

float* w = audioBuffer.getReadPointer (channelIdxOfWChannel + 0);
float* x = audioBuffer.getReadPointer (channelIdxOfWChannel + 1);
float* y = audioBuffer.getReadPointer (channelIdxOfWChannel + 2);
float* z = audioBuffer.getReadPointer (channelIdxOfWChannel + 3);

The following code would not break and have the benefit that the channel pointers would be correct regardless if the audio buffer is ACN or FuMa (as internally everything will be ordered as ACN):

AudioChannelSet ambisonic = AudioChannelSet::ambisonic();

float* w = audioBuffer.getReadPointer (ambisonic.getChannelIndexForType (ambisonicW));
float* x = audioBuffer.getReadPointer (ambisonic.getChannelIndexForType (ambisonicX));
float* y = audioBuffer.getReadPointer (ambisonic.getChannelIndexForType (ambisonicY));
float* z = audioBuffer.getReadPointer (ambisonic.getChannelIndexForType (ambisonicZ));

Do people think this approach is sensible?


Makes sense to me! Using ACN also for first order ambisonics (FOA) leads to a consistent ordering also for higher order ambisonics (HOA). SID ordering (W, X, Y, Z) is not really used very often.


Hi Fabian,

excellent! Very nice solution in my view.



OK. Ambisonics support is now on develop with commit 30269ba.


Nice! :smiley:


thanks fabian!
don’t want to be greedy but what about even higher order Ambisonics? as Reaper (and other DAWs) support up to 64 channels, 7th order Ambisonics would be possible.


Higher than 3rd order is neither defined in VST nor AAX… You could just create a plugin with more discrete channels and it should work fine in Reaper.