diff --git a/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp b/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp index bffc3f4bfc..4ca398ca5d 100644 --- a/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp +++ b/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp @@ -23,7 +23,12 @@ namespace juce { -AudioChannelSet::AudioChannelSet (uint32 c) : channels (c) {} + + +AudioChannelSet::AudioChannelSet (uint32 c) : channels (static_cast (c)) +{ +} + AudioChannelSet::AudioChannelSet (const Array& c) { for (auto channel : c) @@ -106,15 +111,18 @@ String AudioChannelSet::getAbbreviatedChannelTypeName (AudioChannelSet::ChannelT case LFE2: return "Lfe2"; case leftSurroundSide: return "Lss"; case rightSurroundSide: return "Rss"; - case ambisonicW: return "W"; - case ambisonicX: return "X"; - case ambisonicY: return "Y"; - case ambisonicZ: return "Z"; + case ambisonicACN0: return "ACN0"; + case ambisonicACN1: return "ACN1"; + case ambisonicACN2: return "ACN2"; + case ambisonicACN3: return "ACN3"; case topSideLeft: return "Tsl"; case topSideRight: return "Tsr"; default: break; } + if (type >= ambisonicACN4 && type <= ambisonicACN35) + return "ACN" + String (type - ambisonicACN4 + 4); + return {}; } @@ -215,7 +223,26 @@ String AudioChannelSet::getDescription() const if (*this == pentagonal()) return "Pentagonal"; if (*this == hexagonal()) return "Hexagonal"; if (*this == octagonal()) return "Octagonal"; - if (*this == ambisonic()) return "Ambisonic"; + + // ambisonics + { + auto order = getAmbisonicOrder(); + + if (order >= 0) + { + String suffix; + + switch (order) + { + case 1: suffix = "st"; break; + case 2: suffix = "nd"; break; + case 3: suffix = "rd"; break; + default: suffix = "th"; break; + } + + return String (order) + suffix + " Order Ambisonics"; + } + } return "Unknown"; } @@ -223,7 +250,7 @@ String AudioChannelSet::getDescription() const bool AudioChannelSet::isDiscreteLayout() const noexcept { for (auto& speaker : getChannelTypes()) - if (speaker <= topSideRight) + if (speaker <= ambisonicACN35) return false; return true; @@ -299,7 +326,6 @@ AudioChannelSet AudioChannelSet::create7point0() { return AudioChannelSet AudioChannelSet AudioChannelSet::create7point0SDDS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftCentre) | (1u << rightCentre)); } AudioChannelSet AudioChannelSet::create7point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } AudioChannelSet AudioChannelSet::create7point1SDDS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftCentre) | (1u << rightCentre)); } -AudioChannelSet AudioChannelSet::ambisonic() { return AudioChannelSet ((1u << ambisonicW) | (1u << ambisonicX) | (1u << ambisonicY) | (1u << ambisonicZ)); } AudioChannelSet AudioChannelSet::quadraphonic() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << leftSurround) | (1u << rightSurround)); } AudioChannelSet AudioChannelSet::pentagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } AudioChannelSet AudioChannelSet::hexagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << centreSurround) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } @@ -307,6 +333,30 @@ AudioChannelSet AudioChannelSet::octagonal() { return AudioChannelSet AudioChannelSet AudioChannelSet::create7point0point2() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << topSideLeft) | (1u << topSideRight)); } AudioChannelSet AudioChannelSet::create7point1point2() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << topSideLeft) | (1u << topSideRight)); } +AudioChannelSet AudioChannelSet::ambisonic (int order) +{ + jassert (isPositiveAndBelow (order, 6)); + + if (order == 0) + return AudioChannelSet ((uint32) (1 << ambisonicACN0)); + + AudioChannelSet set ((1u << ambisonicACN0) | (1u << ambisonicACN1) | (1u << ambisonicACN2) | (1u << ambisonicACN3)); + + auto numAmbisonicChannels = (order + 1) * (order + 1); + set.channels.setRange (ambisonicACN4, numAmbisonicChannels - 4, true); + + return set; +} + +int AudioChannelSet::getAmbisonicOrder() const +{ + auto ambisonicOrder = getAmbisonicOrderForNumChannels (size()); + + if (ambisonicOrder >= 0) + return (*this == ambisonic (ambisonicOrder) ? ambisonicOrder : -1); + + return -1; +} AudioChannelSet AudioChannelSet::discreteChannels (int numChannels) { @@ -368,7 +418,6 @@ Array AudioChannelSet::channelSetsWithNumberOfChannels (int num { retval.add (AudioChannelSet::quadraphonic()); retval.add (AudioChannelSet::createLCRS()); - retval.add (AudioChannelSet::ambisonic()); } else if (numChannels == 5) { @@ -395,6 +444,10 @@ Array AudioChannelSet::channelSetsWithNumberOfChannels (int num retval.add (AudioChannelSet::create7point1SDDS()); retval.add (AudioChannelSet::octagonal()); } + + auto order = getAmbisonicOrderForNumChannels (numChannels); + if (order >= 0) + retval.add (AudioChannelSet::ambisonic (order)); } return retval; @@ -428,4 +481,106 @@ int32 AudioChannelSet::getWaveChannelMask() const noexcept return (channels.toInteger() >> 1); } +//============================================================================== +int JUCE_CALLTYPE AudioChannelSet::getAmbisonicOrderForNumChannels (int numChannels) +{ + auto sqrtMinusOne = std::sqrt (static_cast (numChannels)) - 1.0f; + auto ambisonicOrder = jmax (0, static_cast (std::floor (sqrtMinusOne))); + + if (ambisonicOrder > 5) + return -1; + + return (static_cast (ambisonicOrder) == sqrtMinusOne ? ambisonicOrder : -1); +} + +//============================================================================== +#if JUCE_UNIT_TESTS +class AudioChannelSetUnitTest : public UnitTest +{ +public: + AudioChannelSetUnitTest() : UnitTest ("AudioChannelSetUnitTest", "Audio") {} + + void runTest() override + { + auto max = AudioChannelSet::maxChannelsOfNamedLayout; + + beginTest ("maxChannelsOfNamedLayout is non-discrete"); + expect (AudioChannelSet::channelSetsWithNumberOfChannels (max).size() >= 2); + + beginTest ("channelSetsWithNumberOfChannels returns correct speaker count"); + { + for (auto ch = 1; ch <= max; ++ch) + { + auto channelSets = AudioChannelSet::channelSetsWithNumberOfChannels (ch); + + for (auto set : channelSets) + expect (set.size() == ch); + } + } + + beginTest ("Ambisonics"); + { + uint64 mask = 0; + + mask |= (1ull << AudioChannelSet::ambisonicACN0); + checkAmbisonic (mask, 0, "0th Order Ambisonics"); + + mask |= (1ull << AudioChannelSet::ambisonicACN1) | (1ull << AudioChannelSet::ambisonicACN2) | (1ull << AudioChannelSet::ambisonicACN3); + checkAmbisonic (mask, 1, "1st Order Ambisonics"); + + mask |= (1ull << AudioChannelSet::ambisonicACN4) | (1ull << AudioChannelSet::ambisonicACN5) | (1ull << AudioChannelSet::ambisonicACN6) + | (1ull << AudioChannelSet::ambisonicACN7) | (1ull << AudioChannelSet::ambisonicACN8); + checkAmbisonic (mask, 2, "2nd Order Ambisonics"); + + mask |= (1ull << AudioChannelSet::ambisonicACN9) | (1ull << AudioChannelSet::ambisonicACN10) | (1ull << AudioChannelSet::ambisonicACN11) + | (1ull << AudioChannelSet::ambisonicACN12) | (1ull << AudioChannelSet::ambisonicACN13) | (1ull << AudioChannelSet::ambisonicACN14) + | (1ull << AudioChannelSet::ambisonicACN15); + checkAmbisonic (mask, 3, "3rd Order Ambisonics"); + + mask |= (1ull << AudioChannelSet::ambisonicACN16) | (1ull << AudioChannelSet::ambisonicACN17) | (1ull << AudioChannelSet::ambisonicACN18) + | (1ull << AudioChannelSet::ambisonicACN19) | (1ull << AudioChannelSet::ambisonicACN20) | (1ull << AudioChannelSet::ambisonicACN21) + | (1ull << AudioChannelSet::ambisonicACN22) | (1ull << AudioChannelSet::ambisonicACN23) | (1ull << AudioChannelSet::ambisonicACN24); + checkAmbisonic (mask, 4, "4th Order Ambisonics"); + + mask |= (1ull << AudioChannelSet::ambisonicACN25) | (1ull << AudioChannelSet::ambisonicACN26) | (1ull << AudioChannelSet::ambisonicACN27) + | (1ull << AudioChannelSet::ambisonicACN28) | (1ull << AudioChannelSet::ambisonicACN29) | (1ull << AudioChannelSet::ambisonicACN30) + | (1ull << AudioChannelSet::ambisonicACN31) | (1ull << AudioChannelSet::ambisonicACN32) | (1ull << AudioChannelSet::ambisonicACN33) + | (1ull << AudioChannelSet::ambisonicACN34) | (1ull << AudioChannelSet::ambisonicACN35); + checkAmbisonic (mask, 5, "5th Order Ambisonics"); + } + } + +private: + void checkAmbisonic (uint64 mask, int order, const char* layoutName) + { + auto expected = AudioChannelSet::ambisonic (order); + auto numChannels = expected.size(); + + expect (numChannels == BigInteger ((int64) mask).countNumberOfSetBits()); + expect (channelSetFromMask (mask) == expected); + + expect (order == expected.getAmbisonicOrder()); + expect (expected.getDescription() == layoutName); + + auto layouts = AudioChannelSet::channelSetsWithNumberOfChannels (numChannels); + expect (layouts.contains (expected)); + + for (auto layout : layouts) + expect (layout.getAmbisonicOrder() == (layout == expected ? order : -1)); + } + + static AudioChannelSet channelSetFromMask (uint64 mask) + { + Array channels; + for (int bit = 0; bit <= 62; ++bit) + if ((mask & (1ull << bit)) != 0) + channels.add (static_cast (bit)); + + return AudioChannelSet::channelSetWithChannels (channels); + } +}; + +static AudioChannelSetUnitTest audioChannelSetUnitTest; +#endif + } // namespace juce diff --git a/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h b/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h index 431ae389df..c8348a1ce4 100644 --- a/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h +++ b/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h @@ -208,13 +208,6 @@ public: //============================================================================== - /** Creates a set for ambisonic surround setups (ambisonicW, ambisonicX, ambisonicY, ambisonicZ). - - Is equivalent to: kBFormat (VST), n/a (AAX), kAudioChannelLayoutTag_Ambisonic_B_Format (CoreAudio) - */ - static AudioChannelSet JUCE_CALLTYPE ambisonic(); - - /** Creates a set for quadraphonic surround setup (left, right, leftSurround, rightSurround) Is equivalent to: k40Music (VST), AAX_eStemFormat_Quad (AAX), kAudioChannelLayoutTag_Quadraphonic (CoreAudio) @@ -246,6 +239,18 @@ public: */ static AudioChannelSet JUCE_CALLTYPE octagonal(); + //============================================================================== + /** Creates a set for ambisonic surround setups (ambisonicW, ambisonicX, ambisonicY, ambisonicZ). + + Is equivalent to: kAmbiXXXOrderACN (VST), AAX_eStemFormat_Ambi_XXX_ACN (AAX), kAudioChannelLayoutTag_HOA_ACN_SN3D (CoreAudio) + */ + static AudioChannelSet JUCE_CALLTYPE ambisonic (int order = 1); + + /** Returns the order of the ambisonic layout represented by this AudioChannelSet. If the + AudioChannelSet is not an ambisonic layout, then this method will return -1. + */ + int getAmbisonicOrder() const; + //============================================================================== /** Creates a set of untyped discrete channels. */ static AudioChannelSet JUCE_CALLTYPE discreteChannels (int numChannels); @@ -269,10 +274,12 @@ public: { unknown = 0, + //============================================================================== left = 1, // L right = 2, // R centre = 3, // C (sometimes M for mono) + //============================================================================== LFE = 4, leftSurround = 5, // Ls rightSurround = 6, // Rs @@ -295,17 +302,67 @@ public: wideLeft = 22, wideRight = 23, - - ambisonicW = 24, - ambisonicX = 25, - ambisonicY = 26, - ambisonicZ = 27, - + //============================================================================== // Used by Dolby Atmos 7.0.2 and 7.1.2 topSideLeft = 28, // Lts (AAX), Tsl (VST) topSideRight = 29, // Rts (AAX), Tsr (VST) + //============================================================================== + // Ambisonic ACN formats - all channels are SN3D normalised + // zero-th and first-order ambisonic ACN + ambisonicACN0 = 24, + ambisonicACN1 = 25, + ambisonicACN2 = 26, + ambisonicACN3 = 27, + + // second-order ambisonic + ambisonicACN4 = 30, + ambisonicACN5 = 31, + ambisonicACN6 = 32, + ambisonicACN7 = 33, + ambisonicACN8 = 34, + + // third-order ambisonic + ambisonicACN9 = 35, + ambisonicACN10 = 36, + ambisonicACN11 = 37, + ambisonicACN12 = 38, + ambisonicACN13 = 39, + ambisonicACN14 = 40, + ambisonicACN15 = 41, + + // fourth-order ambisonic + ambisonicACN16 = 42, + ambisonicACN17 = 43, + ambisonicACN18 = 44, + ambisonicACN19 = 45, + ambisonicACN20 = 46, + ambisonicACN21 = 47, + ambisonicACN22 = 48, + ambisonicACN23 = 49, + ambisonicACN24 = 50, + + // fifth-order ambisonic + ambisonicACN25 = 51, + ambisonicACN26 = 52, + ambisonicACN27 = 53, + ambisonicACN28 = 54, + ambisonicACN29 = 55, + ambisonicACN30 = 56, + ambisonicACN31 = 57, + ambisonicACN32 = 58, + ambisonicACN33 = 59, + ambisonicACN34 = 60, + ambisonicACN35 = 61, + + //============================================================================== + ambisonicW = ambisonicACN0, + ambisonicX = ambisonicACN3, + ambisonicY = ambisonicACN1, + ambisonicZ = ambisonicACN2, + + //============================================================================== discreteChannel0 = 64 /**< Non-typed individual channels are indexed upwards from this value. */ }; @@ -321,7 +378,7 @@ public: //============================================================================== enum { - maxChannelsOfNamedLayout = 10 + maxChannelsOfNamedLayout = 36 }; /** Adds a channel to the set. */ @@ -403,6 +460,9 @@ private: //============================================================================== explicit AudioChannelSet (uint32); explicit AudioChannelSet (const Array&); + + //============================================================================== + static int JUCE_CALLTYPE getAmbisonicOrderForNumChannels (int); }; } // namespace juce diff --git a/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h b/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h index 0aaf6f6664..9434555f6a 100644 --- a/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h +++ b/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h @@ -52,6 +52,9 @@ struct CoreAudioLayouts */ static AudioChannelLayoutTag toCoreAudio (const AudioChannelSet& set) { + if (set.getAmbisonicOrder() >= 0) + return kAudioChannelLayoutTag_HOA_ACN_SN3D | static_cast (set.size()); + for (auto* tbl = SpeakerLayoutTable::get(); tbl->tag != 0; ++tbl) { AudioChannelSet caSet; @@ -125,6 +128,15 @@ struct CoreAudioLayouts } auto numChannels = tag & 0xffff; + if (tag >= kAudioChannelLayoutTag_HOA_ACN_SN3D && tag <= (kAudioChannelLayoutTag_HOA_ACN_SN3D | 0xffff)) + { + auto sqrtMinusOne = std::sqrt (static_cast (numChannels)) - 1.0f; + auto ambisonicOrder = jmax (0, static_cast (std::floor (sqrtMinusOne))); + + if (static_cast (ambisonicOrder) == sqrtMinusOne) + return AudioChannelSet::ambisonic (ambisonicOrder).getChannelTypes(); + } + for (UInt32 i = 0; i < numChannels; ++i) speakers.add (static_cast (AudioChannelSet::discreteChannel0 + i)); @@ -146,6 +158,9 @@ private: for (auto* tbl = SpeakerLayoutTable::get(); tbl->tag != 0; ++tbl) tags.addIfNotAlreadyThere (tbl->tag); + for (unsigned order = 0; order <= 5; ++order) + tags.addIfNotAlreadyThere (kAudioChannelLayoutTag_HOA_ACN_SN3D | ((order + 1) * (order + 1))); + return tags; } diff --git a/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp index dc74af2e97..3b0647a6d0 100644 --- a/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp +++ b/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp @@ -605,6 +605,17 @@ class CoreAudioLayoutsUnitTest : public UnitTest public: CoreAudioLayoutsUnitTest() : UnitTest ("Core Audio Layout <-> JUCE channel layout conversion", "Audio") {} + // some ambisonic tags which are not explicitely defined + enum + { + kAudioChannelLayoutTag_HOA_ACN_SN3D_0Order = kAudioChannelLayoutTag_HOA_ACN_SN3D | 1, + kAudioChannelLayoutTag_HOA_ACN_SN3D_1Order = kAudioChannelLayoutTag_HOA_ACN_SN3D | 4, + kAudioChannelLayoutTag_HOA_ACN_SN3D_2Order = kAudioChannelLayoutTag_HOA_ACN_SN3D | 9, + kAudioChannelLayoutTag_HOA_ACN_SN3D_3Order = kAudioChannelLayoutTag_HOA_ACN_SN3D | 16, + kAudioChannelLayoutTag_HOA_ACN_SN3D_4Order = kAudioChannelLayoutTag_HOA_ACN_SN3D | 25, + kAudioChannelLayoutTag_HOA_ACN_SN3D_5Order = kAudioChannelLayoutTag_HOA_ACN_SN3D | 36 + }; + void runTest() override { auto& knownTags = getAllKnownLayoutTags(); @@ -703,7 +714,7 @@ private: DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_MidSide), DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_XY), DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_Binaural), - DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_Ambisonic_B_Format, AudioChannelSet::ambisonic()), + DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_Ambisonic_B_Format), DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_Quadraphonic, AudioChannelSet::quadraphonic()), DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_Pentagonal, AudioChannelSet::pentagonal()), DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_Hexagonal, AudioChannelSet::hexagonal()), @@ -818,7 +829,14 @@ private: DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_8_0_B), DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_8_1_A), DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_8_1_B), - DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_6_1_D) + DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_6_1_D), + DEFINE_CHANNEL_LAYOUT_DFL_ENTRY (kAudioChannelLayoutTag_DTS_6_1_D), + DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_0Order, AudioChannelSet::ambisonic (0)), + DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_1Order, AudioChannelSet::ambisonic (1)), + DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_2Order, AudioChannelSet::ambisonic (2)), + DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_3Order, AudioChannelSet::ambisonic (3)), + DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_4Order, AudioChannelSet::ambisonic (4)), + DEFINE_CHANNEL_LAYOUT_TAG_ENTRY (kAudioChannelLayoutTag_HOA_ACN_SN3D_5Order, AudioChannelSet::ambisonic (5)) }; static Array knownTags (tags, sizeof (tags) / sizeof (CoreAudioChannelLayoutTag)); diff --git a/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp b/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp index e7b13f03cd..450acc45a0 100644 --- a/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp @@ -144,6 +144,19 @@ namespace AAXClasses AudioChannelSet::ChannelType speakerOrder[10]; }; + static AAX_EStemFormat stemFormatForAmbisonicOrder (int order) + { + switch (order) + { + case 1: return AAX_eStemFormat_Ambi_1_ACN; + case 2: return AAX_eStemFormat_Ambi_2_ACN; + case 3: return AAX_eStemFormat_Ambi_3_ACN; + default: break; + } + + return AAX_eStemFormat_INT32_MAX; + } + static AAXChannelStreamOrder aaxChannelOrder[] = { { AAX_eStemFormat_Mono, { AudioChannelSet::centre, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } }, @@ -180,7 +193,10 @@ namespace AAXClasses AAX_eStemFormat_7_0_DTS, AAX_eStemFormat_7_1_DTS, AAX_eStemFormat_7_0_2, - AAX_eStemFormat_7_1_2 + AAX_eStemFormat_7_1_2, + AAX_eStemFormat_Ambi_1_ACN, + AAX_eStemFormat_Ambi_2_ACN, + AAX_eStemFormat_Ambi_3_ACN }; static AAX_EStemFormat getFormatForAudioChannelSet (const AudioChannelSet& set, bool ignoreLayout) noexcept @@ -188,7 +204,9 @@ namespace AAXClasses // if the plug-in ignores layout, it is ok to convert between formats only by their numchannnels if (ignoreLayout) { - switch (set.size()) + auto numChannels = set.size(); + + switch (numChannels) { case 0: return AAX_eStemFormat_None; case 1: return AAX_eStemFormat_Mono; @@ -201,8 +219,17 @@ namespace AAXClasses case 8: return AAX_eStemFormat_7_1_DTS; case 9: return AAX_eStemFormat_7_0_2; case 10: return AAX_eStemFormat_7_1_2; - default: return AAX_eStemFormat_INT32_MAX; + default: break; } + + // check for ambisonics support + auto sqrtMinusOne = std::sqrt (static_cast (numChannels)) - 1.0f; + auto ambisonicOrder = jmax (0, static_cast (std::floor (sqrtMinusOne))); + + if (static_cast (ambisonicOrder) == sqrtMinusOne) + return stemFormatForAmbisonicOrder (ambisonicOrder); + + return AAX_eStemFormat_INT32_MAX; } if (set == AudioChannelSet::disabled()) return AAX_eStemFormat_None; @@ -222,6 +249,10 @@ namespace AAXClasses if (set == AudioChannelSet::create7point0point2()) return AAX_eStemFormat_7_0_2; if (set == AudioChannelSet::create7point1point2()) return AAX_eStemFormat_7_1_2; + auto order = set.getAmbisonicOrder(); + if (order >= 0) + return stemFormatForAmbisonicOrder (order); + return AAX_eStemFormat_INT32_MAX; } @@ -231,23 +262,26 @@ namespace AAXClasses { switch (format) { - case AAX_eStemFormat_None: return AudioChannelSet::disabled(); - case AAX_eStemFormat_Mono: return AudioChannelSet::mono(); - case AAX_eStemFormat_Stereo: return AudioChannelSet::stereo(); - case AAX_eStemFormat_LCR: return AudioChannelSet::createLCR(); - case AAX_eStemFormat_LCRS: return AudioChannelSet::createLCRS(); - case AAX_eStemFormat_Quad: return AudioChannelSet::quadraphonic(); - case AAX_eStemFormat_5_0: return AudioChannelSet::create5point0(); - case AAX_eStemFormat_5_1: return AudioChannelSet::create5point1(); - case AAX_eStemFormat_6_0: return AudioChannelSet::create6point0(); - case AAX_eStemFormat_6_1: return AudioChannelSet::create6point1(); - case AAX_eStemFormat_7_0_SDDS: return AudioChannelSet::create7point0SDDS(); - case AAX_eStemFormat_7_0_DTS: return AudioChannelSet::create7point0(); - case AAX_eStemFormat_7_1_SDDS: return AudioChannelSet::create7point1SDDS(); - case AAX_eStemFormat_7_1_DTS: return AudioChannelSet::create7point1(); - case AAX_eStemFormat_7_0_2: return AudioChannelSet::create7point0point2(); - case AAX_eStemFormat_7_1_2: return AudioChannelSet::create7point1point2(); - default: return AudioChannelSet::disabled(); + case AAX_eStemFormat_None: return AudioChannelSet::disabled(); + case AAX_eStemFormat_Mono: return AudioChannelSet::mono(); + case AAX_eStemFormat_Stereo: return AudioChannelSet::stereo(); + case AAX_eStemFormat_LCR: return AudioChannelSet::createLCR(); + case AAX_eStemFormat_LCRS: return AudioChannelSet::createLCRS(); + case AAX_eStemFormat_Quad: return AudioChannelSet::quadraphonic(); + case AAX_eStemFormat_5_0: return AudioChannelSet::create5point0(); + case AAX_eStemFormat_5_1: return AudioChannelSet::create5point1(); + case AAX_eStemFormat_6_0: return AudioChannelSet::create6point0(); + case AAX_eStemFormat_6_1: return AudioChannelSet::create6point1(); + case AAX_eStemFormat_7_0_SDDS: return AudioChannelSet::create7point0SDDS(); + case AAX_eStemFormat_7_0_DTS: return AudioChannelSet::create7point0(); + case AAX_eStemFormat_7_1_SDDS: return AudioChannelSet::create7point1SDDS(); + case AAX_eStemFormat_7_1_DTS: return AudioChannelSet::create7point1(); + case AAX_eStemFormat_7_0_2: return AudioChannelSet::create7point0point2(); + case AAX_eStemFormat_7_1_2: return AudioChannelSet::create7point1point2(); + case AAX_eStemFormat_Ambi_1_ACN: return AudioChannelSet::ambisonic (1); + case AAX_eStemFormat_Ambi_2_ACN: return AudioChannelSet::ambisonic (2); + case AAX_eStemFormat_Ambi_3_ACN: return AudioChannelSet::ambisonic (3); + default: return AudioChannelSet::disabled(); } } @@ -283,9 +317,13 @@ namespace AAXClasses static int juceChannelIndexToAax (int juceIndex, const AudioChannelSet& channelSet) { + auto isAmbisonic = (channelSet.getAmbisonicOrder() >= 0); auto currentLayout = getFormatForAudioChannelSet (channelSet, false); int layoutIndex; + if (isAmbisonic && currentLayout != AAX_eStemFormat_INT32_MAX) + return juceIndex; + for (layoutIndex = 0; aaxChannelOrder[layoutIndex].aaxStemFormat != currentLayout; ++layoutIndex) if (aaxChannelOrder[layoutIndex].aaxStemFormat == 0) return juceIndex; @@ -1915,6 +1953,12 @@ namespace AAXClasses { ScopedPointer props (featureInfo->AcquireProperties()); + // Due to a bug in ProTools 12.8, ProTools thinks that AAX_eStemFormat_Ambi_1_ACN is not supported + // To workaround this bug, check if ProTools supports AAX_eStemFormat_Ambi_2_ACN, and, if yes, + // we can safely assume that it will also support AAX_eStemFormat_Ambi_1_ACN + if (stemFormat == AAX_eStemFormat_Ambi_1_ACN) + stemFormat = AAX_eStemFormat_Ambi_2_ACN; + if (props != nullptr && props->GetProperty ((AAX_EProperty) stemFormat, (AAX_CPropertyValue*) &supportLevel) != 0) return (supportLevel == AAX_eSupportLevel_Supported); } diff --git a/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm b/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm index c715c0ce2d..0e4eb016f8 100644 --- a/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm +++ b/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm @@ -1168,8 +1168,6 @@ public: err = MusicDeviceBase::ChangeStreamFormat (scope, element, old, format); - DBG (set.getDescription()); - if (err == noErr) currentTag = CoreAudioLayouts::toCoreAudio (set); diff --git a/modules/juce_audio_processors/format_types/juce_VST3Common.h b/modules/juce_audio_processors/format_types/juce_VST3Common.h index e98fafb373..e62177c6a5 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3Common.h +++ b/modules/juce_audio_processors/format_types/juce_VST3Common.h @@ -133,6 +133,9 @@ static inline Steinberg::Vst::SpeakerArrangement getArrangementForNumChannels (i case 12: return k111; case 13: return k130; case 14: return k131; + #if VST_VERSION >= 0x030608 + case 16: return kAmbi3rdOrderACN; + #endif case 24: return (Steinberg::Vst::SpeakerArrangement) 1929904127; // k222 default: break; } @@ -160,9 +163,9 @@ static inline Steinberg::Vst::Speaker getSpeakerType (const AudioChannelSet& set case AudioChannelSet::leftCentre: return kSpeakerLc; case AudioChannelSet::rightCentre: return kSpeakerRc; case AudioChannelSet::centreSurround: return kSpeakerCs; - case AudioChannelSet::leftSurroundSide: return (1 << 26); /* kSpeakerLcs */ - case AudioChannelSet::rightSurroundSide: return (1 << 27); /* kSpeakerRcs */ - case AudioChannelSet::topMiddle: return (1 << 11); /* kSpeakerTm */ + case AudioChannelSet::leftSurroundSide: return (1ull << 26); /* kSpeakerLcs */ + case AudioChannelSet::rightSurroundSide: return (1ull << 27); /* kSpeakerRcs */ + case AudioChannelSet::topMiddle: return (1ull << 11); /* kSpeakerTm */ case AudioChannelSet::topFrontLeft: return kSpeakerTfl; case AudioChannelSet::topFrontCentre: return kSpeakerTfc; case AudioChannelSet::topFrontRight: return kSpeakerTfr; @@ -174,12 +177,24 @@ static inline Steinberg::Vst::Speaker getSpeakerType (const AudioChannelSet& set case AudioChannelSet::rightSurroundRear: return kSpeakerSr; case AudioChannelSet::wideLeft: return kSpeakerPl; case AudioChannelSet::wideRight: return kSpeakerPr; - case AudioChannelSet::ambisonicW: return (1 << 20); /* kSpeakerW */ - case AudioChannelSet::ambisonicX: return (1 << 21); /* kSpeakerX */ - case AudioChannelSet::ambisonicY: return (1 << 22); /* kSpeakerY */ - case AudioChannelSet::ambisonicZ: return (1 << 23); /* kSpeakerZ */ - case AudioChannelSet::topSideLeft: return (1 << 24); /* kSpeakerTsl */ - case AudioChannelSet::topSideRight: return (1 << 25); /* kSpeakerTsr */ + case AudioChannelSet::ambisonicACN0: return (1ull << 20); /* kSpeakerACN0 */ + case AudioChannelSet::ambisonicACN1: return (1ull << 21); /* kSpeakerACN1 */ + case AudioChannelSet::ambisonicACN2: return (1ull << 22); /* kSpeakerACN2 */ + case AudioChannelSet::ambisonicACN3: return (1ull << 23); /* kSpeakerACN3 */ + case AudioChannelSet::ambisonicACN4: return (1ull << 38); /* kSpeakerACN4 */ + case AudioChannelSet::ambisonicACN5: return (1ull << 39); /* kSpeakerACN5 */ + case AudioChannelSet::ambisonicACN6: return (1ull << 40); /* kSpeakerACN6 */ + case AudioChannelSet::ambisonicACN7: return (1ull << 41); /* kSpeakerACN7 */ + case AudioChannelSet::ambisonicACN8: return (1ull << 42); /* kSpeakerACN8 */ + case AudioChannelSet::ambisonicACN9: return (1ull << 43); /* kSpeakerACN9 */ + case AudioChannelSet::ambisonicACN10: return (1ull << 44); /* kSpeakerACN10 */ + case AudioChannelSet::ambisonicACN11: return (1ull << 45); /* kSpeakerACN11 */ + case AudioChannelSet::ambisonicACN12: return (1ull << 46); /* kSpeakerACN12 */ + case AudioChannelSet::ambisonicACN13: return (1ull << 47); /* kSpeakerACN13 */ + case AudioChannelSet::ambisonicACN14: return (1ull << 48); /* kSpeakerACN14 */ + case AudioChannelSet::ambisonicACN15: return (1ull << 49); /* kSpeakerACN15 */ + case AudioChannelSet::topSideLeft: return (1ull << 24); /* kSpeakerTsl */ + case AudioChannelSet::topSideRight: return (1ull << 25); /* kSpeakerTsr */ case AudioChannelSet::discreteChannel0: return kSpeakerM; default: @@ -189,9 +204,9 @@ static inline Steinberg::Vst::Speaker getSpeakerType (const AudioChannelSet& set switch (static_cast (type)) { - case (int) AudioChannelSet::discreteChannel0 + 3: return (1 << 28); /* kSpeakerBfl */ - case (int) AudioChannelSet::discreteChannel0 + 4: return (1 << 29); /* kSpeakerBfc */ - case (int) AudioChannelSet::discreteChannel0 + 5: return (1 << 30); /* kSpeakerBfr */ + case (int) AudioChannelSet::discreteChannel0 + 3: return (1ull << 28); /* kSpeakerBfl */ + case (int) AudioChannelSet::discreteChannel0 + 4: return (1ull << 29); /* kSpeakerBfc */ + case (int) AudioChannelSet::discreteChannel0 + 5: return (1ull << 30); /* kSpeakerBfr */ default: break; } @@ -217,7 +232,7 @@ static inline AudioChannelSet::ChannelType getChannelType (Steinberg::Vst::Speak case kSpeakerCs: return AudioChannelSet::centreSurround; case kSpeakerSl: return AudioChannelSet::leftSurroundRear; case kSpeakerSr: return AudioChannelSet::rightSurroundRear; - case (1 << 11): return AudioChannelSet::topMiddle; /* kSpeakerTm */ + case (1ull << 11): return AudioChannelSet::topMiddle; /* kSpeakerTm */ case kSpeakerTfl: return AudioChannelSet::topFrontLeft; case kSpeakerTfc: return AudioChannelSet::topFrontCentre; case kSpeakerTfr: return AudioChannelSet::topFrontRight; @@ -225,18 +240,30 @@ static inline AudioChannelSet::ChannelType getChannelType (Steinberg::Vst::Speak case kSpeakerTrc: return AudioChannelSet::topRearCentre; case kSpeakerTrr: return AudioChannelSet::topRearRight; case kSpeakerLfe2: return AudioChannelSet::LFE2; - case (1 << 19): return ((arr & kSpeakerC) != 0 ? AudioChannelSet::discreteChannel0 : AudioChannelSet::centre); - case (1 << 20): return AudioChannelSet::ambisonicW; /* kSpeakerW */ - case (1 << 21): return AudioChannelSet::ambisonicX; /* kSpeakerX */ - case (1 << 22): return AudioChannelSet::ambisonicY; /* kSpeakerY */ - case (1 << 23): return AudioChannelSet::ambisonicZ; /* kSpeakerZ */ - case (1 << 24): return AudioChannelSet::topSideLeft; /* kSpeakerTsl */ - case (1 << 25): return AudioChannelSet::topSideRight; /* kSpeakerTsr */ - case (1 << 26): return AudioChannelSet::leftSurroundSide; /* kSpeakerLcs */ - case (1 << 27): return AudioChannelSet::rightSurroundSide; /* kSpeakerRcs */ - case (1 << 28): return static_cast ((int)AudioChannelSet::discreteChannel0 + 3); /* kSpeakerBfl */ - case (1 << 29): return static_cast ((int)AudioChannelSet::discreteChannel0 + 4); /* kSpeakerBfc */ - case (1 << 30): return static_cast ((int)AudioChannelSet::discreteChannel0 + 5); /* kSpeakerBfr */ + case (1ull << 19): return ((arr & kSpeakerC) != 0 ? AudioChannelSet::discreteChannel0 : AudioChannelSet::centre); + case (1ull << 20): return AudioChannelSet::ambisonicACN0; /* kSpeakerACN0 */ + case (1ull << 21): return AudioChannelSet::ambisonicACN1; /* kSpeakerACN1 */ + case (1ull << 22): return AudioChannelSet::ambisonicACN2; /* kSpeakerACN2 */ + case (1ull << 23): return AudioChannelSet::ambisonicACN3; /* kSpeakerACN3 */ + case (1ull << 38): return AudioChannelSet::ambisonicACN4; /* kSpeakerACN4 */ + case (1ull << 39): return AudioChannelSet::ambisonicACN5; /* kSpeakerACN5 */ + case (1ull << 40): return AudioChannelSet::ambisonicACN6; /* kSpeakerACN6 */ + case (1ull << 41): return AudioChannelSet::ambisonicACN7; /* kSpeakerACN7 */ + case (1ull << 42): return AudioChannelSet::ambisonicACN8; /* kSpeakerACN8 */ + case (1ull << 43): return AudioChannelSet::ambisonicACN9; /* kSpeakerACN9 */ + case (1ull << 44): return AudioChannelSet::ambisonicACN10; /* kSpeakerACN10 */ + case (1ull << 45): return AudioChannelSet::ambisonicACN11; /* kSpeakerACN11 */ + case (1ull << 46): return AudioChannelSet::ambisonicACN12; /* kSpeakerACN12 */ + case (1ull << 47): return AudioChannelSet::ambisonicACN13; /* kSpeakerACN13 */ + case (1ull << 48): return AudioChannelSet::ambisonicACN14; /* kSpeakerACN14 */ + case (1ull << 49): return AudioChannelSet::ambisonicACN15; /* kSpeakerACN15 */ + case (1ull << 24): return AudioChannelSet::topSideLeft; /* kSpeakerTsl */ + case (1ull << 25): return AudioChannelSet::topSideRight; /* kSpeakerTsr */ + case (1ull << 26): return AudioChannelSet::leftSurroundSide; /* kSpeakerLcs */ + case (1ull << 27): return AudioChannelSet::rightSurroundSide; /* kSpeakerRcs */ + case (1ull << 28): return static_cast ((int)AudioChannelSet::discreteChannel0 + 3); /* kSpeakerBfl */ + case (1ull << 29): return static_cast ((int)AudioChannelSet::discreteChannel0 + 4); /* kSpeakerBfc */ + case (1ull << 30): return static_cast ((int)AudioChannelSet::discreteChannel0 + 5); /* kSpeakerBfr */ case kSpeakerPl: return AudioChannelSet::wideLeft; case kSpeakerPr: return AudioChannelSet::wideRight; default: break; @@ -274,6 +301,12 @@ static inline Steinberg::Vst::SpeakerArrangement getVst3SpeakerArrangement (cons else if (channels == AudioChannelSet::quadraphonic()) return k40Music; else if (channels == AudioChannelSet::create7point0point2()) return k71_2 & ~(Steinberg::Vst::kSpeakerLfe); else if (channels == AudioChannelSet::create7point1point2()) return k71_2; + else if (channels == AudioChannelSet::ambisonic (0)) return (1ull << 20); + else if (channels == AudioChannelSet::ambisonic (1)) return (1ull << 20) | (1ull << 21) | (1ull << 22) | (1ull << 23); + #if VST_VERSION >= 0x030608 + else if (channels == AudioChannelSet::ambisonic (2)) return kAmbi2cdOrderACN; + else if (channels == AudioChannelSet::ambisonic (3)) return kAmbi3rdOrderACN; + #endif Steinberg::Vst::SpeakerArrangement result = 0; @@ -289,26 +322,32 @@ static inline AudioChannelSet getChannelSetForSpeakerArrangement (Steinberg::Vst { using namespace Steinberg::Vst::SpeakerArr; - if (arr == kEmpty) return AudioChannelSet::disabled(); - else if (arr == kMono) return AudioChannelSet::mono(); - else if (arr == kStereo) return AudioChannelSet::stereo(); - else if (arr == k30Cine) return AudioChannelSet::createLCR(); - else if (arr == k30Music) return AudioChannelSet::createLRS(); - else if (arr == k40Cine) return AudioChannelSet::createLCRS(); - else if (arr == k50) return AudioChannelSet::create5point0(); - else if (arr == k51) return AudioChannelSet::create5point1(); - else if (arr == k60Cine) return AudioChannelSet::create6point0(); - else if (arr == k61Cine) return AudioChannelSet::create6point1(); - else if (arr == k60Music) return AudioChannelSet::create6point0Music(); - else if (arr == k61Music) return AudioChannelSet::create6point1Music(); - else if (arr == k70Music) return AudioChannelSet::create7point0(); - else if (arr == k70Cine) return AudioChannelSet::create7point0SDDS(); - else if (arr == k71CineSideFill) return AudioChannelSet::create7point1(); - else if (arr == k71Cine) return AudioChannelSet::create7point1SDDS(); - else if (arr == kAmbi1stOrderACN) return AudioChannelSet::ambisonic(); - else if (arr == k40Music) return AudioChannelSet::quadraphonic(); - else if (arr == k71_2) return AudioChannelSet::create7point1point2(); - else if (arr == (k71_2 & ~(Steinberg::Vst::kSpeakerLfe))) return AudioChannelSet::create7point0point2(); + if (arr == kEmpty) return AudioChannelSet::disabled(); + else if (arr == kMono) return AudioChannelSet::mono(); + else if (arr == kStereo) return AudioChannelSet::stereo(); + else if (arr == k30Cine) return AudioChannelSet::createLCR(); + else if (arr == k30Music) return AudioChannelSet::createLRS(); + else if (arr == k40Cine) return AudioChannelSet::createLCRS(); + else if (arr == k50) return AudioChannelSet::create5point0(); + else if (arr == k51) return AudioChannelSet::create5point1(); + else if (arr == k60Cine) return AudioChannelSet::create6point0(); + else if (arr == k61Cine) return AudioChannelSet::create6point1(); + else if (arr == k60Music) return AudioChannelSet::create6point0Music(); + else if (arr == k61Music) return AudioChannelSet::create6point1Music(); + else if (arr == k70Music) return AudioChannelSet::create7point0(); + else if (arr == k70Cine) return AudioChannelSet::create7point0SDDS(); + else if (arr == k71CineSideFill) return AudioChannelSet::create7point1(); + else if (arr == k71Cine) return AudioChannelSet::create7point1SDDS(); + else if (arr == kAmbi1stOrderACN) return AudioChannelSet::ambisonic(); + else if (arr == k40Music) return AudioChannelSet::quadraphonic(); + else if (arr == k71_2) return AudioChannelSet::create7point1point2(); + else if (arr == (k71_2 & ~(Steinberg::Vst::kSpeakerLfe))) return AudioChannelSet::create7point0point2(); + else if (arr == (1 << 20)) return AudioChannelSet::ambisonic (0); + else if (arr == ((1 << 20) | (1 << 21) | (1 << 22) | (1 << 23))) return AudioChannelSet::ambisonic (1); + #if VST_VERSION >= 0x030608 + else if (arr == kAmbi2cdOrderACN) return AudioChannelSet::ambisonic (2); + else if (arr == kAmbi3rdOrderACN) return AudioChannelSet::ambisonic (3); + #endif AudioChannelSet result; diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp b/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp index 960392c4f4..9366876ee3 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp +++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp @@ -1365,6 +1365,9 @@ int32 AudioProcessor::getAAXPluginIDForMainBusConfig (const AudioChannelSet& mai else if (set == AudioChannelSet::create7point1SDDS()) aaxFormatIndex = 13; else if (set == AudioChannelSet::create7point0point2()) aaxFormatIndex = 14; else if (set == AudioChannelSet::create7point1point2()) aaxFormatIndex = 15; + else if (set == AudioChannelSet::ambisonic (1)) aaxFormatIndex = 16; + else if (set == AudioChannelSet::ambisonic (2)) aaxFormatIndex = 17; + else if (set == AudioChannelSet::ambisonic (3)) aaxFormatIndex = 18; else { // AAX does not support this format and the wrapper should not have