1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

VST3: Add support for new wide/Atmos speaker layouts

This commit is contained in:
reuk 2024-05-30 13:57:42 +01:00
parent 34e454e3f1
commit a42a498f5e
No known key found for this signature in database
GPG key ID: FCB43929F012EE5C
4 changed files with 158 additions and 35 deletions

View file

@ -214,7 +214,6 @@ HarfBuzz Typeface implementation. New support for automatic font fallback
will be introduced in JUCE 8, and this will obviate much of the need for will be introduced in JUCE 8, and this will obviate much of the need for
CustomTypeface. CustomTypeface.
>>>>>>> 94454123d6 (Typeface: Implement platform typefaces using Harfbuzz hb_font_t)
## Change ## Change
@ -428,6 +427,48 @@ the licensing situation and encourages the creation of more open source software
without impacting personal use of the JUCE framework. without impacting personal use of the JUCE framework.
# Version 7.0.12
## Change
The function AudioChannelSet::create9point0point4, along with variants for
9.1.4, 9.0.6, and 9.1.6, used to correspond to VST3 layouts k90_4, k91_4,
k90_6, and k91_6 respectively. These functions now correspond to k90_4_W,
k91_4_W, k90_6_W, and k91_6_W respectively.
**Possible Issues**
VST3 plugins that used these AudioChannelSet layouts to specify initial bus
layouts, or to validate layouts in isBusesLayoutSupported, will now behave
differently.
For example, if the host wants to check whether the k90_4 layout is supported,
previously isBusesLayoutSupported() would have received the layout created by
create9point0point4(), but will now receive the layout created by
create9point0point4ITU().
**Workaround**
If you already have special-case handling for specific surround layouts,
e.g. to enable or disable them in isBusesLayoutSupported(), you may need to
add cases to handle the new AudioChannelSet::create*ITU() layout variants.
**Rationale**
Previously, the VST3 SDK only contained ITU higher-order surround layouts, but
the higher-order layouts specified in JUCE used Atmos speaker positions rather
than ITU speaker positions. This meant that JUCE had to remap speaker layouts
between Atmos/ITU formats when communicating with VST3 plugins. This was
confusing, as it required that the meaning of some channels was changed during
the conversion.
In newer versions of the VST3 SDK, new "wide" left and right speaker
definitions are available, allowing both ITU and Atmos surround layouts to be
represented. The change in JUCE surfaces this distinction to the user, allowing
them to determine e.g. whether the host has requested an ITU or an Atmos
layout, and to handle these cases separately if necessary.
# Version 7.0.10 # Version 7.0.10
## Change ## Change

View file

@ -268,33 +268,51 @@ public:
*/ */
static AudioChannelSet JUCE_CALLTYPE create7point1point6(); static AudioChannelSet JUCE_CALLTYPE create7point1point6();
/** Creates a set for a 9.0.4 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topRearLeft, topRearRight). /** Creates a set for a 9.0.4 Atmos surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topRearLeft, topRearRight).
Is equivalent to: k90_4 (VST3), AAX_eStemFormat_9_0_4 (AAX). Is equivalent to: k90_4_W (VST3), AAX_eStemFormat_9_0_4 (AAX).
@see create9point0point4ITU()
*/ */
static AudioChannelSet JUCE_CALLTYPE create9point0point4(); static AudioChannelSet JUCE_CALLTYPE create9point0point4();
/** Creates a set for a 9.1.4 surround setup (left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topRearLeft, topRearRight). /** Creates a set for a 9.1.4 Atmos surround setup (left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topRearLeft, topRearRight).
Is equivalent to: k91_4 (VST3), AAX_eStemFormat_9_1_4 (AAX). Is equivalent to: k91_4_W (VST3), AAX_eStemFormat_9_1_4 (AAX).
@see create9point1point4ITU()
*/ */
static AudioChannelSet JUCE_CALLTYPE create9point1point4(); static AudioChannelSet JUCE_CALLTYPE create9point1point4();
/** Creates a set for a 9.0.6 surround setup (left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight). /** Creates a set for a 9.0.6 Atmos surround setup (left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight).
Is equivalent to: k90_6 (VST3), AAX_eStemFormat_9_0_6 (AAX). Is equivalent to: k90_6_W (VST3), AAX_eStemFormat_9_0_6 (AAX).
@see create9point0point6ITU()
*/ */
static AudioChannelSet JUCE_CALLTYPE create9point0point6(); static AudioChannelSet JUCE_CALLTYPE create9point0point6();
/** Creates a set for a 9.1.6 surround setup (left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight). /** Creates a set for a 9.1.6 Atmos surround setup (left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight).
Note that the VST3 layout arranges the front speakers "L Lc C Rc R", but the JUCE layout Older versions of the VST3 SDK only supported ITU versions of the 9.0.4, 9.1.4, 9.0.6, and
uses the arrangement "wideLeft left centre right wideRight". To maintain the relative 9.1.6 layouts, which have the front-channel ordering "L Lc C Rc R".
positions of the speakers, the channels will be remapped accordingly. This means that the To maintain the correct relative channel ordering, JUCE would perform the following mapping:
VST3 host's "L" channel will be received on a JUCE plugin's "wideLeft" channel, the L -> wideLeft, Lc -> left, Rc -> right, R -> wideRight
"Lc" channel will be received on a JUCE plugin's "left" channel, and so on.
Is equivalent to: k91_6 (VST3), kAudioChannelLayoutTag_Atmos_9_1_6 (CoreAudio). The version of the VST3 SDK bundled with JUCE now supports Atmos versions of the above
layouts, which have the front-channel ordering "Lw L C R Rw". This order matches the
JUCE ordering, so no remapping is required.
create9point0point4(), create9point1point4(), create9point0point6(), and
create9point1point6() now correspond to the VST3 k90_4_W, k91_4_W, k90_6_W, and k91_6_W
Atmos layouts respectively.
If you need to support the old ITU layouts, use create9point0point4ITU(),
create9point1point4ITU(), create9point0point6ITU(), and create9point1point6ITU() instead.
Is equivalent to: k91_6_W (VST3), kAudioChannelLayoutTag_Atmos_9_1_6 (CoreAudio).
@see create9point1point6ITU()
*/ */
static AudioChannelSet JUCE_CALLTYPE create9point1point6(); static AudioChannelSet JUCE_CALLTYPE create9point1point6();

View file

@ -269,6 +269,8 @@ static std::optional<Steinberg::Vst::Speaker> getSpeakerType (const AudioChannel
case AudioChannelSet::bottomRearLeft: return Steinberg::Vst::kSpeakerBrl; case AudioChannelSet::bottomRearLeft: return Steinberg::Vst::kSpeakerBrl;
case AudioChannelSet::bottomRearCentre: return Steinberg::Vst::kSpeakerBrc; case AudioChannelSet::bottomRearCentre: return Steinberg::Vst::kSpeakerBrc;
case AudioChannelSet::bottomRearRight: return Steinberg::Vst::kSpeakerBrr; case AudioChannelSet::bottomRearRight: return Steinberg::Vst::kSpeakerBrr;
case AudioChannelSet::wideLeft: return Steinberg::Vst::kSpeakerLw;
case AudioChannelSet::wideRight: return Steinberg::Vst::kSpeakerRw;
case AudioChannelSet::discreteChannel0: return Steinberg::Vst::kSpeakerM; case AudioChannelSet::discreteChannel0: return Steinberg::Vst::kSpeakerM;
@ -311,8 +313,6 @@ static std::optional<Steinberg::Vst::Speaker> getSpeakerType (const AudioChannel
case AudioChannelSet::ambisonicACN61: case AudioChannelSet::ambisonicACN61:
case AudioChannelSet::ambisonicACN62: case AudioChannelSet::ambisonicACN62:
case AudioChannelSet::ambisonicACN63: case AudioChannelSet::ambisonicACN63:
case AudioChannelSet::wideLeft:
case AudioChannelSet::wideRight:
case AudioChannelSet::unknown: case AudioChannelSet::unknown:
break; break;
} }
@ -383,6 +383,8 @@ static std::optional<AudioChannelSet::ChannelType> getChannelType (Steinberg::Vs
case Steinberg::Vst::kSpeakerBrl: return AudioChannelSet::bottomRearLeft; case Steinberg::Vst::kSpeakerBrl: return AudioChannelSet::bottomRearLeft;
case Steinberg::Vst::kSpeakerBrc: return AudioChannelSet::bottomRearCentre; case Steinberg::Vst::kSpeakerBrc: return AudioChannelSet::bottomRearCentre;
case Steinberg::Vst::kSpeakerBrr: return AudioChannelSet::bottomRearRight; case Steinberg::Vst::kSpeakerBrr: return AudioChannelSet::bottomRearRight;
case Steinberg::Vst::kSpeakerLw: return AudioChannelSet::wideLeft;
case Steinberg::Vst::kSpeakerRw: return AudioChannelSet::wideRight;
} }
return {}; return {};
@ -433,11 +435,15 @@ namespace detail
{ k71_6, { X::left, X::right, X::centre, X::LFE, X::leftSurroundRear, X::rightSurroundRear, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight, X::topSideLeft, X::topSideRight } }, { k71_6, { X::left, X::right, X::centre, X::LFE, X::leftSurroundRear, X::rightSurroundRear, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight, X::topSideLeft, X::topSideRight } },
{ k70_6, { X::left, X::right, X::centre, X::leftSurroundRear, X::rightSurroundRear, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight, X::topSideLeft, X::topSideRight } }, { k70_6, { X::left, X::right, X::centre, X::leftSurroundRear, X::rightSurroundRear, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight, X::topSideLeft, X::topSideRight } },
// The VST3 layout uses 'left/right' and 'left-of-center/right-of-center', but the JUCE layout uses 'left/right' and 'wide-left/wide-right'. { k90_4_W, { X::left, X::right, X::centre, X::leftSurroundRear, X::rightSurroundRear, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight, X::wideLeft, X::wideRight } },
{ k91_4, { X::wideLeft, X::wideRight, X::centre, X::LFE, X::leftSurroundRear, X::rightSurroundRear, X::left, X::right, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight } }, { k91_4_W, { X::left, X::right, X::centre, X::LFE, X::leftSurroundRear, X::rightSurroundRear, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight, X::wideLeft, X::wideRight } },
{ k90_4, { X::wideLeft, X::wideRight, X::centre, X::leftSurroundRear, X::rightSurroundRear, X::left, X::right, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight } }, { k90_6_W, { X::left, X::right, X::centre, X::leftSurroundRear, X::rightSurroundRear, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight, X::topSideLeft, X::topSideRight, X::wideLeft, X::wideRight } },
{ k91_6, { X::wideLeft, X::wideRight, X::centre, X::LFE, X::leftSurroundRear, X::rightSurroundRear, X::left, X::right, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight, X::topSideLeft, X::topSideRight } }, { k91_6_W, { X::left, X::right, X::centre, X::LFE, X::leftSurroundRear, X::rightSurroundRear, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight, X::topSideLeft, X::topSideRight, X::wideLeft, X::wideRight } },
{ k90_6, { X::wideLeft, X::wideRight, X::centre, X::leftSurroundRear, X::rightSurroundRear, X::left, X::right, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight, X::topSideLeft, X::topSideRight } },
{ k90_4, { X::left, X::right, X::centre, X::leftSurround, X::rightSurround, X::leftCentre, X::rightCentre, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight } },
{ k91_4, { X::left, X::right, X::centre, X::LFE, X::leftSurround, X::rightSurround, X::leftCentre, X::rightCentre, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight } },
{ k90_6, { X::left, X::right, X::centre, X::leftSurround, X::rightSurround, X::leftCentre, X::rightCentre, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight, X::topSideLeft, X::topSideRight } },
{ k91_6, { X::left, X::right, X::centre, X::LFE, X::leftSurround, X::rightSurround, X::leftCentre, X::rightCentre, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight, X::topSideLeft, X::topSideRight } },
}; };
#if JUCE_DEBUG #if JUCE_DEBUG

View file

@ -57,9 +57,9 @@ public:
expect (map.getJuceChannelForVst3Channel (1) == 1); // R -> right expect (map.getJuceChannelForVst3Channel (1) == 1); // R -> right
} }
beginTest ("ChannelMapping for a 9.1.6 bus remaps the channels appropriately"); beginTest ("ChannelMapping for a k91_6 bus remaps the channels appropriately");
{ {
ChannelMapping map (AudioChannelSet::create9point1point6()); ChannelMapping map (AudioChannelSet::create9point1point6ITU());
expect (map.size() == 16); expect (map.size() == 16);
// VST3 order is: // VST3 order is:
@ -80,6 +80,64 @@ public:
// Tsl // Tsl
// Tsr // Tsr
// JUCE order is: // JUCE order is:
// left
// right
// centre
// LFE
// leftSurround
// rightSurround
// leftCentre
// rightCentre
// leftSurroundSide
// rightSurroundSide
// topFrontLeft
// topRearRight
// topRearLeft
// topRearRight
// topSideLeft
// topSideRight
expect (map.getJuceChannelForVst3Channel (0) == 0); // L -> left
expect (map.getJuceChannelForVst3Channel (1) == 1); // R -> right
expect (map.getJuceChannelForVst3Channel (2) == 2); // C -> centre
expect (map.getJuceChannelForVst3Channel (3) == 3); // Lfe -> LFE
expect (map.getJuceChannelForVst3Channel (4) == 4); // Ls -> leftSurround
expect (map.getJuceChannelForVst3Channel (5) == 5); // Rs -> rightSurround
expect (map.getJuceChannelForVst3Channel (6) == 6); // Lc -> leftCentre
expect (map.getJuceChannelForVst3Channel (7) == 7); // Rc -> rightCentre
expect (map.getJuceChannelForVst3Channel (8) == 8); // Sl -> leftSurroundSide
expect (map.getJuceChannelForVst3Channel (9) == 9); // Sr -> rightSurroundSide
expect (map.getJuceChannelForVst3Channel (10) == 10); // Tfl -> topFrontLeft
expect (map.getJuceChannelForVst3Channel (11) == 11); // Tfr -> topFrontRight
expect (map.getJuceChannelForVst3Channel (12) == 12); // Trl -> topRearLeft
expect (map.getJuceChannelForVst3Channel (13) == 13); // Trr -> topRearRight
expect (map.getJuceChannelForVst3Channel (14) == 14); // Tsl -> topSideLeft
expect (map.getJuceChannelForVst3Channel (15) == 15); // Tsr -> topSideRight
}
beginTest ("ChannelMapping for a k91_6_W bus remaps the channels appropriately");
{
ChannelMapping map (AudioChannelSet::create9point1point6());
expect (map.size() == 16);
// VST3 order is:
// L
// R
// C
// Lfe
// Ls
// Rs
// Sl
// Sr
// Tfl
// Tfr
// Trl
// Trr
// Tsl
// Tsr
// Lw
// Rw
// JUCE order is:
// Left // Left
// Right // Right
// Centre // Centre
@ -97,22 +155,22 @@ public:
// Top Side Left // Top Side Left
// Top Side Right // Top Side Right
expect (map.getJuceChannelForVst3Channel (0) == 12); // L -> wideLeft expect (map.getJuceChannelForVst3Channel (0) == 0); // L -> left
expect (map.getJuceChannelForVst3Channel (1) == 13); // R -> wideRight expect (map.getJuceChannelForVst3Channel (1) == 1); // R -> right
expect (map.getJuceChannelForVst3Channel (2) == 2); // C -> centre expect (map.getJuceChannelForVst3Channel (2) == 2); // C -> centre
expect (map.getJuceChannelForVst3Channel (3) == 3); // Lfe -> LFE expect (map.getJuceChannelForVst3Channel (3) == 3); // Lfe -> LFE
expect (map.getJuceChannelForVst3Channel (4) == 10); // Ls -> leftSurroundRear expect (map.getJuceChannelForVst3Channel (4) == 10); // Ls -> leftSurroundRear
expect (map.getJuceChannelForVst3Channel (5) == 11); // Rs -> rightSurroundRear expect (map.getJuceChannelForVst3Channel (5) == 11); // Rs -> rightSurroundRear
expect (map.getJuceChannelForVst3Channel (6) == 0); // Lc -> left expect (map.getJuceChannelForVst3Channel (6) == 4); // Sl -> leftSurroundSide
expect (map.getJuceChannelForVst3Channel (7) == 1); // Rc -> right expect (map.getJuceChannelForVst3Channel (7) == 5); // Sr -> rightSurroundSide
expect (map.getJuceChannelForVst3Channel (8) == 4); // Sl -> leftSurroundSide expect (map.getJuceChannelForVst3Channel (8) == 6); // Tfl -> topFrontLeft
expect (map.getJuceChannelForVst3Channel (9) == 5); // Sl -> leftSurroundSide expect (map.getJuceChannelForVst3Channel (9) == 7); // Tfr -> topFrontRight
expect (map.getJuceChannelForVst3Channel (10) == 6); // Tfl -> topFrontLeft expect (map.getJuceChannelForVst3Channel (10) == 8); // Trl -> topRearLeft
expect (map.getJuceChannelForVst3Channel (11) == 7); // Tfr -> topFrontRight expect (map.getJuceChannelForVst3Channel (11) == 9); // Trr -> topRearRight
expect (map.getJuceChannelForVst3Channel (12) == 8); // Trl -> topRearLeft expect (map.getJuceChannelForVst3Channel (12) == 14); // Tsl -> topSideLeft
expect (map.getJuceChannelForVst3Channel (13) == 9); // Trr -> topRearRight expect (map.getJuceChannelForVst3Channel (13) == 15); // Tsr -> topSideRight
expect (map.getJuceChannelForVst3Channel (14) == 14); // Tsl -> topSideLeft expect (map.getJuceChannelForVst3Channel (14) == 12); // Lw -> wideLeft
expect (map.getJuceChannelForVst3Channel (15) == 15); // Tsr -> topSideRight expect (map.getJuceChannelForVst3Channel (15) == 13); // Lw -> wideRight
} }
const auto blockSize = 128; const auto blockSize = 128;