Browse Source

VST3: Tidy up channel layout conversion tables

pull/22/head
reuk 3 years ago
parent
commit
a8c160691c
No known key found for this signature in database GPG Key ID: FCB43929F012EE5C
2 changed files with 160 additions and 71 deletions
  1. +7
    -1
      modules/juce_audio_basics/buffers/juce_AudioChannelSet.h
  2. +153
    -70
      modules/juce_audio_processors/format_types/juce_VST3Common.h

+ 7
- 1
modules/juce_audio_basics/buffers/juce_AudioChannelSet.h View File

@@ -240,7 +240,13 @@ public:
/** 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).
Is equivalent to: kAudioChannelLayoutTag_Atmos_9_1_6 (CoreAudio).
Note that the VST3 layout arranges the front speakers "L Lc C Rc R", but the JUCE layout
uses the arrangement "wideLeft left centre right wideRight". To maintain the relative
positions of the speakers, the channels will be remapped accordingly. This means that the
VST3 host's "L" channel will be received on a JUCE plugin's "wideLeft" channel, the
"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).
*/
static AudioChannelSet JUCE_CALLTYPE create9point1point6();


+ 153
- 70
modules/juce_audio_processors/format_types/juce_VST3Common.h View File

@@ -346,41 +346,116 @@ static AudioChannelSet::ChannelType getChannelType (Steinberg::Vst::SpeakerArran
return static_cast<AudioChannelSet::ChannelType> (static_cast<int> (AudioChannelSet::discreteChannel0) + 6 + (channelType - 33));
}
namespace detail
{
struct LayoutPair
{
Steinberg::Vst::SpeakerArrangement arrangement;
std::initializer_list<AudioChannelSet::ChannelType> channelOrder;
};
using namespace Steinberg::Vst::SpeakerArr;
using X = AudioChannelSet;
/* Maps VST3 layouts to the equivalent JUCE channels, in VST3 order.
The channel types are taken from the equivalent JUCE AudioChannelSet, and then reordered to
match the VST3 speaker positions.
*/
const LayoutPair layoutTable[]
{
{ kEmpty, {} },
{ kMono, { X::centre } },
{ kStereo, { X::left, X::right } },
{ k30Cine, { X::left, X::right, X::centre } },
{ k30Music, { X::left, X::right, X::surround } },
{ k40Cine, { X::left, X::right, X::centre, X::surround } },
{ k50, { X::left, X::right, X::centre, X::leftSurround, X::rightSurround } },
{ k51, { X::left, X::right, X::centre, X::LFE, X::leftSurround, X::rightSurround } },
{ k60Cine, { X::left, X::right, X::centre, X::leftSurround, X::rightSurround, X::centreSurround } },
{ k61Cine, { X::left, X::right, X::centre, X::LFE, X::leftSurround, X::rightSurround, X::centreSurround } },
{ k60Music, { X::left, X::right, X::leftSurround, X::rightSurround, X::leftSurroundSide, X::rightSurroundSide } },
{ k61Music, { X::left, X::right, X::LFE, X::leftSurround, X::rightSurround, X::leftSurroundSide, X::rightSurroundSide } },
{ k70Music, { X::left, X::right, X::centre, X::leftSurroundRear, X::rightSurroundRear, X::leftSurroundSide, X::rightSurroundSide } },
{ k70Cine, { X::left, X::right, X::centre, X::leftSurround, X::rightSurround, X::leftCentre, X::rightCentre } },
{ k71Music, { X::left, X::right, X::centre, X::LFE, X::leftSurroundRear, X::rightSurroundRear, X::leftSurroundSide, X::rightSurroundSide } },
{ k71Cine, { X::left, X::right, X::centre, X::LFE, X::leftSurround, X::rightSurround, X::leftCentre, X::rightCentre } },
{ k40Music, { X::left, X::right, X::leftSurround, X::rightSurround } },
{ k51_4, { X::left, X::right, X::centre, X::LFE, X::leftSurround, X::rightSurround, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight } },
{ k50_4, { X::left, X::right, X::centre, X::leftSurround, X::rightSurround, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight } },
{ k71_2, { X::left, X::right, X::centre, X::LFE, X::leftSurroundRear, X::rightSurroundRear, X::leftSurroundSide, X::rightSurroundSide, X::topSideLeft, X::topSideRight } },
{ k70_2, { X::left, X::right, X::centre, X::leftSurroundRear, X::rightSurroundRear, X::leftSurroundSide, X::rightSurroundSide, X::topSideLeft, X::topSideRight } },
{ k71_4, { X::left, X::right, X::centre, X::LFE, X::leftSurroundRear, X::rightSurroundRear, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight } },
{ k70_4, { X::left, X::right, X::centre, X::leftSurroundRear, X::rightSurroundRear, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight } },
{ 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 } },
// 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'.
{ 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 } },
{ 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 } },
};
}
inline bool isLayoutTableValid()
{
for (const auto& item : detail::layoutTable)
if ((size_t) countNumberOfBits (item.arrangement) != item.channelOrder.size())
return false;
std::set<Steinberg::Vst::SpeakerArrangement> arrangements;
for (const auto& item : detail::layoutTable)
arrangements.insert (item.arrangement);
if (arrangements.size() != (size_t) numElementsInArray (detail::layoutTable))
return false; // There's a duplicate speaker arrangement
return std::all_of (std::begin (detail::layoutTable), std::end (detail::layoutTable), [] (const auto& item)
{
return std::set<AudioChannelSet::ChannelType> (item.channelOrder).size() == item.channelOrder.size();
});
}
static Array<AudioChannelSet::ChannelType> getSpeakerOrder (Steinberg::Vst::SpeakerArrangement arr)
{
using namespace Steinberg::Vst;
using namespace Steinberg::Vst::SpeakerArr;
jassert (isLayoutTableValid());
// Check if this is a layout with a hard-coded conversion
const auto arrangementMatches = [arr] (const auto& layoutPair) { return layoutPair.arrangement == arr; };
const auto iter = std::find_if (std::begin (detail::layoutTable), std::end (detail::layoutTable), arrangementMatches);
if (iter != std::end (detail::layoutTable))
return iter->channelOrder;
// There's no hard-coded conversion, so assume that the channels are in the same orders in both layouts.
const auto channels = getChannelCount (arr);
Array<AudioChannelSet::ChannelType> result;
result.ensureStorageAllocated (channels);
for (auto i = 0; i < channels; ++i)
result.add (getChannelType (arr, getSpeaker (arr, i)));
return result;
}
static Steinberg::Vst::SpeakerArrangement getVst3SpeakerArrangement (const AudioChannelSet& channels) noexcept
{
using namespace Steinberg::Vst::SpeakerArr;
if (channels == AudioChannelSet::disabled()) return kEmpty;
if (channels == AudioChannelSet::mono()) return kMono;
if (channels == AudioChannelSet::stereo()) return kStereo;
if (channels == AudioChannelSet::createLCR()) return k30Cine;
if (channels == AudioChannelSet::createLRS()) return k30Music;
if (channels == AudioChannelSet::createLCRS()) return k40Cine;
if (channels == AudioChannelSet::create5point0()) return k50;
if (channels == AudioChannelSet::create5point1()) return k51;
if (channels == AudioChannelSet::create6point0()) return k60Cine;
if (channels == AudioChannelSet::create6point1()) return k61Cine;
if (channels == AudioChannelSet::create6point0Music()) return k60Music;
if (channels == AudioChannelSet::create6point1Music()) return k61Music;
if (channels == AudioChannelSet::create7point0()) return k70Music;
if (channels == AudioChannelSet::create7point0SDDS()) return k70Cine;
if (channels == AudioChannelSet::create7point1()) return k71CineSideFill;
if (channels == AudioChannelSet::create7point1SDDS()) return k71Cine;
if (channels == AudioChannelSet::ambisonic()) return kAmbi1stOrderACN;
if (channels == AudioChannelSet::quadraphonic()) return k40Music;
if (channels == AudioChannelSet::create5point1point4()) return k51_4;
if (channels == AudioChannelSet::create7point0point2()) return k71_2 & ~(Steinberg::Vst::kSpeakerLfe);
if (channels == AudioChannelSet::create7point1point2()) return k71_2;
if (channels == AudioChannelSet::create7point0point4()) return k71_4 & ~(Steinberg::Vst::kSpeakerLfe);
if (channels == AudioChannelSet::create7point1point4()) return k71_4;
if (channels == AudioChannelSet::create7point1point6()) return k71_6;
if (channels == AudioChannelSet::create9point1point6()) return k91_6;
if (channels == AudioChannelSet::ambisonic (0)) return (1ull << 20);
if (channels == AudioChannelSet::ambisonic (1)) return (1ull << 20) | (1ull << 21) | (1ull << 22) | (1ull << 23);
#if VST_VERSION >= 0x030608
if (channels == AudioChannelSet::ambisonic (2)) return kAmbi2cdOrderACN;
if (channels == AudioChannelSet::ambisonic (3)) return kAmbi3rdOrderACN;
#endif
jassert (isLayoutTableValid());
const auto channelSetMatches = [&channels] (const auto& layoutPair)
{
return AudioChannelSet::channelSetWithChannels (layoutPair.channelOrder) == channels;
};
const auto iter = std::find_if (std::begin (detail::layoutTable), std::end (detail::layoutTable), channelSetMatches);
if (iter != std::end (detail::layoutTable))
return iter->arrangement;
Steinberg::Vst::SpeakerArrangement result = 0;
@@ -394,53 +469,61 @@ static AudioChannelSet getChannelSetForSpeakerArrangement (Steinberg::Vst::Speak
{
using namespace Steinberg::Vst::SpeakerArr;
switch (arr)
const auto result = AudioChannelSet::channelSetWithChannels (getSpeakerOrder (arr));
// VST3 <-> JUCE layout conversion error: report this bug to the JUCE forum
jassert (result.size() == getChannelCount (arr));
return result;
}
//==============================================================================
struct ChannelMapping
{
explicit ChannelMapping (const AudioChannelSet& layout)
: indices (makeChannelIndices (layout)),
active (true)
{
case kEmpty: return AudioChannelSet::disabled();
case kMono: return AudioChannelSet::mono();
case kStereo: return AudioChannelSet::stereo();
case k30Cine: return AudioChannelSet::createLCR();
case k30Music: return AudioChannelSet::createLRS();
case k40Cine: return AudioChannelSet::createLCRS();
case k50: return AudioChannelSet::create5point0();
case k51: return AudioChannelSet::create5point1();
case k60Cine: return AudioChannelSet::create6point0();
case k61Cine: return AudioChannelSet::create6point1();
case k60Music: return AudioChannelSet::create6point0Music();
case k61Music: return AudioChannelSet::create6point1Music();
case k70Music: return AudioChannelSet::create7point0();
case k70Cine: return AudioChannelSet::create7point0SDDS();
case k71CineSideFill: return AudioChannelSet::create7point1();
case k71Cine: return AudioChannelSet::create7point1SDDS();
case k40Music: return AudioChannelSet::quadraphonic();
case k71_2: return AudioChannelSet::create7point1point2();
case k71_2 & ~(Steinberg::Vst::kSpeakerLfe): return AudioChannelSet::create7point0point2();
case k71_4: return AudioChannelSet::create7point1point4();
case k71_4 & ~(Steinberg::Vst::kSpeakerLfe): return AudioChannelSet::create7point0point4();
case k71_6: return AudioChannelSet::create7point1point6();
case (1 << 20): return AudioChannelSet::ambisonic (0);
case kAmbi1stOrderACN: return AudioChannelSet::ambisonic (1);
#if VST_VERSION >= 0x030608
case kAmbi2cdOrderACN: return AudioChannelSet::ambisonic (2);
case kAmbi3rdOrderACN: return AudioChannelSet::ambisonic (3);
#endif
}
AudioChannelSet result;
explicit ChannelMapping (const AudioProcessor::Bus& juceBus)
: indices (makeChannelIndices (juceBus.getLastEnabledLayout())),
active (juceBus.isEnabled())
{
}
BigInteger vstChannels (static_cast<int64> (arr));
for (auto bit = vstChannels.findNextSetBit (0); bit != -1; bit = vstChannels.findNextSetBit (bit + 1))
int getJuceChannelForVst3Channel (int vst3Channel) const { return indices[(size_t) vst3Channel]; }
size_t size() const { return indices.size(); }
void setActive (bool activeIn) { active = activeIn; }
bool isActive() const { return active; }
private:
/* Builds a table that provides the index of the corresponding JUCE channel, given a VST3 channel.
Depending on the mapping, the VST3 arrangement and JUCE arrangement may not contain channels
that map 1:1 via getChannelType. For example, the VST3 7.1 layout contains
'kSpeakerLs' which maps to the 'leftSurround' channel type, but the JUCE 7.1 layout does not
contain this channel type. As a result, we need to try to map the channels sensibly, even
if there's not a 'direct' mapping.
*/
static std::vector<int> makeChannelIndices (const AudioChannelSet& juceArrangement)
{
AudioChannelSet::ChannelType channelType = getChannelType (arr, 1ull << static_cast<uint64> (bit));
if (channelType != AudioChannelSet::unknown)
result.addChannel (channelType);
const auto order = getSpeakerOrder (getVst3SpeakerArrangement (juceArrangement));
std::vector<int> result;
for (const auto& type : order)
result.push_back (juceArrangement.getChannelIndexForType (type));
return result;
}
// VST3 <-> JUCE layout conversion error: report this bug to the JUCE forum
jassert (result.size() == vstChannels.countNumberOfSetBits());
std::vector<int> indices;
bool active = true;
};
return result;
}
//==============================================================================
template <class ObjectType>


Loading…
Cancel
Save