From fe3d42025b193b62d2072b48deeb5e17c4eec28a Mon Sep 17 00:00:00 2001 From: reuk Date: Thu, 17 Mar 2022 20:55:17 +0000 Subject: [PATCH] VST3: Add channel mapping tests --- .../format_types/juce_VST3Headers.h | 2 + .../juce_VST3PluginFormat_test.cpp | 543 ++++++++++++++++++ .../juce_audio_processors.cpp | 4 + .../unit_tests/juce_UnitTestCategories.h | 1 + 4 files changed, 550 insertions(+) create mode 100644 modules/juce_audio_processors/format_types/juce_VST3PluginFormat_test.cpp diff --git a/modules/juce_audio_processors/format_types/juce_VST3Headers.h b/modules/juce_audio_processors/format_types/juce_VST3Headers.h index 6bf806f972..05a2da81e8 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3Headers.h +++ b/modules/juce_audio_processors/format_types/juce_VST3Headers.h @@ -23,6 +23,8 @@ ============================================================================== */ +#pragma once + #if JUCE_BSD && ! JUCE_CUSTOM_VST3_SDK #error To build JUCE VST3 plug-ins on BSD you must use an external BSD-compatible VST3 SDK with JUCE_CUSTOM_VST3_SDK=1 #endif diff --git a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat_test.cpp b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat_test.cpp new file mode 100644 index 0000000000..b6ef7ff7ab --- /dev/null +++ b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat_test.cpp @@ -0,0 +1,543 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 6 End-User License + Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). + + End User License Agreement: www.juce.com/juce-6-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#include "juce_VST3Headers.h" +#include "juce_VST3Common.h" + +namespace juce +{ + +class VST3PluginFormatTests : public UnitTest +{ +public: + VST3PluginFormatTests() + : UnitTest ("VST3 Hosting", UnitTestCategories::audioProcessors) + { + } + + void runTest() override + { + beginTest ("ChannelMapping for a stereo bus performs no remapping"); + { + ChannelMapping map (AudioChannelSet::stereo()); + expect (map.size() == 2); + expect (map.isActive() == true); + + expect (map.getJuceChannelForVst3Channel (0) == 0); // L -> left + expect (map.getJuceChannelForVst3Channel (1) == 1); // R -> right + } + + beginTest ("ChannelMapping for a 9.1.6 bus remaps the channels appropriately"); + { + ChannelMapping map (AudioChannelSet::create9point1point6()); + expect (map.size() == 16); + expect (map.isActive() == true); + + // VST3 order is: + // L + // R + // C + // Lfe + // Ls + // Rs + // Lc + // Rc + // Sl + // Sr + // Tfl + // Tfr + // Trl + // Trr + // Tsl + // Tsr + // JUCE order is: + // Left + // Right + // Centre + // LFE + // Left Surround Side + // Right Surround Side + // Top Front Left + // Top Front Right + // Top Rear Left + // Top Rear Right + // Left Surround Rear + // Right Surround Rear + // Wide Left + // Wide Right + // Top Side Left + // Top Side Right + + expect (map.getJuceChannelForVst3Channel (0) == 12); // L -> wideLeft + expect (map.getJuceChannelForVst3Channel (1) == 13); // R -> wideRight + expect (map.getJuceChannelForVst3Channel (2) == 2); // C -> centre + expect (map.getJuceChannelForVst3Channel (3) == 3); // Lfe -> LFE + expect (map.getJuceChannelForVst3Channel (4) == 10); // Ls -> leftSurroundRear + expect (map.getJuceChannelForVst3Channel (5) == 11); // Rs -> rightSurroundRear + expect (map.getJuceChannelForVst3Channel (6) == 0); // Lc -> left + expect (map.getJuceChannelForVst3Channel (7) == 1); // Rc -> right + expect (map.getJuceChannelForVst3Channel (8) == 4); // Sl -> leftSurroundSide + expect (map.getJuceChannelForVst3Channel (9) == 5); // Sl -> leftSurroundSide + expect (map.getJuceChannelForVst3Channel (10) == 6); // Tfl -> topFrontLeft + expect (map.getJuceChannelForVst3Channel (11) == 7); // Tfr -> topFrontRight + expect (map.getJuceChannelForVst3Channel (12) == 8); // Trl -> topRearLeft + expect (map.getJuceChannelForVst3Channel (13) == 9); // Trr -> topRearRight + expect (map.getJuceChannelForVst3Channel (14) == 14); // Tsl -> topSideLeft + expect (map.getJuceChannelForVst3Channel (15) == 15); // Tsr -> topSideRight + } + + const auto blockSize = 128; + + beginTest ("If the host provides more buses than the plugin knows about, the remapped buffer is silent and uses only internal channels"); + { + ClientBufferMapperData remapper; + remapper.prepare (2, blockSize * 2); + + const std::vector emptyBuses; + const std::vector stereoBus { ChannelMapping { AudioChannelSet::stereo() } }; + + TestBuffers testBuffers { blockSize }; + + auto ins = MultiBusBuffers{}.withBus (testBuffers, 2).withBus (testBuffers, 1); + auto outs = MultiBusBuffers{}.withBus (testBuffers, 2).withBus (testBuffers, 1); + auto data = makeProcessData (blockSize, ins, outs); + + for (const auto& config : { Config { stereoBus, stereoBus }, Config { emptyBuses, stereoBus }, Config { stereoBus, emptyBuses } }) + { + testBuffers.init(); + const auto remapped = remapper.getMappedBuffer (data, config.ins, config.outs); + expect (remapped.getNumChannels() == config.getNumChannels()); + expect (remapped.getNumSamples() == blockSize); + + for (auto i = 0; i < remapped.getNumChannels(); ++i) + expect (allMatch (remapped, i, 0.0f)); + + expect (! testBuffers.isClear (0)); + expect (! testBuffers.isClear (1)); + expect (! testBuffers.isClear (2)); + expect (testBuffers.isClear (3)); + expect (testBuffers.isClear (4)); + expect (testBuffers.isClear (5)); + } + } + + beginTest ("If the host provides fewer buses than the plugin knows about, the remapped buffer is silent and uses only internal channels"); + { + ClientBufferMapperData remapper; + remapper.prepare (3, blockSize * 2); + + const std::vector noBus; + const std::vector oneBus { ChannelMapping { AudioChannelSet::mono() } }; + const std::vector twoBuses { ChannelMapping { AudioChannelSet::mono() }, + ChannelMapping { AudioChannelSet::stereo() } }; + + TestBuffers testBuffers { blockSize }; + + auto ins = MultiBusBuffers{}.withBus (testBuffers, 1); + auto outs = MultiBusBuffers{}.withBus (testBuffers, 1); + auto data = makeProcessData (blockSize, ins, outs); + + for (const auto& config : { Config { noBus, twoBuses }, + Config { twoBuses, noBus }, + Config { oneBus, twoBuses }, + Config { twoBuses, oneBus }, + Config { twoBuses, twoBuses } }) + { + testBuffers.init(); + const auto remapped = remapper.getMappedBuffer (data, config.ins, config.outs); + expect (remapped.getNumChannels() == config.getNumChannels()); + expect (remapped.getNumSamples() == blockSize); + + for (auto i = 0; i < remapped.getNumChannels(); ++i) + expect (allMatch (remapped, i, 0.0f)); + + expect (! testBuffers.isClear (0)); + expect (testBuffers.isClear (1)); + } + } + + beginTest ("If the host channel count on any bus is incorrect, the remapped buffer is silent and uses only internal channels"); + { + ClientBufferMapperData remapper; + remapper.prepare (3, blockSize * 2); + + const std::vector monoBus { ChannelMapping { AudioChannelSet::mono() } }; + const std::vector stereoBus { ChannelMapping { AudioChannelSet::stereo() } }; + + TestBuffers testBuffers { blockSize }; + + auto ins = MultiBusBuffers{}.withBus (testBuffers, 1); + auto outs = MultiBusBuffers{}.withBus (testBuffers, 2); + auto data = makeProcessData (blockSize, ins, outs); + + for (const auto& config : { Config { stereoBus, monoBus }, + Config { stereoBus, stereoBus }, + Config { monoBus, monoBus } }) + { + testBuffers.init(); + const auto remapped = remapper.getMappedBuffer (data, config.ins, config.outs); + expect (remapped.getNumChannels() == config.getNumChannels()); + expect (remapped.getNumSamples() == blockSize); + + for (auto i = 0; i < remapped.getNumChannels(); ++i) + expect (allMatch (remapped, i, 0.0f)); + + expect (! testBuffers.isClear (0)); + expect (testBuffers.isClear (1)); + expect (testBuffers.isClear (2)); + } + } + + beginTest ("A layout with more output channels than input channels leaves unused inputs untouched"); + { + ClientBufferMapperData remapper; + remapper.prepare (20, blockSize * 2); + + const Config config { { ChannelMapping { AudioChannelSet::mono() }, + ChannelMapping { AudioChannelSet::create5point1() } }, + { ChannelMapping { AudioChannelSet::stereo() }, + ChannelMapping { AudioChannelSet::create7point1() } } }; + + TestBuffers testBuffers { blockSize }; + + auto ins = MultiBusBuffers{}.withBus (testBuffers, 1).withBus (testBuffers, 6); + auto outs = MultiBusBuffers{}.withBus (testBuffers, 2).withBus (testBuffers, 8); + + auto data = makeProcessData (blockSize, ins, outs); + + testBuffers.init(); + const auto remapped = remapper.getMappedBuffer (data, config.ins, config.outs); + + expect (remapped.getNumChannels() == 10); + + // Data from the input channels is copied to the correct channels of the remapped buffer + expect (allMatch (remapped, 0, 1.0f)); + expect (allMatch (remapped, 1, 2.0f)); + expect (allMatch (remapped, 2, 3.0f)); + expect (allMatch (remapped, 3, 4.0f)); + expect (allMatch (remapped, 4, 5.0f)); + expect (allMatch (remapped, 5, 6.0f)); + expect (allMatch (remapped, 6, 7.0f)); + // These channels are output-only, so they keep whatever data was previously on that output channel + expect (allMatch (remapped, 7, 17.0f)); + expect (allMatch (remapped, 8, 14.0f)); + expect (allMatch (remapped, 9, 15.0f)); + + // Channel pointers from the VST3 buffer are used + expect (remapped.getReadPointer (0) == testBuffers.get (7)); + expect (remapped.getReadPointer (1) == testBuffers.get (8)); + expect (remapped.getReadPointer (2) == testBuffers.get (9)); + expect (remapped.getReadPointer (3) == testBuffers.get (10)); + expect (remapped.getReadPointer (4) == testBuffers.get (11)); + expect (remapped.getReadPointer (5) == testBuffers.get (12)); + expect (remapped.getReadPointer (6) == testBuffers.get (15)); // JUCE surround side -> VST3 surround side + expect (remapped.getReadPointer (7) == testBuffers.get (16)); // JUCE surround side -> VST3 surround side + expect (remapped.getReadPointer (8) == testBuffers.get (13)); // JUCE surround rear -> VST3 surround rear + expect (remapped.getReadPointer (9) == testBuffers.get (14)); // JUCE surround rear -> VST3 surround rear + } + + beginTest ("A layout with more input channels than output channels uses input channels directly in remapped buffer"); + { + ClientBufferMapperData remapper; + remapper.prepare (15, blockSize * 2); + + const Config config { { ChannelMapping { AudioChannelSet::create7point1point6() }, + ChannelMapping { AudioChannelSet::mono() } }, + { ChannelMapping { AudioChannelSet::createLCRS() }, + ChannelMapping { AudioChannelSet::stereo() } } }; + + TestBuffers testBuffers { blockSize }; + + auto ins = MultiBusBuffers{}.withBus (testBuffers, 14).withBus (testBuffers, 1); + auto outs = MultiBusBuffers{}.withBus (testBuffers, 4) .withBus (testBuffers, 2); + + auto data = makeProcessData (blockSize, ins, outs); + + testBuffers.init(); + const auto remapped = remapper.getMappedBuffer (data, config.ins, config.outs); + + expect (remapped.getNumChannels() == 15); + + // Data from the input channels is copied to the correct channels of the remapped buffer + expect (allMatch (remapped, 0, 1.0f)); + expect (allMatch (remapped, 1, 2.0f)); + expect (allMatch (remapped, 2, 3.0f)); + expect (allMatch (remapped, 3, 4.0f)); + expect (allMatch (remapped, 4, 7.0f)); + expect (allMatch (remapped, 5, 8.0f)); + expect (allMatch (remapped, 6, 9.0f)); + expect (allMatch (remapped, 7, 10.0f)); + expect (allMatch (remapped, 8, 11.0f)); + expect (allMatch (remapped, 9, 12.0f)); + expect (allMatch (remapped, 10, 5.0f)); + expect (allMatch (remapped, 11, 6.0f)); + expect (allMatch (remapped, 12, 13.0f)); + expect (allMatch (remapped, 13, 14.0f)); + expect (allMatch (remapped, 14, 15.0f)); + + // Use output channel pointers for output channels + expect (remapped.getReadPointer (0) == testBuffers.get (15)); + expect (remapped.getReadPointer (1) == testBuffers.get (16)); + expect (remapped.getReadPointer (2) == testBuffers.get (17)); + expect (remapped.getReadPointer (3) == testBuffers.get (18)); + expect (remapped.getReadPointer (4) == testBuffers.get (19)); + expect (remapped.getReadPointer (5) == testBuffers.get (20)); + + // Use input channel pointers for channels with no corresponding output + expect (remapped.getReadPointer (6) == testBuffers.get (8)); + expect (remapped.getReadPointer (7) == testBuffers.get (9)); + expect (remapped.getReadPointer (8) == testBuffers.get (10)); + expect (remapped.getReadPointer (9) == testBuffers.get (11)); + expect (remapped.getReadPointer (10) == testBuffers.get (4)); + expect (remapped.getReadPointer (11) == testBuffers.get (5)); + expect (remapped.getReadPointer (12) == testBuffers.get (12)); + expect (remapped.getReadPointer (13) == testBuffers.get (13)); + expect (remapped.getReadPointer (14) == testBuffers.get (14)); + } + + beginTest ("Inactive buses are ignored"); + { + ClientBufferMapperData remapper; + remapper.prepare (15, blockSize * 2); + + const Config config { { ChannelMapping { AudioChannelSet::create7point1point6() }, + ChannelMapping { AudioChannelSet::mono(), false }, + ChannelMapping { AudioChannelSet::quadraphonic() }, + ChannelMapping { AudioChannelSet::mono(), false } }, + { ChannelMapping { AudioChannelSet::create5point0(), false }, + ChannelMapping { AudioChannelSet::createLCRS() }, + ChannelMapping { AudioChannelSet::stereo() } } }; + + TestBuffers testBuffers { blockSize }; + + // The host doesn't need to provide trailing buses that are inactive + auto ins = MultiBusBuffers{}.withBus (testBuffers, 14).withBus (testBuffers, 1).withBus (testBuffers, 4); + auto outs = MultiBusBuffers{}.withBus (testBuffers, 5) .withBus (testBuffers, 4).withBus (testBuffers, 2); + + auto data = makeProcessData (blockSize, ins, outs); + + testBuffers.init(); + const auto remapped = remapper.getMappedBuffer (data, config.ins, config.outs); + + expect (remapped.getNumChannels() == 18); + + // Data from the input channels is copied to the correct channels of the remapped buffer + expect (allMatch (remapped, 0, 1.0f)); + expect (allMatch (remapped, 1, 2.0f)); + expect (allMatch (remapped, 2, 3.0f)); + expect (allMatch (remapped, 3, 4.0f)); + expect (allMatch (remapped, 4, 7.0f)); + expect (allMatch (remapped, 5, 8.0f)); + expect (allMatch (remapped, 6, 9.0f)); + expect (allMatch (remapped, 7, 10.0f)); + expect (allMatch (remapped, 8, 11.0f)); + expect (allMatch (remapped, 9, 12.0f)); + expect (allMatch (remapped, 10, 5.0f)); + expect (allMatch (remapped, 11, 6.0f)); + expect (allMatch (remapped, 12, 13.0f)); + expect (allMatch (remapped, 13, 14.0f)); + + expect (allMatch (remapped, 14, 16.0f)); + expect (allMatch (remapped, 15, 17.0f)); + expect (allMatch (remapped, 16, 18.0f)); + expect (allMatch (remapped, 17, 19.0f)); + + // Use output channel pointers for output channels + expect (remapped.getReadPointer (0) == testBuffers.get (24)); + expect (remapped.getReadPointer (1) == testBuffers.get (25)); + expect (remapped.getReadPointer (2) == testBuffers.get (26)); + expect (remapped.getReadPointer (3) == testBuffers.get (27)); + expect (remapped.getReadPointer (4) == testBuffers.get (28)); + expect (remapped.getReadPointer (5) == testBuffers.get (29)); + + // Use input channel pointers for channels with no corresponding output + expect (remapped.getReadPointer (6) == testBuffers.get (8)); + expect (remapped.getReadPointer (7) == testBuffers.get (9)); + expect (remapped.getReadPointer (8) == testBuffers.get (10)); + expect (remapped.getReadPointer (9) == testBuffers.get (11)); + expect (remapped.getReadPointer (10) == testBuffers.get (4)); + expect (remapped.getReadPointer (11) == testBuffers.get (5)); + expect (remapped.getReadPointer (12) == testBuffers.get (12)); + expect (remapped.getReadPointer (13) == testBuffers.get (13)); + + expect (remapped.getReadPointer (14) == testBuffers.get (15)); + expect (remapped.getReadPointer (15) == testBuffers.get (16)); + expect (remapped.getReadPointer (16) == testBuffers.get (17)); + expect (remapped.getReadPointer (17) == testBuffers.get (18)); + } + + beginTest ("HostBufferMapper reorders channels correctly"); + { + HostBufferMapper mapper; + + { + mapper.prepare ({ ChannelMapping { AudioChannelSet::stereo() }, + ChannelMapping { AudioChannelSet::create7point1point2() }, + ChannelMapping { AudioChannelSet::create9point1point6(), false }, + ChannelMapping { AudioChannelSet::createLCRS() } }); + AudioBuffer hostBuffer (16, blockSize); + const auto* clientBuffers = mapper.getVst3LayoutForJuceBuffer (hostBuffer); + + expect (clientBuffers[0].numChannels == 2); + expect (clientBuffers[1].numChannels == 10); + // Even though it's disabled, this bus should still have the correct channel count + expect (clientBuffers[2].numChannels == 16); + expect (clientBuffers[3].numChannels == 4); + + expect (clientBuffers[0].channelBuffers32[0] == hostBuffer.getReadPointer (0)); + expect (clientBuffers[0].channelBuffers32[1] == hostBuffer.getReadPointer (1)); + + expect (clientBuffers[1].channelBuffers32[0] == hostBuffer.getReadPointer (2)); + expect (clientBuffers[1].channelBuffers32[1] == hostBuffer.getReadPointer (3)); + expect (clientBuffers[1].channelBuffers32[2] == hostBuffer.getReadPointer (4)); + expect (clientBuffers[1].channelBuffers32[3] == hostBuffer.getReadPointer (5)); + expect (clientBuffers[1].channelBuffers32[4] == hostBuffer.getReadPointer (8)); + expect (clientBuffers[1].channelBuffers32[5] == hostBuffer.getReadPointer (9)); + expect (clientBuffers[1].channelBuffers32[6] == hostBuffer.getReadPointer (6)); + expect (clientBuffers[1].channelBuffers32[7] == hostBuffer.getReadPointer (7)); + expect (clientBuffers[1].channelBuffers32[8] == hostBuffer.getReadPointer (10)); + expect (clientBuffers[1].channelBuffers32[9] == hostBuffer.getReadPointer (11)); + + for (auto i = 0; i < clientBuffers[2].numChannels; ++i) + expect (clientBuffers[2].channelBuffers32[i] == nullptr); + + expect (clientBuffers[3].channelBuffers32[0] == hostBuffer.getReadPointer (12)); + expect (clientBuffers[3].channelBuffers32[1] == hostBuffer.getReadPointer (13)); + expect (clientBuffers[3].channelBuffers32[2] == hostBuffer.getReadPointer (14)); + expect (clientBuffers[3].channelBuffers32[3] == hostBuffer.getReadPointer (15)); + } + + { + mapper.prepare ({ ChannelMapping { AudioChannelSet::mono() }, + ChannelMapping { AudioChannelSet::mono(), false }, + ChannelMapping { AudioChannelSet::mono() }, + ChannelMapping { AudioChannelSet::mono(), false } }); + AudioBuffer hostBuffer (2, blockSize); + const auto* clientBuffers = mapper.getVst3LayoutForJuceBuffer (hostBuffer); + + expect (clientBuffers[0].numChannels == 1); + expect (clientBuffers[1].numChannels == 1); + expect (clientBuffers[2].numChannels == 1); + expect (clientBuffers[3].numChannels == 1); + + expect (clientBuffers[0].channelBuffers64[0] == hostBuffer.getReadPointer (0)); + expect (clientBuffers[1].channelBuffers64[0] == nullptr); + expect (clientBuffers[2].channelBuffers64[0] == hostBuffer.getReadPointer (1)); + expect (clientBuffers[3].channelBuffers64[0] == nullptr); + } + } + } + +private: + //============================================================================== + struct Config + { + std::vector ins, outs; + + int getNumChannels() const { return countUsedChannels (ins, outs); } + }; + + struct TestBuffers + { + explicit TestBuffers (int samples) : numSamples (samples) {} + + void init() + { + auto index = 1; + + for (auto& channel : buffers) + std::fill (channel.begin(), channel.end(), (float) index++); + } + + bool allMatch (int channel, float value) const + { + const auto& buf = buffers[(size_t) channel]; + return std::all_of (buf.begin(), buf.end(), [&] (auto x) { return x == value; }); + } + + bool isClear (int channel) const + { + return allMatch (channel, 0.0f); + } + + float* addChannel() + { + buffers.emplace_back (numSamples); + return buffers.back().data(); + } + + float* get (int channel) { return buffers[(size_t) channel].data(); } + const float* get (int channel) const { return buffers[(size_t) channel].data(); } + + std::vector> buffers; + int numSamples = 0; + }; + + static bool allMatch (const AudioBuffer& buf, int index, float value) + { + const auto* ptr = buf.getReadPointer (index); + return std::all_of (ptr, ptr + buf.getNumSamples(), [&] (auto x) { return x == value; }); + } + + struct MultiBusBuffers + { + std::vector buffers; + std::vector> pointerStorage; + + MultiBusBuffers withBus (TestBuffers& storage, int numChannels) && + { + MultiBusBuffers result { std::move (buffers), std::move (pointerStorage) }; + + std::vector pointers; + + for (auto i = 0; i < numChannels; ++i) + pointers.push_back (storage.addChannel()); + + Steinberg::Vst::AudioBusBuffers buffer; + buffer.numChannels = (Steinberg::int32) pointers.size(); + buffer.channelBuffers32 = pointers.data(); + + result.buffers.push_back (buffer); + result.pointerStorage.push_back (std::move (pointers)); + + return result; + } + }; + + static Steinberg::Vst::ProcessData makeProcessData (int blockSize, MultiBusBuffers& ins, MultiBusBuffers& outs) + { + Steinberg::Vst::ProcessData result; + result.numSamples = blockSize; + result.inputs = ins.buffers.data(); + result.numInputs = (Steinberg::int32) ins.buffers.size(); + result.outputs = outs.buffers.data(); + result.numOutputs = (Steinberg::int32) outs.buffers.size(); + return result; + } +}; + +static VST3PluginFormatTests vst3PluginFormatTests; + +} // namespace juce diff --git a/modules/juce_audio_processors/juce_audio_processors.cpp b/modules/juce_audio_processors/juce_audio_processors.cpp index 49c6e390e2..9d0a143219 100644 --- a/modules/juce_audio_processors/juce_audio_processors.cpp +++ b/modules/juce_audio_processors/juce_audio_processors.cpp @@ -209,3 +209,7 @@ private: #include "utilities/juce_ParameterAttachments.cpp" #include "utilities/juce_AudioProcessorValueTreeState.cpp" #include "utilities/juce_PluginHostType.cpp" + +#if JUCE_UNIT_TESTS + #include "format_types/juce_VST3PluginFormat_test.cpp" +#endif diff --git a/modules/juce_core/unit_tests/juce_UnitTestCategories.h b/modules/juce_core/unit_tests/juce_UnitTestCategories.h index 9f6fa6ca9d..ac4564663c 100644 --- a/modules/juce_core/unit_tests/juce_UnitTestCategories.h +++ b/modules/juce_core/unit_tests/juce_UnitTestCategories.h @@ -28,6 +28,7 @@ namespace UnitTestCategories static const String analytics { "Analytics" }; static const String audio { "Audio" }; static const String audioProcessorParameters { "AudioProcessorParameters" }; + static const String audioProcessors { "AudioProcessors" }; static const String blocks { "Blocks" }; static const String compression { "Compression" }; static const String containers { "Containers" };