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 } }, { 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 } }, { 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() inline bool isLayoutTableValid()
@@ -422,7 +426,9 @@ static Array<AudioChannelSet::ChannelType> getSpeakerOrder (Steinberg::Vst::Spea
using namespace Steinberg::Vst; using namespace Steinberg::Vst;
using namespace Steinberg::Vst::SpeakerArr; 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 // Check if this is a layout with a hard-coded conversion
const auto arrangementMatches = [arr] (const auto& layoutPair) { return layoutPair.arrangement == arr; }; 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; 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) 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> template <class ObjectType>
class VSTComSmartPtr class VSTComSmartPtr
@@ -1049,107 +1133,6 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiEventList) 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 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)); warnOnFailure (holder->component->activateBus (Vst::kAudio, Vst::kOutput, i, getBus (false, i)->isEnabled() ? 1 : 0));
setLatencySamples (jmax (0, (int) processor->getLatencySamples())); setLatencySamples (jmax (0, (int) processor->getLatencySamples()));
cachedBusLayouts = getBusesLayout();
inputBusMap .prepare (createChannelMappings (true));
outputBusMap.prepare (createChannelMappings (false));
setStateForAllMidiBuses (true); setStateForAllMidiBuses (true);
@@ -2670,7 +2672,7 @@ public:
bool result = syncBusLayouts (layouts); 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) if (! result)
syncBusLayouts (getBusesLayout()); syncBusLayouts (getBusesLayout());
@@ -3017,9 +3019,7 @@ private:
even if there aren't enough channels to process, even if there aren't enough channels to process,
as very poorly specified by the Steinberg SDK as very poorly specified by the Steinberg SDK
*/ */
VST3FloatAndDoubleBusMapComposite inputBusMap, outputBusMap;
Array<Vst::AudioBusBuffers> inputBuses, outputBuses;
AudioProcessor::BusesLayout cachedBusLayouts;
HostBufferMapper inputBusMap, outputBusMap;
StringArray programNames; StringArray programNames;
Vst::ParamID programParameterID = (Vst::ParamID) -1; Vst::ParamID programParameterID = (Vst::ParamID) -1;
@@ -3214,6 +3214,17 @@ private:
setStateForAllBusesOfType (holder->component, newState, false, false); // Activate/deactivate MIDI outputs 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() void setupIO()
{ {
setStateForAllMidiBuses (true); setStateForAllMidiBuses (true);
@@ -3226,7 +3237,8 @@ private:
warnOnFailure (processor->setupProcessing (setup)); warnOnFailure (processor->setupProcessing (setup));
cachedBusLayouts = getBusesLayout();
inputBusMap .prepare (createChannelMappings (true));
outputBusMap.prepare (createChannelMappings (false));
setRateAndBufferSizeDetails (setup.sampleRate, (int) setup.maxSamplesPerBlock); setRateAndBufferSizeDetails (setup.sampleRate, (int) setup.maxSamplesPerBlock);
} }
@@ -3317,11 +3329,8 @@ private:
template <typename FloatType> template <typename FloatType>
void associateWith (Vst::ProcessData& destination, AudioBuffer<FloatType>& buffer) 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) void associateWith (Vst::ProcessData& destination, MidiBuffer& midiBuffer)


Loading…
Cancel
Save