// Copyright (c) 2012-2017 VideoStitch SAS // Copyright (c) 2018 stitchEm #include "gpu/testing.hpp" #include #include "libvideostitch/audioPipeDef.hpp" #include "libvideostitch/parse.hpp" #include "audio/audioPipeFactory.hpp" #include "audio/audioPipeline.hpp" #include "audio/sampleDelay.hpp" #include "audio/gain.hpp" #include #include namespace VideoStitch { namespace Testing { using namespace Core; using namespace Audio; static const std::string DEFAULT_PANO_FILENAME = "data/4i_default_pano_definition.json"; PanoDefinition *getDefaultPano() { auto parser = Ptv::Parser::create(); if (!parser->parse(DEFAULT_PANO_FILENAME)) { std::cerr << parser->getErrorMessage() << std::endl; ENSURE(false); } return PanoDefinition::create(parser->getRoot()); } // For testing purpose we serialize the audio pipe and recreate it afterwards to check serialization is passing fine AudioPipeDefinition *reCreateAudiopipeAfterSerialization(AudioPipeDefinition *audioPipeDefBeforeSerialization) { // Serialize the audio pipe definition std::unique_ptr pipeSerialized(audioPipeDefBeforeSerialization->serialize()); std::ofstream os; std::string testData = getDataFolder(); std::string pipeFileName = testData + "/titi.json"; os.open(pipeFileName, std::ios::out); pipeSerialized->printJson(os); os.close(); // Re open it and check everything is as expected Potential parser(Ptv::Parser::create()); ENSURE(parser->parse(pipeFileName)); std::unique_ptr ptv(parser->getRoot().clone()); std::unique_ptr audioPipeConfig(ptv->clone()); return AudioPipeDefinition::create(*audioPipeConfig); } void testCreateAudioPipeDef() { std::cout << "TEST creation of audio pipe definition" << std::endl; AudioPipeDefinition *audioPipeDef(AudioPipeDefinition::createDefault()); ENSURE_EQ(audioPipeDef->numAudioInputs(), 0, "check default number of inputs"); // Add a new input with 2 sources to the audio pipe InputParam firstInputParam("my_first_input", 0, STEREO); std::unique_ptr newInput(AudioInputDefinition::create(firstInputParam)); ENSURE_EQ((size_t)2, newInput->numSources(), "check new number of sources"); newInput->setIsMaster(true); ENSURE(!newInput->setLayout("wrongLayout").ok(), "Test setting a wrong layout"); newInput->setLayout(getStringFromChannelLayout(STEREO)); newInput->getSource(0)->setReaderId(0); newInput->getSource(0)->setChannel(0); newInput->getSource(1)->setReaderId(1); newInput->getSource(1)->setChannel(1); audioPipeDef->addInput(newInput.release()); // Add a second input with 2 sources to the audio pipe InputParam secondInputParam("my_second_input", 1, STEREO); std::unique_ptr secondInput(AudioInputDefinition::create(secondInputParam)); secondInput->getSource(0)->setReaderId(0); secondInput->getSource(0)->setChannel(-1); audioPipeDef->addInput(secondInput.release()); ENSURE_EQ(2, audioPipeDef->numAudioInputs(), "check new inputs have been created"); ENSURE_EQ(audioPipeDef->getInput(0)->getName(), firstInputParam.name, "check default reader id"); ENSURE_EQ(audioPipeDef->getInput(0)->getSource(0)->getReaderId(), (readerid_t)0, "check default reader id"); ENSURE_EQ(audioPipeDef->getInput(0)->getSource(0)->getChannel(), (size_t)0, "check default channel"); ENSURE_EQ(audioPipeDef->getInput(0)->getSource(1)->getReaderId(), (readerid_t)1, "check default reader id"); ENSURE_EQ(audioPipeDef->getInput(0)->getSource(1)->getChannel(), (size_t)1, "check default channel"); ENSURE_EQ(true, audioPipeDef->getInput(0)->getIsMaster(), "check is master"); ENSURE_EQ(std::string("stereo"), audioPipeDef->getInput(0)->getLayout(), "check layout"); // Add a a delay processor std::cout << "TEST add delay processor" << std::endl; double delay_s = 3.0; audioPipeDef->addDelayProcessor(secondInputParam.name, delay_s); audioPipeDef->addDelayProcessor(firstInputParam.name, 1.1); ENSURE_EQ(delay_s, audioPipeDef->getDelay(secondInputParam.name).value(), "Check creation of delay processor"); ENSURE_EQ(1.1, audioPipeDef->getDelay(firstInputParam.name).value(), "Check add delay processor for an other input"); // Check invalid input name at the creation of the delay processor ENSURE(!audioPipeDef->addDelayProcessor("invalidName", delay_s).ok()); // Add a gain processor std::cout << "TEST add delay processor" << std::endl; bool reversePol = true, mute = false; audioPipeDef->addGainProcessor(firstInputParam.name, 2., reversePol, mute); ENSURE_EQ(2., audioPipeDef->getGain(firstInputParam.name).value(), "Check gain control value"); ENSURE_EQ(mute, audioPipeDef->getMute(firstInputParam.name).value(), "Check mute control value"); ENSURE_EQ(reversePol, audioPipeDef->getReversePolarity(firstInputParam.name).value(), "Check mute control value"); audioPipeDef->addGainProcessor(firstInputParam.name, -2., reversePol, mute); audioPipeDef->setGain(firstInputParam.name, -20.); audioPipeDef->setReversePolarity(firstInputParam.name, !reversePol); audioPipeDef->setMute(firstInputParam.name, !mute); ENSURE_EQ(-20., audioPipeDef->getGain(firstInputParam.name).value(), "Check gain control value"); ENSURE_EQ(!mute, audioPipeDef->getMute(firstInputParam.name).value(), "Check mute control value"); ENSURE_EQ(!reversePol, audioPipeDef->getReversePolarity(firstInputParam.name).value(), "Check mute control value"); PanoDefinition *panoDef = getDefaultPano(); Potential audioPipe = AudioPipeFactory::create(*audioPipeDef, *panoDef); ENSURE(audioPipe.ok(), "Check creation of the audio pipe"); // Check removing a processor std::cout << "TEST remove delay processor" << std::endl; ENSURE(audioPipeDef->removeProcessor(kDelayProcessorName).ok(), "Check remove delay processor"); ENSURE(!audioPipeDef->getProcessor(kDelayProcessorName).ok(), "Check remove delay processor"); std::cout << "TEST remove gain processor" << std::endl; ENSURE(audioPipeDef->removeProcessor(kGainProcessorName).ok(), "Check remove gain processor"); ENSURE(!audioPipeDef->getProcessor(kGainProcessorName).ok(), "Check remove gain processor"); delete audioPipeDef; delete panoDef; return; } void testReadAudioPipeDef() { std::string inputFile = "data/audio_pipe.json"; Potential parser(Ptv::Parser::create()); ENSURE(parser->parse(inputFile)); std::unique_ptr ptv(parser->getRoot().clone()); std::unique_ptr audioPipeConfig(ptv->has("audio_pipe")->clone()); // Test the create from a ptv config std::unique_ptr audioPipeDefOrigin(AudioPipeDefinition::create(*audioPipeConfig)); // Test the clone method std::unique_ptr audioPipeDef(audioPipeDefOrigin->clone()); ENSURE_EQ(audioPipeDef->getSamplingRate(), 44100, "default sampling rate"); ENSURE_EQ(audioPipeDef->getBlockSize(), 512, "default internal block size"); ENSURE_EQ(audioPipeDef->numAudioInputs(), 1, "number of audio inputs"); std::vector expectedNumSources = {2}; for (audioreaderid_t i = 0; i < audioPipeDef->numAudioInputs(); i++) { AudioInputDefinition *inputDef = audioPipeDef->getInput(i); std::stringstream ss; ss << "input " << i << ": number of sources" << inputDef->numSources() << "!=" << expectedNumSources[i]; ENSURE_EQ(inputDef->numSources(), expectedNumSources[i], ss.str().c_str()); std::vector expectedChannel = {0, 1}; if (i == 0) { ENSURE_EQ(true, inputDef->getIsMaster(), "Check master parameter"); ENSURE_EQ(std::string("stereo"), inputDef->getLayout(), "Check layout parameter"); } for (size_t j = 0; j < inputDef->numSources(); j++) { AudioSourceDefinition *sourceDef = inputDef->getSource(j); ENSURE_EQ(sourceDef->getReaderId(), (readerid_t)0, "Reader id of the source"); ENSURE_EQ(sourceDef->getChannel(), (size_t)expectedChannel[j], "Reader id of the source"); } } ENSURE_EQ(static_cast(3), audioPipeDef->numProcessors(), "number of audio processor"); // First audio processor should be audio delay std::string expectedProcName = Core::kDelayProcessorName; std::string procName = audioPipeDef->getProcessor(0)->getName(); ENSURE_EQ(expectedProcName, procName, std::string("name of the audio processor " + expectedProcName).c_str()); // Second audio processor should be audio gain expectedProcName = Core::kGainProcessorName; procName = audioPipeDef->getProcessor(1)->getName(); ENSURE_EQ(expectedProcName, procName, std::string("name of the audio processor " + expectedProcName).c_str()); // Third audio processor should be audio ambRotator expectedProcName = Core::kAmbRotateProcessorName; procName = audioPipeDef->getProcessor(2)->getName(); ENSURE_EQ(expectedProcName, procName, std::string("name of the audio processor " + expectedProcName).c_str()); std::unique_ptr pipeSerialized(audioPipeDef->serialize()); std::ofstream os; std::string testData = getDataFolder(); os.open(testData + "/toto.json", std::ios::out); pipeSerialized->printJson(os); os.close(); } void testUpdateAudioProcessorParams() { std::cout << "Test Update audio processors params" << std::endl; std::string inputFile = "data/audio_pipe.json"; Potential parser(Ptv::Parser::create()); ENSURE(parser->parse(inputFile)); std::unique_ptr ptv(parser->getRoot().clone()); std::unique_ptr audioPipeConfig(ptv->has("audio_pipe")->clone()); std::unique_ptr AudioPipeDefBeforeSerialization(AudioPipeDefinition::create(*audioPipeConfig)); std::unique_ptr audioPipeDef( reCreateAudiopipeAfterSerialization(AudioPipeDefBeforeSerialization.get())); std::unique_ptr pano(getDefaultPano()); Potential audioPipeline(AudioPipeFactory::create(*audioPipeDef, *pano)); // Change delay of the audio pipe definition audioPipeDef->setDelay("bbb", 3.0); PotentialValue delay = audioPipeDef->getDelay("bbb"); ENSURE(delay.ok(), "check getDelay message"); ENSURE_EQ(3.0, delay.value(), "Check delay value"); // Change gain parameters of the audio pipe definition audioPipeDef->setGain("bbb", -10.0); audioPipeDef->setMute("bbb", false); audioPipeDef->setReversePolarity("bbb", true); PotentialValue gain = audioPipeDef->getGain("bbb"); PotentialValue mute = audioPipeDef->getMute("bbb"); PotentialValue reversePolarity = audioPipeDef->getReversePolarity("bbb"); ENSURE(gain.ok(), "check getGain message"); ENSURE_EQ(-10., gain.value(), "Check gain value"); ENSURE(mute.ok(), "check getMute message"); ENSURE_EQ(false, mute.value(), "Check mute value"); ENSURE(reversePolarity.ok(), "check getReversePolarity message"); ENSURE_EQ(true, reversePolarity.value(), "Check reverse polarity value"); // update audio processors param of the pipeline audioPipeline->applyProcessorParam(*audioPipeDef); PotentialValue audioObject = audioPipeline->getAudioProcessor("bbb", "delay"); double delay_s = static_cast(audioObject.value())->getDelaySeconds(); ENSURE_EQ(3.0, delay_s, "Check delay of the pipeline"); audioObject = audioPipeline->getAudioProcessor("bbb", "gain"); double gaindB = static_cast(audioObject.value())->getGainDB(); bool mutePipe = static_cast(audioObject.value())->getMute(); bool rpPipe = static_cast(audioObject.value())->getReversePolarity(); ENSURE_EQ(-10., gaindB, "Check gain of the pipeline"); ENSURE_EQ(false, mutePipe, "Check gain of the pipeline"); ENSURE_EQ(true, rpPipe, "Check gain of the pipeline"); // Try to set/get the delay of a non existing input ENSURE(!audioPipeDef->setDelay("xxx", 3.0).ok(), "check that setting delay of non existing input fails"); ENSURE(!audioPipeDef->getDelay("xxx").ok(), "check that getting delay of non existing input fails"); // Try to set/get the gain of a non existing input ENSURE(!audioPipeDef->setGain("xxx", 3.0).ok(), "check that setting gain of non existing input fails"); ENSURE(!audioPipeDef->getGain("xxx").ok(), "check that getting gain of non existing input fails"); // Try to set/get the mute of a non existing input ENSURE(!audioPipeDef->setMute("xxx", true).ok(), "check that setting mute of non existing input fails"); ENSURE(!audioPipeDef->getMute("xxx").ok(), "check that getting mute of non existing input fails"); // Try to set/get the reverse polarity of a non existing input ENSURE(!audioPipeDef->setReversePolarity("xxx", true).ok(), "check that setting reverse polarity of non existing input fails"); ENSURE(!audioPipeDef->getReversePolarity("xxx").ok(), "check that getting reverse polarity of non existing input fails"); // Check ambrotator param PotentialValue ambRotator = audioPipeline->getAudioProcessor("bbb", Core::kAmbRotateProcessorName); Vector3 offsetRotation = static_cast(ambRotator.value())->getRotationOffset(); ENSURE_EQ(3.14, offsetRotation(0), "Check yaw offset"); ENSURE_EQ(1.57, offsetRotation(1), "Check pitch offset"); ENSURE_EQ(0., offsetRotation(2), "Check roll offset"); } void testCreateAudioPipeDefFromPano() { std::unique_ptr panoDef(getDefaultPano()); std::unique_ptr audioPipeDefBeforeSerialization( AudioPipeDefinition::createAudioPipeFromPanoInputs(panoDef.get())); ENSURE(audioPipeDefBeforeSerialization->readersAreConsistent(panoDef.get()), "Audio pipe and pano def should be consistent"); std::unique_ptr audioPipeDef( reCreateAudiopipeAfterSerialization(audioPipeDefBeforeSerialization.get())); // VSA-7376 ENSURE(panoDef.get()->numAudioInputs() > 0); InputDefinition *lastInput = panoDef.get()->popInput(panoDef.get()->numInputs() - 1); ENSURE(!audioPipeDef->readersAreConsistent(panoDef.get()), "Audio pipe and pano def should not be consistent"); panoDef.get()->insertInput(lastInput, -1); ENSURE(audioPipeDef->readersAreConsistent(panoDef.get()), "Audio pipe and pano def should be consistent"); ENSURE_EQ(panoDef->numAudioInputs(), audioPipeDef->numAudioInputs(), "Check number of audio inputs of the pano and the audio pipe"); ENSURE_EQ(3, audioPipeDef->numAudioInputs(), "Check number of audio inputs of the audio pipe"); ENSURE_EQ((size_t)audioPipeDef->numAudioInputs(), audioPipeDef->numAudioMixes(), "Check default number of audio mixes of the audio pipe"); // Check channel layout of the audio inputs ENSURE_EQ(std::string(getStringFromChannelLayout(STEREO)), audioPipeDef->getInput(0)->getLayout(), "Check channel layout of the first input"); ENSURE_EQ(std::string(getStringFromChannelLayout(MONO)), audioPipeDef->getInput(1)->getLayout(), "Check channel layout of the second input"); ENSURE_EQ(std::string(getStringFromChannelLayout(MONO)), audioPipeDef->getInput(2)->getLayout(), "Check channel layout of the third input"); // Change pano definition Ptv::Value *readerConfig(panoDef->getAudioInput(0).getReaderConfig().clone()); readerConfig->get("audio_channels")->asInt() = readerConfig->has("audio_channels")->asInt() - 1; panoDef->getAudioInput(0).setReaderConfig(readerConfig); ENSURE(!audioPipeDef->readersAreConsistent(panoDef.get()), "Audio pipe and pano def should not be consistent"); } void checkAudioInputLayout(ChannelLayout layoutToTest) { std::cout << "TEST make audio " << getStringFromChannelLayout(layoutToTest) << " input " << std::endl; std::unique_ptr audioPipeDef(AudioPipeDefinition::createDefault()); AudioInputDefinition *inDef(AudioInputDefinition::create(InputParam("blabla", 0, layoutToTest))); inDef->setLayout(getStringFromChannelLayout(layoutToTest)); audioPipeDef->addInput(inDef); audioPipeDef->addGainProcessor(inDef->getName(), 0, false, false); std::string testData = getDataFolder(); audioPipeDef->setDebugFolder(testData); std::unique_ptr pano(getDefaultPano()); Potential audioPipeline(AudioPipeFactory::create(*audioPipeDef, *pano)); AudioBlock blk(getAChannelLayoutFromNbChannels(getNbChannelsFromChannelLayout(layoutToTest)), 0); audioBlockReaderMap_t samplesPerRd; audioBlockGroupMap_t samplesPerGr; blk.resize(3); double x = 0; for (auto &track : blk) { track[0] = 0. + x; track[1] = 0.1 + x; track[2] = 0.2 + x; x = x + 0.01; } // Copy the block as it will be moved by the audio pipeline AudioBlock copyBlk = blk.clone(); samplesPerRd[0] = std::move(blk); samplesPerGr[0] = std::move(samplesPerRd); audioPipeline->process(samplesPerGr); // Call desctructor of the audiopipeline to force the writing of debug files delete audioPipeline.release(); std::string wavFile(audioPipeDef->getDebugFolder() + "/" + inDef->getName() + "input.wav"); WavReader wvRd(wavFile); ENSURE_EQ((int)inDef->numSources(), wvRd.getChannels(), "Check number of channels produced by the audio pipe"); AudioBlock out; wvRd.step(out); ENSURE_EQ(copyBlk.getLayout(), out.getLayout(), "Check layout"); ENSURE_EQ(copyBlk.numSamples(), out.numSamples(), "Check numSamples"); for (const auto &outT : out) { for (size_t i = 0; i < outT.size(); i++) { ENSURE_APPROX_EQ(copyBlk[outT.channel()][i], outT[i], 1e-4, "Check sample value"); } } std::cout << "TEST make audio " << getStringFromChannelLayout(layoutToTest) << " input: PASSED " << std::endl; } void testAudioInputs() { std::vector layoutToTest({MONO, STEREO, AMBISONICS_WXYZ, _5POINT1}); for (const auto &layout : layoutToTest) { checkAudioInputLayout(layout); } } } // namespace Testing } // namespace VideoStitch int main(int argc, char **argv) { VideoStitch::Testing::testReadAudioPipeDef(); VideoStitch::Testing::testCreateAudioPipeDef(); VideoStitch::Testing::testCreateAudioPipeDefFromPano(); VideoStitch::Testing::testAudioInputs(); }