|  |  | @@ -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<float> remapper; | 
		
	
		
			
			|  |  |  | remapper.prepare (2, blockSize * 2); | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | const std::vector<ChannelMapping> emptyBuses; | 
		
	
		
			
			|  |  |  | const std::vector<ChannelMapping> 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<float> remapper; | 
		
	
		
			
			|  |  |  | remapper.prepare (3, blockSize * 2); | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | const std::vector<ChannelMapping> noBus; | 
		
	
		
			
			|  |  |  | const std::vector<ChannelMapping> oneBus { ChannelMapping { AudioChannelSet::mono() } }; | 
		
	
		
			
			|  |  |  | const std::vector<ChannelMapping> 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<float> remapper; | 
		
	
		
			
			|  |  |  | remapper.prepare (3, blockSize * 2); | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | const std::vector<ChannelMapping> monoBus { ChannelMapping { AudioChannelSet::mono() } }; | 
		
	
		
			
			|  |  |  | const std::vector<ChannelMapping> 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<float> 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<float> 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<float> 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<float> 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<double> 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<ChannelMapping> 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<std::vector<float>> buffers; | 
		
	
		
			
			|  |  |  | int numSamples = 0; | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | static bool allMatch (const AudioBuffer<float>& 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<Steinberg::Vst::AudioBusBuffers> buffers; | 
		
	
		
			
			|  |  |  | std::vector<std::vector<float*>> pointerStorage; | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | MultiBusBuffers withBus (TestBuffers& storage, int numChannels) && | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | MultiBusBuffers result { std::move (buffers), std::move (pointerStorage) }; | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | std::vector<float*> 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 |