Browse Source

VST3 Host: Properly map between VST3 and JUCE layouts

pull/22/head
reuk 3 years ago
parent
commit
bfa4f93a43
No known key found for this signature in database GPG Key ID: FCB43929F012EE5C
2 changed files with 106 additions and 114 deletions
  1. +86
    -103
      modules/juce_audio_processors/format_types/juce_VST3Common.h
  2. +20
    -11
      modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp

+ 86
- 103
modules/juce_audio_processors/format_types/juce_VST3Common.h View File

@@ -395,6 +395,10 @@ namespace detail
{ 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 } },
};
#if JUCE_DEBUG
static std::once_flag layoutTableCheckedFlag;
#endif
}
inline bool isLayoutTableValid()
@@ -422,7 +426,9 @@ static Array<AudioChannelSet::ChannelType> getSpeakerOrder (Steinberg::Vst::Spea
using namespace Steinberg::Vst;
using namespace Steinberg::Vst::SpeakerArr;
jassert (isLayoutTableValid());
#if JUCE_DEBUG
std::call_once (detail::layoutTableCheckedFlag, [] { jassert (isLayoutTableValid()); });
#endif
// Check if this is a layout with a hard-coded conversion
const auto arrangementMatches = [arr] (const auto& layoutPair) { return layoutPair.arrangement == arr; };
@@ -446,7 +452,9 @@ static Steinberg::Vst::SpeakerArrangement getVst3SpeakerArrangement (const Audio
{
using namespace Steinberg::Vst::SpeakerArr;
jassert (isLayoutTableValid());
#if JUCE_DEBUG
std::call_once (detail::layoutTableCheckedFlag, [] { jassert (isLayoutTableValid()); });
#endif
const auto channelSetMatches = [&channels] (const auto& layoutPair)
{
@@ -525,6 +533,82 @@ private:
};
//==============================================================================
/*
Remaps a JUCE buffer to an equivalent VST3 layout.
An instance of this class handles mappings for both float and double buffers, but in a single
direction (input or output).
*/
class HostBufferMapper
{
public:
/* Builds a cached map of juce <-> vst3 channel mappings. */
void prepare (std::vector<ChannelMapping> arrangements)
{
mappings = std::move (arrangements);
floatBusMap .resize (mappings.size());
doubleBusMap.resize (mappings.size());
busBuffers .resize (mappings.size());
}
/* Applies the mapping to an AudioBuffer using JUCE channel layout. */
template <typename FloatType>
Steinberg::Vst::AudioBusBuffers* getVst3LayoutForJuceBuffer (AudioBuffer<FloatType>& source)
{
int channelIndexOffset = 0;
for (size_t i = 0; i < mappings.size(); ++i)
{
const auto& mapping = mappings[i];
associateBufferTo (busBuffers[i], get (detail::Tag<FloatType>{})[i], source, mapping, channelIndexOffset);
channelIndexOffset += mapping.isActive() ? (int) mapping.size() : 0;
}
return busBuffers.data();
}
private:
template <typename FloatType>
using Bus = std::vector<FloatType*>;
template <typename FloatType>
using BusMap = std::vector<Bus<FloatType>>;
static void assignRawPointer (Steinberg::Vst::AudioBusBuffers& vstBuffers, float** raw) { vstBuffers.channelBuffers32 = raw; }
static void assignRawPointer (Steinberg::Vst::AudioBusBuffers& vstBuffers, double** raw) { vstBuffers.channelBuffers64 = raw; }
template <typename FloatType>
void associateBufferTo (Steinberg::Vst::AudioBusBuffers& vstBuffers,
Bus<FloatType>& bus,
AudioBuffer<FloatType>& buffer,
const ChannelMapping& busMap,
int channelStartOffset) const
{
bus.clear();
for (size_t i = 0; i < busMap.size(); ++i)
{
bus.push_back (busMap.isActive() ? buffer.getWritePointer (channelStartOffset + busMap.getJuceChannelForVst3Channel ((int) i))
: nullptr);
}
assignRawPointer (vstBuffers, bus.data());
vstBuffers.numChannels = (Steinberg::int32) busMap.size();
vstBuffers.silenceFlags = busMap.isActive() ? 0 : std::numeric_limits<Steinberg::uint64>::max();
}
auto& get (detail::Tag<float>) { return floatBusMap; }
auto& get (detail::Tag<double>) { return doubleBusMap; }
BusMap<float> floatBusMap;
BusMap<double> doubleBusMap;
std::vector<Steinberg::Vst::AudioBusBuffers> busBuffers;
std::vector<ChannelMapping> mappings;
};
//==============================================================================
template <class ObjectType>
class VSTComSmartPtr
@@ -1049,107 +1133,6 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiEventList)
};
//==============================================================================
template <typename FloatType>
struct VST3BufferExchange
{
using Bus = Array<FloatType*>;
using BusMap = Array<Bus>;
static void assignRawPointer (Steinberg::Vst::AudioBusBuffers& vstBuffers, float** raw) { vstBuffers.channelBuffers32 = raw; }
static void assignRawPointer (Steinberg::Vst::AudioBusBuffers& vstBuffers, double** raw) { vstBuffers.channelBuffers64 = raw; }
/** Assigns a series of AudioBuffer's channels to an AudioBusBuffers'
@warning For speed, does not check the channel count and offsets according to the AudioBuffer
*/
static void associateBufferTo (Steinberg::Vst::AudioBusBuffers& vstBuffers,
Bus& bus,
AudioBuffer<FloatType>& buffer,
int numChannels, int channelStartOffset,
int sampleOffset = 0)
{
const int channelEnd = numChannels + channelStartOffset;
jassert (channelEnd >= 0 && channelEnd <= buffer.getNumChannels());
bus.clearQuick();
for (int i = channelStartOffset; i < channelEnd; ++i)
bus.add (buffer.getWritePointer (i, sampleOffset));
assignRawPointer (vstBuffers, (numChannels > 0 ? bus.getRawDataPointer() : nullptr));
vstBuffers.numChannels = numChannels;
vstBuffers.silenceFlags = 0;
}
static void mapArrangementToBuses (int& channelIndexOffset, int index,
Array<Steinberg::Vst::AudioBusBuffers>& result,
BusMap& busMapToUse, const AudioChannelSet& arrangement,
AudioBuffer<FloatType>& source)
{
const int numChansForBus = arrangement.size();
if (index >= result.size())
result.add (Steinberg::Vst::AudioBusBuffers());
if (index >= busMapToUse.size())
busMapToUse.add (Bus());
associateBufferTo (result.getReference (index),
busMapToUse.getReference (index),
source, numChansForBus, channelIndexOffset);
channelIndexOffset += numChansForBus;
}
static void mapBufferToBuses (Array<Steinberg::Vst::AudioBusBuffers>& result, BusMap& busMapToUse,
const Array<AudioChannelSet>& arrangements,
AudioBuffer<FloatType>& source)
{
int channelIndexOffset = 0;
for (int i = 0; i < arrangements.size(); ++i)
mapArrangementToBuses (channelIndexOffset, i, result, busMapToUse,
arrangements.getUnchecked (i), source);
}
static void mapBufferToBuses (Array<Steinberg::Vst::AudioBusBuffers>& result,
Steinberg::Vst::IAudioProcessor& processor,
BusMap& busMapToUse, bool isInput, int numBuses,
AudioBuffer<FloatType>& source)
{
int channelIndexOffset = 0;
for (int i = 0; i < numBuses; ++i)
mapArrangementToBuses (channelIndexOffset, i,
result, busMapToUse,
getArrangementForBus (&processor, isInput, i),
source);
}
};
template <typename FloatType>
struct VST3FloatAndDoubleBusMapCompositeHelper {};
struct VST3FloatAndDoubleBusMapComposite
{
VST3BufferExchange<float>::BusMap floatVersion;
VST3BufferExchange<double>::BusMap doubleVersion;
template <typename FloatType>
inline typename VST3BufferExchange<FloatType>::BusMap& get() { return VST3FloatAndDoubleBusMapCompositeHelper<FloatType>::get (*this); }
};
template <> struct VST3FloatAndDoubleBusMapCompositeHelper<float>
{
static VST3BufferExchange<float>::BusMap& get (VST3FloatAndDoubleBusMapComposite& impl) { return impl.floatVersion; }
};
template <> struct VST3FloatAndDoubleBusMapCompositeHelper<double>
{
static VST3BufferExchange<double>::BusMap& get (VST3FloatAndDoubleBusMapComposite& impl) { return impl.doubleVersion; }
};
//==============================================================================
class FloatCache
{


+ 20
- 11
modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp View File

@@ -2435,7 +2435,9 @@ public:
warnOnFailure (holder->component->activateBus (Vst::kAudio, Vst::kOutput, i, getBus (false, i)->isEnabled() ? 1 : 0));
setLatencySamples (jmax (0, (int) processor->getLatencySamples()));
cachedBusLayouts = getBusesLayout();
inputBusMap .prepare (createChannelMappings (true));
outputBusMap.prepare (createChannelMappings (false));
setStateForAllMidiBuses (true);
@@ -2670,7 +2672,7 @@ public:
bool result = syncBusLayouts (layouts);
// didn't succeed? Make sure it's back in it's original state
// didn't succeed? Make sure it's back in its original state
if (! result)
syncBusLayouts (getBusesLayout());
@@ -3017,9 +3019,7 @@ private:
even if there aren't enough channels to process,
as very poorly specified by the Steinberg SDK
*/
VST3FloatAndDoubleBusMapComposite inputBusMap, outputBusMap;
Array<Vst::AudioBusBuffers> inputBuses, outputBuses;
AudioProcessor::BusesLayout cachedBusLayouts;
HostBufferMapper inputBusMap, outputBusMap;
StringArray programNames;
Vst::ParamID programParameterID = (Vst::ParamID) -1;
@@ -3214,6 +3214,17 @@ private:
setStateForAllBusesOfType (holder->component, newState, false, false); // Activate/deactivate MIDI outputs
}
std::vector<ChannelMapping> createChannelMappings (bool isInput) const
{
std::vector<ChannelMapping> result;
result.reserve ((size_t) getBusCount (isInput));
for (auto i = 0; i < getBusCount (isInput); ++i)
result.emplace_back (*getBus (isInput, i));
return result;
}
void setupIO()
{
setStateForAllMidiBuses (true);
@@ -3226,7 +3237,8 @@ private:
warnOnFailure (processor->setupProcessing (setup));
cachedBusLayouts = getBusesLayout();
inputBusMap .prepare (createChannelMappings (true));
outputBusMap.prepare (createChannelMappings (false));
setRateAndBufferSizeDetails (setup.sampleRate, (int) setup.maxSamplesPerBlock);
}
@@ -3317,11 +3329,8 @@ private:
template <typename FloatType>
void associateWith (Vst::ProcessData& destination, AudioBuffer<FloatType>& buffer)
{
VST3BufferExchange<FloatType>::mapBufferToBuses (inputBuses, inputBusMap.get<FloatType>(), cachedBusLayouts.inputBuses, buffer);
VST3BufferExchange<FloatType>::mapBufferToBuses (outputBuses, outputBusMap.get<FloatType>(), cachedBusLayouts.outputBuses, buffer);
destination.inputs = inputBuses.getRawDataPointer();
destination.outputs = outputBuses.getRawDataPointer();
destination.inputs = inputBusMap .getVst3LayoutForJuceBuffer (buffer);
destination.outputs = outputBusMap.getVst3LayoutForJuceBuffer (buffer);
}
void associateWith (Vst::ProcessData& destination, MidiBuffer& midiBuffer)


Loading…
Cancel
Save