From 58da3cf1250bbf73e4e9e5746b20e40dcc0da56a Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Mon, 23 Mar 2015 22:46:21 +0100 Subject: [PATCH] [DEV] work on the stream of AEC --- river/Manager.cpp | 2 +- river/io/NodeAEC.cpp | 86 ++++++++------- river/io/NodeAEC.h | 2 + river/io/NodeAirTAudio.cpp | 12 ++- test/testAEC.h | 203 ++++++++++++++++++++++++++++++++++++ test/testPlaybackCallback.h | 2 +- 6 files changed, 256 insertions(+), 51 deletions(-) diff --git a/river/Manager.cpp b/river/Manager.cpp index 0a12b08..ae55ec0 100644 --- a/river/Manager.cpp +++ b/river/Manager.cpp @@ -19,7 +19,7 @@ static std11::mutex g_mutex; static std::vector > g_listOfAllManager; std11::shared_ptr river::Manager::create(const std::string& _applicationUniqueId) { - std11::unique_lock lock(g_mutex); + std11::unique_lock lock(g_mutex); for (size_t iii=0; iii tmp = g_listOfAllManager[iii].lock(); if (tmp == nullptr) { diff --git a/river/io/NodeAEC.cpp b/river/io/NodeAEC.cpp index f6cf829..5a8b2c5 100644 --- a/river/io/NodeAEC.cpp +++ b/river/io/NodeAEC.cpp @@ -30,7 +30,7 @@ std11::shared_ptr river::io::NodeAEC::createInput(float _freq, } std::string streamName = tmppp->getStringValue("map-on", "error"); - + m_nbChunk = m_config->getNumberValue("nb-chunk", 1024); // check if it is an Output: std::string type = tmppp->getStringValue("io", "error"); if ( type != "input" @@ -197,8 +197,8 @@ void river::io::NodeAEC::onDataReceivedFeedBack(const void* _data, } void river::io::NodeAEC::process() { - if ( m_bufferMicrophone.getSize() <= 256 - || m_bufferFeedBack.getSize() <= 256) { + if ( m_bufferMicrophone.getSize() <= m_nbChunk + || m_bufferFeedBack.getSize() <= m_nbChunk) { return; } std11::chrono::system_clock::time_point MicTime = m_bufferMicrophone.getReadTimeStamp(); @@ -227,10 +227,8 @@ void river::io::NodeAEC::process() { } } // check if enought time after synchronisation ... - if (m_bufferMicrophone.getSize() <= 256) { - return; - } - if (m_bufferFeedBack.getSize() <= 256) { + if ( m_bufferMicrophone.getSize() <= m_nbChunk + || m_bufferFeedBack.getSize() <= m_nbChunk) { return; } @@ -243,22 +241,20 @@ void river::io::NodeAEC::process() { } std::vector dataMic; std::vector dataFB; - dataMic.resize(256*sizeof(int16_t)*2, 0); - dataFB.resize(256*sizeof(int16_t), 0); + dataMic.resize(m_nbChunk*sizeof(int16_t)*2, 0); + dataFB.resize(m_nbChunk*sizeof(int16_t), 0); while (true) { MicTime = m_bufferMicrophone.getReadTimeStamp(); fbTime = m_bufferFeedBack.getReadTimeStamp(); RIVER_INFO(" process 256 samples ... micTime=" << MicTime << " fbTime=" << fbTime << " delta = " << (MicTime-fbTime).count()); - m_bufferMicrophone.read(&dataMic[0], 256); - m_bufferFeedBack.read(&dataFB[0], 256); - RIVER_SAVE_FILE_MACRO(int16_t, "REC_Microphone_sync.raw", &dataMic[0], 256*2); - RIVER_SAVE_FILE_MACRO(int16_t, "REC_FeedBack_sync.raw", &dataFB[0], 256); + m_bufferMicrophone.read(&dataMic[0], m_nbChunk); + m_bufferFeedBack.read(&dataFB[0], m_nbChunk); + RIVER_SAVE_FILE_MACRO(int16_t, "REC_Microphone_sync.raw", &dataMic[0], m_nbChunk*2); + RIVER_SAVE_FILE_MACRO(int16_t, "REC_FeedBack_sync.raw", &dataFB[0], m_nbChunk); // if threaded : send event / otherwise, process ... - //processAEC(&dataMic[0], &dataFB[0], 256, _time); - if (m_bufferMicrophone.getSize() <= 256) { - return; - } - if (m_bufferFeedBack.getSize() <= 256) { + processAEC(&dataMic[0], &dataFB[0], m_nbChunk, MicTime); + if ( m_bufferMicrophone.getSize() <= m_nbChunk + || m_bufferFeedBack.getSize() <= m_nbChunk) { return; } } @@ -274,27 +270,15 @@ void river::io::NodeAEC::generateDot(etk::FSNode& _node) { _node << " subgraph clusterNode_" << m_uid << " {\n"; _node << " color=blue;\n"; _node << " label=\"[" << m_uid << "] IO::Node : " << m_name << "\";\n"; - - _node << " node [shape=box];\n"; - // TODO : Create a structure ... - _node << " NODE_" << m_uid << "_HW_AEC [ label=\"AEC\" ];\n"; - _node << " subgraph clusterNode_" << m_uid << "_process {\n"; - _node << " label=\"Drain::Process\";\n"; - _node << " node [shape=ellipse];\n"; - _node << " node_ALGO_" << m_uid << "_in [ label=\"format=" << etk::to_string(m_process.getInputConfig().getFormat()) - << "\\n freq=" << m_process.getInputConfig().getFrequency() - << "\\n channelMap=" << etk::to_string(m_process.getInputConfig().getMap()) << "\" ];\n"; - _node << " node_ALGO_" << m_uid << "_out [ label=\"format=" << etk::to_string(m_process.getOutputConfig().getFormat()) - << "\\n freq=" << m_process.getOutputConfig().getFrequency() - << "\\n channelMap=" << etk::to_string(m_process.getOutputConfig().getMap()) << "\" ];\n"; - - _node << " }\n"; + _node << " NODE_" << m_uid << "_HW_AEC [ label=\"AEC\\n channelMap=" << etk::to_string(getInterfaceFormat().getMap()) << "\" ];\n"; + std::string nameIn; + std::string nameOut; + m_process.generateDot(_node, 3, m_uid, nameIn, nameOut, false); _node << " node [shape=square];\n"; _node << " NODE_" << m_uid << "_demuxer [ label=\"DEMUXER\\n format=" << etk::to_string(m_process.getOutputConfig().getFormat()) << "\" ];\n"; // Link all nodes : - _node << " NODE_" << m_uid << "_HW_AEC -> node_ALGO_" << m_uid << "_in;\n"; - _node << " node_ALGO_" << m_uid << "_in -> node_ALGO_" << m_uid << "_out;\n"; - _node << " node_ALGO_" << m_uid << "_out -> NODE_" << m_uid << "_demuxer;\n"; + _node << " NODE_" << m_uid << "_HW_AEC -> " << nameIn << ";\n"; + _node << " " << nameOut << " -> NODE_" << m_uid << "_demuxer;\n"; _node << " }\n"; if (m_interfaceMicrophone != nullptr) { _node << " " << m_interfaceMicrophone->getDotNodeName() << " -> NODE_" << m_uid << "_HW_AEC;\n"; @@ -302,15 +286,29 @@ void river::io::NodeAEC::generateDot(etk::FSNode& _node) { if (m_interfaceFeedBack != nullptr) { _node << " " << m_interfaceFeedBack->getDotNodeName() << " -> NODE_" << m_uid << "_HW_AEC;\n"; } + _node << " \n"; - for (size_t iii=0; iiigetMode() == modeInterface_input) { - m_list[iii]->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_demuxer"); - } else if (m_list[iii]->getMode() == modeInterface_output) { - m_list[iii]->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_muxer"); - } else if (m_list[iii]->getMode() == modeInterface_feedback) { - m_list[iii]->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_demuxer"); + for (size_t iii=0; iii< m_listAvaillable.size(); ++iii) { + if (m_listAvaillable[iii].expired() == true) { + continue; + } + std11::shared_ptr element = m_listAvaillable[iii].lock(); + if (element == nullptr) { + continue; + } + bool isLink = false; + for (size_t jjj=0; jjjgetMode() == modeInterface_input) { + element->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_demuxer", isLink); + } else if (element->getMode() == modeInterface_output) { + element->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_muxer", isLink); + } else if (element->getMode() == modeInterface_feedback) { + element->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_demuxer", isLink); } else { } diff --git a/river/io/NodeAEC.h b/river/io/NodeAEC.h index 0c51f87..b09565c 100644 --- a/river/io/NodeAEC.h +++ b/river/io/NodeAEC.h @@ -89,6 +89,8 @@ namespace river { void processAEC(void* _dataMic, void* _dataFB, uint32_t _nbChunk, const std11::chrono::system_clock::time_point& _time); public: virtual void generateDot(etk::FSNode& _node); + private: + int32_t m_nbChunk; }; } } diff --git a/river/io/NodeAirTAudio.cpp b/river/io/NodeAirTAudio.cpp index 50ce89a..c51c743 100644 --- a/river/io/NodeAirTAudio.cpp +++ b/river/io/NodeAirTAudio.cpp @@ -190,13 +190,15 @@ river::io::NodeAirTAudio::NodeAirTAudio(const std::string& _name, const std11::s airtaudio::StreamParameters params; params.deviceId = deviceId; params.deviceName = streamName; - // TODO : Remove limit of 2 channels ... + params.nChannels = hardwareFormat.getMap().size(); if (m_isInput == true) { - m_info.inputChannels = 2; - params.nChannels = 2; + if (m_info.inputChannels < params.nChannels) { + RIVER_CRITICAL("Can not open hardware device with more channel (" << params.nChannels << ") that is autorized by hardware (" << m_info.inputChannels << ")."); + } } else { - m_info.outputChannels = 2; - params.nChannels = 2; + if (m_info.outputChannels < params.nChannels) { + RIVER_CRITICAL("Can not open hardware device with more channel (" << params.nChannels << ") that is autorized by hardware (" << m_info.inputChannels << ")."); + } } airtaudio::StreamOptions option; etk::from_string(option.mode, tmpObject->getStringValue("timestamp-mode", "soft")); diff --git a/test/testAEC.h b/test/testAEC.h index d945d44..5c66fc1 100644 --- a/test/testAEC.h +++ b/test/testAEC.h @@ -12,6 +12,209 @@ namespace river_test_aec { + class Linker { + private: + std11::shared_ptr m_manager; + std11::shared_ptr m_interfaceOut; + std11::shared_ptr m_interfaceIn; + drain::CircularBuffer m_buffer; + public: + Linker(std11::shared_ptr _manager, const std::string& _input, const std::string& _output) : + m_manager(_manager) { + //Set stereo output: + std::vector channelMap; + if (false) { //"speaker" == _output) { + channelMap.push_back(audio::channel_frontCenter); + } else { + channelMap.push_back(audio::channel_frontLeft); + channelMap.push_back(audio::channel_frontRight); + } + m_buffer.setCapacity(std11::chrono::milliseconds(2000), sizeof(int16_t)*channelMap.size(), 48000); + + m_interfaceOut = m_manager->createOutput(48000, + channelMap, + audio::format_int16, + _output); + if(m_interfaceOut == nullptr) { + APPL_ERROR("nullptr interface"); + return; + } + // set callback mode ... + m_interfaceOut->setOutputCallback(std11::bind(&Linker::onDataNeeded, + this, + std11::placeholders::_1, + std11::placeholders::_2, + std11::placeholders::_3, + std11::placeholders::_4, + std11::placeholders::_5, + std11::placeholders::_6)); + m_interfaceOut->addVolumeGroup("FLOW"); + if ("speaker" == _output) { + m_interfaceOut->setParameter("volume", "FLOW", "0dB"); + } + + m_interfaceIn = m_manager->createInput(48000, + channelMap, + audio::format_int16, + _input); + if(m_interfaceIn == nullptr) { + APPL_ERROR("nullptr interface"); + return; + } + // set callback mode ... + m_interfaceIn->setInputCallback(std11::bind(&Linker::onDataReceived, + this, + std11::placeholders::_1, + std11::placeholders::_2, + std11::placeholders::_3, + std11::placeholders::_4, + std11::placeholders::_5, + std11::placeholders::_6)); + + } + void onDataNeeded(void* _data, + const std11::chrono::system_clock::time_point& _time, + size_t _nbChunk, + enum audio::format _format, + uint32_t _frequency, + const std::vector& _map) { + if (_format != audio::format_int16) { + APPL_ERROR("call wrong type ... (need int16_t)"); + } + m_buffer.read(_data, _nbChunk); + } + void onDataReceived(const void* _data, + const std11::chrono::system_clock::time_point& _time, + size_t _nbChunk, + enum audio::format _format, + uint32_t _frequency, + const std::vector& _map) { + if (_format != audio::format_int16) { + APPL_ERROR("call wrong type ... (need int16_t)"); + } + m_buffer.write(_data, _nbChunk); + } + void start() { + if(m_interfaceIn == nullptr) { + APPL_ERROR("nullptr interface"); + return; + } + if(m_interfaceOut == nullptr) { + APPL_ERROR("nullptr interface"); + return; + } + m_interfaceOut->start(); + m_interfaceIn->start(); + } + void stop() { + if(m_interfaceIn == nullptr) { + APPL_ERROR("nullptr interface"); + return; + } + if(m_interfaceOut == nullptr) { + APPL_ERROR("nullptr interface"); + return; + } + m_manager->generateDotAll("activeProcess.dot"); + m_interfaceOut->stop(); + m_interfaceIn->stop(); + } + }; + + static const std::string configurationRiver = + "{\n" + " speaker:{\n" + " io:'output',\n" + " map-on:{\n" + " interface:'auto',\n" + " name:'hw:0,0',\n" + " timestamp-mode:'trigered',\n" + " },\n" + " frequency:0,\n" + " channel-map:['front-left', 'front-right'],\n" + " type:'auto',\n" + " nb-chunk:1024,\n" + " },\n" + " microphone:{\n" + " io:'input',\n" + " map-on:{\n" + " interface:'auto',\n" + " name:'hw:0,0',\n" + " timestamp-mode:'trigered',\n" + " },\n" + " frequency:0,\n" + " channel-map:['front-left', 'front-right'],\n" + " type:'auto',\n" + " nb-chunk:1024\n" + " },\n" + " speaker-test:{\n" + " io:'output',\n" + " map-on:{\n" + " interface:'alsa',\n" + " name:'hw:2,0',\n" + " timestamp-mode:'trigered',\n" + " },\n" + //" group:'groupSynchro',\n" + " frequency:0,\n" + " channel-map:['front-left', 'front-right'],\n" + " type:'auto',\n" + " nb-chunk:1024\n" + " },\n" + " microphone-test:{\n" + " io:'input',\n" + " map-on:{\n" + " interface:'alsa',\n" + " name:'hw:2,0',\n" + " timestamp-mode:'trigered',\n" + " },\n" + //" group:'groupSynchro',\n" + " frequency:0,\n" + " channel-map:['front-center'],\n" + " type:'auto',\n" + " nb-chunk:1024\n" + " },\n" + " # virtual Nodes :\n" + " microphone-clean:{\n" + " io:'aec',\n" + " map-on-microphone:{\n" + " io:'input',\n" + " map-on:'microphone-test'\n" + " },\n" + " map-on-feedback:{\n" + " io:'feedback',\n" + " map-on:'speaker-test',\n" + " },\n" + " frequency:48000,\n" + " channel-map:[\n" + " 'front-left', 'front-right'\n" + //" 'front-center'\n" + " ],\n" + " nb-chunk:1024,\n" + " type:'int16',\n" + " algo:'river-remover',\n" + " algo-mode:'cutter',\n" + " feedback-delay:10000,\n" + " mux-demux-type:'int16'\n" + " }\n" + "}\n"; + + TEST(TestUser, testAECManually) { + river::initString(configurationRiver); + std11::shared_ptr manager; + manager = river::Manager::create("testApplication"); + std11::shared_ptr processLink1 = std11::make_shared(manager, "microphone-clean", "speaker"); + std11::shared_ptr processLink2 = std11::make_shared(manager, "microphone", "speaker-test"); + processLink1->start(); + processLink2->start(); + sleep(30); + processLink1->stop(); + processLink2->stop(); + + processLink1.reset(); + processLink2.reset(); + manager.reset(); + river::unInit(); + } }; #undef __class__ diff --git a/test/testPlaybackCallback.h b/test/testPlaybackCallback.h index d4f3569..354dcb5 100644 --- a/test/testPlaybackCallback.h +++ b/test/testPlaybackCallback.h @@ -82,7 +82,7 @@ namespace river_test_playback_callback { " io:'output',\n" " map-on:{\n" " interface:'auto',\n" - " name:'default',\n" + " name:'hw:2,0',\n" " },\n" " frequency:0,\n" " channel-map:['front-left', 'front-right'],\n"