[DEV] add portaudio
This commit is contained in:
parent
dfb46560a7
commit
7d6e1e0bf6
@ -94,6 +94,7 @@
|
|||||||
# AEC algo definition
|
# AEC algo definition
|
||||||
algo:"river-remover",
|
algo:"river-remover",
|
||||||
algo-mode:"cutter",
|
algo-mode:"cutter",
|
||||||
|
feedback-delay:10000, # in nanosecond
|
||||||
mux-demux-type:"int16",
|
mux-demux-type:"int16",
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,11 +17,21 @@ def create(target):
|
|||||||
'river/CircularBuffer.cpp',
|
'river/CircularBuffer.cpp',
|
||||||
'river/io/Node.cpp',
|
'river/io/Node.cpp',
|
||||||
'river/io/NodeAirTAudio.cpp',
|
'river/io/NodeAirTAudio.cpp',
|
||||||
|
'river/io/NodePortAudio.cpp',
|
||||||
'river/io/NodeAEC.cpp',
|
'river/io/NodeAEC.cpp',
|
||||||
'river/io/Manager.cpp'
|
'river/io/Manager.cpp'
|
||||||
])
|
])
|
||||||
|
if False:
|
||||||
|
myModule.add_optionnal_module_depend('airtaudio', "__AIRTAUDIO_INFERFACE__")
|
||||||
|
myModule.add_optionnal_module_depend('portaudio', "__PORTAUDIO_INFERFACE__")
|
||||||
|
else:
|
||||||
|
myModule.compile_flags_CC([
|
||||||
|
'-D__AIRTAUDIO_INFERFACE__',
|
||||||
|
'-D__PORTAUDIO_INFERFACE__'
|
||||||
|
])
|
||||||
|
myModule.add_module_depend(['airtaudio', 'portaudio'])
|
||||||
|
|
||||||
myModule.add_module_depend(['audio', 'airtaudio', 'drain', 'ejson'])
|
myModule.add_module_depend(['audio', 'drain', 'ejson'])
|
||||||
myModule.add_export_path(tools.get_current_path(__file__))
|
myModule.add_export_path(tools.get_current_path(__file__))
|
||||||
|
|
||||||
# add the currrent module at the
|
# add the currrent module at the
|
||||||
|
@ -336,3 +336,46 @@ void river::Interface::systemVolumeChange() {
|
|||||||
}
|
}
|
||||||
algo->volumeChange();
|
algo->volumeChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void link(etk::FSNode& _node, const std::string& _first, const std::string& _op, const std::string& _second) {
|
||||||
|
if (_op == "->") {
|
||||||
|
_node << " " << _first << " -> " << _second << ";\n";
|
||||||
|
} else if (_op == "<-") {
|
||||||
|
_node << " " << _first << " -> " <<_second<< " [color=transparent];\n";
|
||||||
|
_node << " " << _second << " -> " << _first << " [constraint=false];\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void river::Interface::generateDot(etk::FSNode& _node, const std::string& _nameIO) {
|
||||||
|
_node << "subgraph clusterInterface_" << m_uid << " {\n";
|
||||||
|
_node << " color=orange;\n";
|
||||||
|
_node << " label=\"[" << m_uid << "] Interface : " << m_name << "\";\n";
|
||||||
|
|
||||||
|
_node << " subgraph clusterInterface_" << m_uid << "_process {\n";
|
||||||
|
_node << " label=\"Drain::Process\";\n";
|
||||||
|
_node << " node [shape=ellipse];\n";
|
||||||
|
_node << " INTERFACE_ALGO_" << m_uid << "_in [ label=\"format=xxx\n freq=yyy\n channelMap={left,right}\n in\" ];\n";
|
||||||
|
_node << " INTERFACE_ALGO_" << m_uid << "_out [ label=\"format=xxx\n freq=yyy\n channelMap={left,right}\n out\" ];\n";
|
||||||
|
_node << " }\n";
|
||||||
|
if ( m_mode == river::modeInterface_input
|
||||||
|
|| m_mode == river::modeInterface_feedback) {
|
||||||
|
link(_node, _nameIO, "->", "INTERFACE_ALGO_" + etk::to_string(m_uid) + "_in");
|
||||||
|
link(_node, "INTERFACE_ALGO_" + etk::to_string(m_uid) + "_in", "->", "INTERFACE_ALGO_" + etk::to_string(m_uid) + "_out");
|
||||||
|
} else {
|
||||||
|
link(_node, _nameIO, "<-", "INTERFACE_ALGO_" + etk::to_string(m_uid) + "_out");
|
||||||
|
link(_node, "INTERFACE_ALGO_" + etk::to_string(m_uid) + "_out", "<-", "INTERFACE_ALGO_" + etk::to_string(m_uid) + "_in");
|
||||||
|
}
|
||||||
|
_node << " node [shape=Mdiamond];\n";
|
||||||
|
if (m_mode == river::modeInterface_input) {
|
||||||
|
_node << " API_" << m_uid << "_input [ label=\"API\nINPUT\" ];\n";
|
||||||
|
link(_node, "INTERFACE_ALGO_" + etk::to_string(m_uid) + "_out", "->", "API_" + etk::to_string(m_uid) + "_input");
|
||||||
|
} else if (m_mode == river::modeInterface_feedback) {
|
||||||
|
_node << " API_" << m_uid << "_feedback [ label=\"API\nFEEDBACK\" ];\n";
|
||||||
|
link(_node, "INTERFACE_ALGO_" + etk::to_string(m_uid) + "_out", "->", "API_" + etk::to_string(m_uid) + "_feedback");
|
||||||
|
} else if (m_mode == river::modeInterface_output) {
|
||||||
|
_node << " API_" << m_uid << "_output [ label=\"API\nOUTPUT\" ];\n";
|
||||||
|
link(_node, "INTERFACE_ALGO_" + etk::to_string(m_uid) + "_in", "<-", "API_" + etk::to_string(m_uid) + "_output");
|
||||||
|
}
|
||||||
|
_node << "}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <drain/EndPointWrite.h>
|
#include <drain/EndPointWrite.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <ejson/ejson.h>
|
#include <ejson/ejson.h>
|
||||||
|
#include <etk/os/FSNode.h>
|
||||||
|
|
||||||
namespace river {
|
namespace river {
|
||||||
namespace io {
|
namespace io {
|
||||||
@ -204,6 +205,8 @@ namespace river {
|
|||||||
virtual void systemNeedOutputData(std::chrono::system_clock::time_point _time, void* _data, size_t _nbChunk, size_t _chunkSize);
|
virtual void systemNeedOutputData(std::chrono::system_clock::time_point _time, void* _data, size_t _nbChunk, size_t _chunkSize);
|
||||||
virtual void systemVolumeChange();
|
virtual void systemVolumeChange();
|
||||||
float m_volume; //!< Local channel Volume
|
float m_volume; //!< Local channel Volume
|
||||||
|
public:
|
||||||
|
virtual void generateDot(etk::FSNode& _node, const std::string& _nameIO);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,11 +10,15 @@
|
|||||||
#include "Node.h"
|
#include "Node.h"
|
||||||
#include "NodeAEC.h"
|
#include "NodeAEC.h"
|
||||||
#include "NodeAirTAudio.h"
|
#include "NodeAirTAudio.h"
|
||||||
|
#include "NodePortAudio.h"
|
||||||
#include <etk/os/FSNode.h>
|
#include <etk/os/FSNode.h>
|
||||||
|
|
||||||
#undef __class__
|
#undef __class__
|
||||||
#define __class__ "io::Manager"
|
#define __class__ "io::Manager"
|
||||||
|
|
||||||
|
#ifdef __PORTAUDIO_INFERFACE__
|
||||||
|
#include <portaudio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
static std::string basicAutoConfig =
|
static std::string basicAutoConfig =
|
||||||
"{\n"
|
"{\n"
|
||||||
@ -55,6 +59,22 @@ river::io::Manager::Manager() {
|
|||||||
m_config.parse(basicAutoConfig);
|
m_config.parse(basicAutoConfig);
|
||||||
}
|
}
|
||||||
// TODO : Load virtual.json and check if all is correct ...
|
// TODO : Load virtual.json and check if all is correct ...
|
||||||
|
|
||||||
|
#ifdef __PORTAUDIO_INFERFACE__
|
||||||
|
PaError err = Pa_Initialize();
|
||||||
|
if(err != paNoError) {
|
||||||
|
RIVER_WARNING("Can not initialize portaudio : " << Pa_GetErrorText(err));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
river::io::Manager::~Manager() {
|
||||||
|
#ifdef __PORTAUDIO_INFERFACE__
|
||||||
|
PaError err = Pa_Terminate();
|
||||||
|
if(err != paNoError) {
|
||||||
|
RIVER_WARNING("Can not initialize portaudio : " << Pa_GetErrorText(err));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -64,13 +84,16 @@ std::shared_ptr<river::io::Manager> river::io::Manager::getInstance() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<river::io::Node> river::io::Manager::getNode(const std::string& _name) {
|
std::shared_ptr<river::io::Node> river::io::Manager::getNode(const std::string& _name) {
|
||||||
|
RIVER_WARNING("Get node : " << _name);
|
||||||
for (auto &it : m_list) {
|
for (auto &it : m_list) {
|
||||||
std::shared_ptr<river::io::Node> tmppp = it.lock();
|
std::shared_ptr<river::io::Node> tmppp = it.lock();
|
||||||
if ( tmppp != nullptr
|
if ( tmppp != nullptr
|
||||||
&& _name == tmppp->getName()) {
|
&& _name == tmppp->getName()) {
|
||||||
|
RIVER_WARNING(" find it ... ");
|
||||||
return tmppp;
|
return tmppp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RIVER_WARNING("Create a new one : " << _name);
|
||||||
// check if the node can be open :
|
// check if the node can be open :
|
||||||
const std::shared_ptr<const ejson::Object> tmpObject = m_config.getObject(_name);
|
const std::shared_ptr<const ejson::Object> tmpObject = m_config.getObject(_name);
|
||||||
if (tmpObject != nullptr) {
|
if (tmpObject != nullptr) {
|
||||||
@ -81,6 +104,11 @@ std::shared_ptr<river::io::Node> river::io::Manager::getNode(const std::string&
|
|||||||
std::shared_ptr<river::io::Node> tmp = river::io::NodeAirTAudio::create(_name, tmpObject);
|
std::shared_ptr<river::io::Node> tmp = river::io::NodeAirTAudio::create(_name, tmpObject);
|
||||||
m_list.push_back(tmp);
|
m_list.push_back(tmp);
|
||||||
return tmp;
|
return tmp;
|
||||||
|
} else if ( ioType == "PAinput"
|
||||||
|
|| ioType == "PAoutput") {
|
||||||
|
std::shared_ptr<river::io::Node> tmp = river::io::NodePortAudio::create(_name, tmpObject);
|
||||||
|
m_list.push_back(tmp);
|
||||||
|
return tmp;
|
||||||
} else if (ioType == "aec") {
|
} else if (ioType == "aec") {
|
||||||
std::shared_ptr<river::io::Node> tmp = river::io::NodeAEC::create(_name, tmpObject);
|
std::shared_ptr<river::io::Node> tmp = river::io::NodeAEC::create(_name, tmpObject);
|
||||||
m_list.push_back(tmp);
|
m_list.push_back(tmp);
|
||||||
@ -152,7 +180,7 @@ void river::io::Manager::generateDot(const std::string& _filename) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
node << "digraph G {" << "\n";
|
node << "digraph G {" << "\n";
|
||||||
node << " rankdir=\"RL\";\n";
|
node << " rankdir=\"LR\";\n";
|
||||||
int32_t id = 0;
|
int32_t id = 0;
|
||||||
for (auto &it2 : m_list) {
|
for (auto &it2 : m_list) {
|
||||||
std::shared_ptr<river::io::Node> val = it2.lock();
|
std::shared_ptr<river::io::Node> val = it2.lock();
|
||||||
|
@ -33,7 +33,7 @@ namespace river {
|
|||||||
/**
|
/**
|
||||||
* @brief Destructor
|
* @brief Destructor
|
||||||
*/
|
*/
|
||||||
virtual ~Manager() {};
|
virtual ~Manager();
|
||||||
private:
|
private:
|
||||||
ejson::Document m_config; // harware configuration
|
ejson::Document m_config; // harware configuration
|
||||||
std::vector<std::shared_ptr<river::io::Node> > m_listKeepAlive; //!< list of all Node that might be keep alive sone time
|
std::vector<std::shared_ptr<river::io::Node> > m_listKeepAlive; //!< list of all Node that might be keep alive sone time
|
||||||
|
@ -38,6 +38,7 @@ river::io::Node::Node(const std::string& _name, const std::shared_ptr<const ejso
|
|||||||
*/
|
*/
|
||||||
std::string interfaceType = m_config->getStringValue("io");
|
std::string interfaceType = m_config->getStringValue("io");
|
||||||
if ( interfaceType == "input"
|
if ( interfaceType == "input"
|
||||||
|
|| interfaceType == "PAinput"
|
||||||
|| interfaceType == "aec") {
|
|| interfaceType == "aec") {
|
||||||
m_isInput = true;
|
m_isInput = true;
|
||||||
} else {
|
} else {
|
||||||
@ -102,6 +103,19 @@ river::io::Node::~Node() {
|
|||||||
RIVER_INFO("-----------------------------------------------------------------");
|
RIVER_INFO("-----------------------------------------------------------------");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
size_t river::io::Node::getNumberOfInterface(enum river::modeInterface _interfaceType) {
|
||||||
|
size_t out = 0;
|
||||||
|
for (auto &it : m_list) {
|
||||||
|
if (it == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (it->getMode() == _interfaceType) {
|
||||||
|
out++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
void river::io::Node::registerAsRemote(const std::shared_ptr<river::Interface>& _interface) {
|
void river::io::Node::registerAsRemote(const std::shared_ptr<river::Interface>& _interface) {
|
||||||
auto it = m_listAvaillable.begin();
|
auto it = m_listAvaillable.begin();
|
||||||
while (it != m_listAvaillable.end()) {
|
while (it != m_listAvaillable.end()) {
|
||||||
@ -116,8 +130,8 @@ void river::io::Node::registerAsRemote(const std::shared_ptr<river::Interface>&
|
|||||||
void river::io::Node::interfaceAdd(const std::shared_ptr<river::Interface>& _interface) {
|
void river::io::Node::interfaceAdd(const std::shared_ptr<river::Interface>& _interface) {
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(m_mutex);
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
for (size_t iii=0; iii< m_list.size(); ++iii) {
|
for (auto &it : m_list) {
|
||||||
if (_interface == m_list[iii]) {
|
if (_interface == it) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -226,81 +240,85 @@ int32_t river::io::Node::newOutput(void* _outputBuffer,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void link(etk::FSNode& _node, const std::string& _first, const std::string& _op, const std::string& _second) {
|
||||||
|
if (_op == "->") {
|
||||||
|
_node << " " << _first << " -> " << _second << ";\n";
|
||||||
|
} else if (_op == "<-") {
|
||||||
|
_node << " " << _first << " -> " <<_second<< " [color=transparent];\n";
|
||||||
|
_node << " " << _second << " -> " << _first << " [constraint=false];\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void river::io::Node::generateDot(etk::FSNode& _node) {
|
void river::io::Node::generateDot(etk::FSNode& _node) {
|
||||||
_node << "subgraph clusterNode_" << m_uid << " {\n";
|
_node << "subgraph clusterNode_" << m_uid << " {\n";
|
||||||
_node << " color=blue;\n";
|
_node << " color=blue;\n";
|
||||||
_node << " label=\"IO::Node : " << m_name << "\";\n";
|
_node << " label=\"[" << m_uid << "] IO::Node : " << m_name << "\";\n";
|
||||||
if (m_isInput == true) {
|
if (m_isInput == true) {
|
||||||
_node << " node [shape=larrow];\n";
|
_node << " node [shape=rarrow];\n";
|
||||||
_node << " NODE_" << m_uid << "_HW_interface [ label=\"HW interface\n interface=ALSA\n stream=MICROPHONE\n type=input\" ];\n";
|
_node << " NODE_" << m_uid << "_HW_interface [ label=\"HW interface\n interface=ALSA\n stream=MICROPHONE\n type=input\" ];\n";
|
||||||
_node << " subgraph clusterNode_" << m_uid << "_process {\n";
|
_node << " subgraph clusterNode_" << m_uid << "_process {\n";
|
||||||
_node << " label=\"Drain::Process\";\n";
|
_node << " label=\"Drain::Process\";\n";
|
||||||
_node << " node [shape=ellipse];\n";
|
_node << " node [shape=ellipse];\n";
|
||||||
_node << " ALGO_" << m_uid << "_in [ label=\"format=xxx\n freq=yyy\n channelMap={left,right}\" ];\n";
|
_node << " node_ALGO_" << m_uid << "_in [ label=\"format=xxx\n freq=yyy\n channelMap={left,right}\" ];\n";
|
||||||
_node << " ALGO_" << m_uid << "_out [ label=\"format=xxx\n freq=yyy\n channelMap={left,right}\" ];\n";
|
_node << " node_ALGO_" << m_uid << "_out [ label=\"format=xxx\n freq=yyy\n channelMap={left,right}\" ];\n";
|
||||||
|
|
||||||
_node << " }\n";
|
_node << " }\n";
|
||||||
_node << " node [shape=square];\n";
|
_node << " node [shape=square];\n";
|
||||||
_node << " NODE_" << m_uid << "_demuxer [ label=\"DEMUXER\n format=xxx\" ];\n";
|
_node << " NODE_" << m_uid << "_demuxer [ label=\"DEMUXER\n format=xxx\" ];\n";
|
||||||
// Link all nodes :
|
// Link all nodes :
|
||||||
_node << " NODE_" << m_uid << "_HW_interface -> ALGO_" << m_uid << "_in [arrowhead=\"open\"];\n";
|
_node << " NODE_" << m_uid << "_HW_interface -> node_ALGO_" << m_uid << "_in [arrowhead=\"open\"];\n";
|
||||||
_node << " ALGO_" << m_uid << "_in -> ALGO_" << m_uid << "_out [arrowhead=\"open\"];\n";
|
_node << " node_ALGO_" << m_uid << "_in -> node_ALGO_" << m_uid << "_out [arrowhead=\"open\"];\n";
|
||||||
_node << " ALGO_" << m_uid << "_out -> NODE_" << m_uid << "_demuxer [arrowhead=\"open\"];\n";
|
_node << " node_ALGO_" << m_uid << "_out -> NODE_" << m_uid << "_demuxer [arrowhead=\"open\"];\n";
|
||||||
} else {
|
} else {
|
||||||
_node << " node [shape=rarrow];\n";
|
size_t nbOutput = getNumberOfInterface(river::modeInterface_output);
|
||||||
|
size_t nbfeedback = getNumberOfInterface(river::modeInterface_feedback);
|
||||||
|
_node << " node [shape=larrow];\n";
|
||||||
_node << " NODE_" << m_uid << "_HW_interface [ label=\"HW interface\n interface=ALSA\n stream=SPEAKER\n type=output\" ];\n";
|
_node << " NODE_" << m_uid << "_HW_interface [ label=\"HW interface\n interface=ALSA\n stream=SPEAKER\n type=output\" ];\n";
|
||||||
|
if (nbOutput>0) {
|
||||||
_node << " subgraph clusterNode_" << m_uid << "_process {\n";
|
_node << " subgraph clusterNode_" << m_uid << "_process {\n";
|
||||||
_node << " label=\"Drain::Process\";\n";
|
_node << " label=\"Drain::Process\";\n";
|
||||||
_node << " node [shape=ellipse];\n";
|
_node << " node [shape=ellipse];\n";
|
||||||
_node << " ALGO_" << m_uid << "_out [ label=\"format=xxx\n freq=yyy\n channelMap={left,right}\" ];\n";
|
_node << " node_ALGO_" << m_uid << "_out [ label=\"format=xxx\n freq=yyy\n channelMap={left,right}\" ];\n";
|
||||||
_node << " ALGO_" << m_uid << "_in [ label=\"format=xxx\n freq=yyy\n channelMap={left,right}\" ];\n";
|
_node << " node_ALGO_" << m_uid << "_in [ label=\"format=xxx\n freq=yyy\n channelMap={left,right}\" ];\n";
|
||||||
_node << " }\n";
|
_node << " }\n";
|
||||||
|
}
|
||||||
_node << " node [shape=square];\n";
|
_node << " node [shape=square];\n";
|
||||||
|
if (nbOutput>0) {
|
||||||
_node << " NODE_" << m_uid << "_muxer [ label=\"MUXER\n format=xxx\" ];\n";
|
_node << " NODE_" << m_uid << "_muxer [ label=\"MUXER\n format=xxx\" ];\n";
|
||||||
|
}
|
||||||
|
if (nbfeedback>0) {
|
||||||
_node << " NODE_" << m_uid << "_demuxer [ label=\"DEMUXER\n format=xxx\" ];\n";
|
_node << " NODE_" << m_uid << "_demuxer [ label=\"DEMUXER\n format=xxx\" ];\n";
|
||||||
|
}
|
||||||
// Link all nodes :
|
// Link all nodes :
|
||||||
_node << " NODE_" << m_uid << "_muxer -> ALGO_" << m_uid << "_in [arrowhead=\"open\"];\n";
|
if (nbOutput>0) {
|
||||||
_node << " ALGO_" << m_uid << "_in -> ALGO_" << m_uid << "_out [arrowhead=\"open\"];\n";
|
link(_node, "NODE_" + etk::to_string(m_uid) + "_HW_interface", "<-", "node_ALGO_" + etk::to_string(m_uid) + "_out");
|
||||||
_node << " ALGO_" << m_uid << "_out -> NODE_" << m_uid << "_HW_interface [arrowhead=\"open\"];\n";
|
link(_node, "node_ALGO_" + etk::to_string(m_uid) + "_out", "<-", "node_ALGO_" + etk::to_string(m_uid) + "_in");
|
||||||
|
link(_node, "node_ALGO_" + etk::to_string(m_uid) + "_in", "<-", "NODE_" + etk::to_string(m_uid) + "_muxer");
|
||||||
|
}
|
||||||
|
if (nbfeedback>0) {
|
||||||
_node << " NODE_" << m_uid << "_HW_interface -> NODE_" << m_uid << "_demuxer [arrowhead=\"open\"];\n";
|
_node << " NODE_" << m_uid << "_HW_interface -> NODE_" << m_uid << "_demuxer [arrowhead=\"open\"];\n";
|
||||||
|
}
|
||||||
|
if ( nbOutput>0
|
||||||
|
&& nbfeedback>0) {
|
||||||
_node << " { rank=same; NODE_" << m_uid << "_demuxer; NODE_" << m_uid << "_muxer }\n";
|
_node << " { rank=same; NODE_" << m_uid << "_demuxer; NODE_" << m_uid << "_muxer }\n";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
_node << "}\n";
|
||||||
|
|
||||||
for (auto &it : m_list) {
|
for (auto &it : m_list) {
|
||||||
if (it != nullptr) {
|
if (it != nullptr) {
|
||||||
if (it->getMode() == modeInterface_input) {
|
if (it->getMode() == modeInterface_input) {
|
||||||
_node << " interface_" << it->m_uid << " [ label=\"name=" << it->getName() << "\n type=input\" ];\n";
|
it->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_demuxer");
|
||||||
_node << " NODE_" << m_uid << "_demuxer -> interface_" << it->m_uid << " [ arrowhead=\"open\"];\n";
|
|
||||||
} else if (it->getMode() == modeInterface_output) {
|
} else if (it->getMode() == modeInterface_output) {
|
||||||
_node << " interface_" << it->m_uid << " [ label=\"name=" << it->getName() << "\n type=output\" ];\n";
|
it->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_muxer");
|
||||||
_node << " interface_" << it->m_uid << " -> NODE_" << m_uid << "_muxer [ arrowhead=\"open\"];\n";
|
|
||||||
} else if (it->getMode() == modeInterface_feedback) {
|
} else if (it->getMode() == modeInterface_feedback) {
|
||||||
_node << " interface_" << it->m_uid << " [ label=\"name=" << it->getName() << "\n type=feedback\" ];\n";
|
it->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_demuxer");
|
||||||
_node << " NODE_" << m_uid << "_demuxer -> interface_" << it->m_uid << " [ arrowhead=\"open\"];\n";
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// configure display:
|
|
||||||
_node << " node [shape=record, fontname=Helvetica, fontsize=10, color=lightsteelblue1, style=filled];\n";
|
|
||||||
//_node << " node [shape=diamond, fontname=Helvetica, fontsize=10, color=orangered, style=filled];\n"
|
|
||||||
//_node << " node [shape=ellipse, fontname=Helvetica, fontsize=8, color=aquamarine2, style=filled];\n";
|
|
||||||
// add elements
|
|
||||||
_node << " NODE_" << m_uid << " [ label=\"name=" << m_name << "\n type=" << (m_isInput?"input":"output") << "\" ];\n";
|
|
||||||
// add IO
|
|
||||||
_node << " node [shape=record, fontname=Helvetica, fontsize=8, color=aquamarine2, style=filled];\n";
|
|
||||||
int32_t id = 0;
|
|
||||||
for (auto &it : m_list) {
|
|
||||||
if (it != nullptr) {
|
|
||||||
_node << " interface_" << it->m_uid << " [ label=\"name=" << it->getName() << "\" ];\n";
|
|
||||||
_node << " NODE_" << m_uid << " -> interface_" << it->m_uid << " [ arrowhead=\"open\"];\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
_node << "}\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +67,7 @@ namespace river {
|
|||||||
protected:
|
protected:
|
||||||
std::vector<std::weak_ptr<river::Interface> > m_listAvaillable; //!< List of all interface that exist on this Node
|
std::vector<std::weak_ptr<river::Interface> > m_listAvaillable; //!< List of all interface that exist on this Node
|
||||||
std::vector<std::shared_ptr<river::Interface> > m_list;
|
std::vector<std::shared_ptr<river::Interface> > m_list;
|
||||||
|
size_t getNumberOfInterface(enum river::modeInterface _interfaceType);
|
||||||
public:
|
public:
|
||||||
void registerAsRemote(const std::shared_ptr<river::Interface>& _interface);
|
void registerAsRemote(const std::shared_ptr<river::Interface>& _interface);
|
||||||
void interfaceAdd(const std::shared_ptr<river::Interface>& _interface);
|
void interfaceAdd(const std::shared_ptr<river::Interface>& _interface);
|
||||||
|
@ -127,7 +127,7 @@ river::io::NodeAEC::NodeAEC(const std::string& _name, const std::shared_ptr<cons
|
|||||||
Node(_name, _config) {
|
Node(_name, _config) {
|
||||||
drain::IOFormatInterface interfaceFormat = getInterfaceFormat();
|
drain::IOFormatInterface interfaceFormat = getInterfaceFormat();
|
||||||
drain::IOFormatInterface hardwareFormat = getHarwareFormat();
|
drain::IOFormatInterface hardwareFormat = getHarwareFormat();
|
||||||
m_sampleTime = std::chrono::nanoseconds(1000000000*int64_t(hardwareFormat.getFrequency()));
|
m_sampleTime = std::chrono::nanoseconds(1000000000/int64_t(hardwareFormat.getFrequency()));
|
||||||
/**
|
/**
|
||||||
# connect in input mode
|
# connect in input mode
|
||||||
map-on-microphone:{
|
map-on-microphone:{
|
||||||
@ -311,8 +311,15 @@ void river::io::NodeAEC::process() {
|
|||||||
}
|
}
|
||||||
std::chrono::system_clock::time_point MicTime = m_bufferMicrophone.getReadTimeStamp();
|
std::chrono::system_clock::time_point MicTime = m_bufferMicrophone.getReadTimeStamp();
|
||||||
std::chrono::system_clock::time_point fbTime = m_bufferFeedBack.getReadTimeStamp();
|
std::chrono::system_clock::time_point fbTime = m_bufferFeedBack.getReadTimeStamp();
|
||||||
|
std::chrono::nanoseconds delta;
|
||||||
|
if (MicTime < fbTime) {
|
||||||
|
delta = fbTime - MicTime;
|
||||||
|
} else {
|
||||||
|
delta = MicTime - fbTime;
|
||||||
|
}
|
||||||
|
|
||||||
if (MicTime-fbTime > m_sampleTime) {
|
RIVER_INFO("check delta " << delta.count() << " > " << m_sampleTime.count());
|
||||||
|
if (delta > m_sampleTime) {
|
||||||
// Synchronize if possible
|
// Synchronize if possible
|
||||||
if (MicTime < fbTime) {
|
if (MicTime < fbTime) {
|
||||||
RIVER_INFO("micTime < fbTime : Change Microphone time start " << fbTime);
|
RIVER_INFO("micTime < fbTime : Change Microphone time start " << fbTime);
|
||||||
@ -349,7 +356,7 @@ void river::io::NodeAEC::process() {
|
|||||||
while (true) {
|
while (true) {
|
||||||
MicTime = m_bufferMicrophone.getReadTimeStamp();
|
MicTime = m_bufferMicrophone.getReadTimeStamp();
|
||||||
fbTime = m_bufferFeedBack.getReadTimeStamp();
|
fbTime = m_bufferFeedBack.getReadTimeStamp();
|
||||||
RIVER_INFO(" process 256 samples ... " << MicTime);
|
RIVER_INFO(" process 256 samples ... micTime=" << MicTime << " fbTime=" << fbTime << " delta = " << (MicTime-fbTime).count());
|
||||||
m_bufferMicrophone.read(&dataMic[0], 256);
|
m_bufferMicrophone.read(&dataMic[0], 256);
|
||||||
m_bufferFeedBack.read(&dataFB[0], 256);
|
m_bufferFeedBack.read(&dataFB[0], 256);
|
||||||
SAVE_FILE_MACRO(int16_t, "REC_Microphone_sync.raw", &dataMic[0], 256*2);
|
SAVE_FILE_MACRO(int16_t, "REC_Microphone_sync.raw", &dataMic[0], 256*2);
|
||||||
@ -370,3 +377,47 @@ void river::io::NodeAEC::processAEC(void* _dataMic, void* _dataFB, uint32_t _nbC
|
|||||||
newInput(_dataMic, _nbChunk, _time);
|
newInput(_dataMic, _nbChunk, _time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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=xxx\n freq=yyy\n channelMap={left,right}\" ];\n";
|
||||||
|
_node << " node_ALGO_" << m_uid << "_out [ label=\"format=xxx\n freq=yyy\n channelMap={left,right}\" ];\n";
|
||||||
|
|
||||||
|
_node << " }\n";
|
||||||
|
_node << " node [shape=square];\n";
|
||||||
|
_node << " NODE_" << m_uid << "_demuxer [ label=\"DEMUXER\n format=xxx\" ];\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 << "}\n";
|
||||||
|
if (m_interfaceMicrophone != nullptr) {
|
||||||
|
_node << " API_" << m_interfaceMicrophone->m_uid << "_input -> NODE_" << m_uid << "_HW_AEC;\n";
|
||||||
|
}
|
||||||
|
if (m_interfaceFeedBack != nullptr) {
|
||||||
|
_node << " API_" << m_interfaceFeedBack->m_uid << "_feedback -> NODE_" << m_uid << "_HW_AEC;\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &it : m_list) {
|
||||||
|
if (it != nullptr) {
|
||||||
|
if (it->getMode() == modeInterface_input) {
|
||||||
|
it->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_demuxer");
|
||||||
|
} else if (it->getMode() == modeInterface_output) {
|
||||||
|
it->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_muxer");
|
||||||
|
} else if (it->getMode() == modeInterface_feedback) {
|
||||||
|
it->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_demuxer");
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -53,6 +53,8 @@ namespace river {
|
|||||||
std::chrono::nanoseconds m_sampleTime; //!< represent the sample time at the specify frequency.
|
std::chrono::nanoseconds m_sampleTime; //!< represent the sample time at the specify frequency.
|
||||||
void process();
|
void process();
|
||||||
void processAEC(void* _dataMic, void* _dataFB, uint32_t _nbChunk, const std::chrono::system_clock::time_point& _time);
|
void processAEC(void* _dataMic, void* _dataFB, uint32_t _nbChunk, const std::chrono::system_clock::time_point& _time);
|
||||||
|
public:
|
||||||
|
virtual void generateDot(etk::FSNode& _node);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
#undef __class__
|
#undef __class__
|
||||||
#define __class__ "io::NodeAirTAudio"
|
#define __class__ "io::NodeAirTAudio"
|
||||||
|
|
||||||
std::string asString(const std::chrono::system_clock::time_point& tp) {
|
static std::string asString(const std::chrono::system_clock::time_point& tp) {
|
||||||
// convert to system time:
|
// convert to system time:
|
||||||
std::time_t t = std::chrono::system_clock::to_time_t(tp);
|
std::time_t t = std::chrono::system_clock::to_time_t(tp);
|
||||||
// convert in human string
|
// convert in human string
|
||||||
|
152
river/io/NodePortAudio.cpp
Normal file
152
river/io/NodePortAudio.cpp
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/** @file
|
||||||
|
* @author Edouard DUPIN
|
||||||
|
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||||
|
* @license APACHE v2.0 (see license file)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <river/io/NodePortAudio.h>
|
||||||
|
#include <river/debug.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#undef __class__
|
||||||
|
#define __class__ "io::NodePortAudio"
|
||||||
|
|
||||||
|
static std::string asString(const std::chrono::system_clock::time_point& tp) {
|
||||||
|
// convert to system time:
|
||||||
|
std::time_t t = std::chrono::system_clock::to_time_t(tp);
|
||||||
|
// convert in human string
|
||||||
|
std::string ts = std::ctime(&t);
|
||||||
|
// remove \n
|
||||||
|
ts.resize(ts.size()-1);
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
static std::ostream& operator <<(std::ostream& _os, const std::chrono::system_clock::time_point& _obj) {
|
||||||
|
std::chrono::microseconds us = std::chrono::duration_cast<std::chrono::microseconds>(_obj.time_since_epoch());
|
||||||
|
_os << us.count();
|
||||||
|
return _os;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int portAudioStreamCallback(const void *_input,
|
||||||
|
void *_output,
|
||||||
|
unsigned long _frameCount,
|
||||||
|
const PaStreamCallbackTimeInfo* _timeInfo,
|
||||||
|
PaStreamCallbackFlags _statusFlags,
|
||||||
|
void *_userData) {
|
||||||
|
river::io::NodePortAudio* myClass = reinterpret_cast<river::io::NodePortAudio*>(_userData);
|
||||||
|
int64_t sec = int64_t(_timeInfo->inputBufferAdcTime);
|
||||||
|
int64_t nsec = (_timeInfo->inputBufferAdcTime-double(sec))*1000000000LL;
|
||||||
|
std::chrono::system_clock::time_point timeInput = std::chrono::system_clock::from_time_t(sec) + std::chrono::nanoseconds(nsec);
|
||||||
|
sec = int64_t(_timeInfo->outputBufferDacTime);
|
||||||
|
nsec = (_timeInfo->outputBufferDacTime-double(sec))*1000000000LL;
|
||||||
|
std::chrono::system_clock::time_point timeOutput = std::chrono::system_clock::from_time_t(sec) + std::chrono::nanoseconds(nsec);
|
||||||
|
return myClass->duplexCallback(_input,
|
||||||
|
timeInput,
|
||||||
|
_output,
|
||||||
|
timeOutput,
|
||||||
|
_frameCount,
|
||||||
|
_statusFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t river::io::NodePortAudio::duplexCallback(const void* _inputBuffer,
|
||||||
|
const std::chrono::system_clock::time_point& _timeInput,
|
||||||
|
void* _outputBuffer,
|
||||||
|
const std::chrono::system_clock::time_point& _timeOutput,
|
||||||
|
uint32_t _nbChunk,
|
||||||
|
PaStreamCallbackFlags _status) {
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
// TODO : Manage status ...
|
||||||
|
if (_inputBuffer != nullptr) {
|
||||||
|
RIVER_VERBOSE("data Input size request :" << _nbChunk << " [BEGIN] status=" << _status << " nbIO=" << m_list.size());
|
||||||
|
newInput(_inputBuffer, _nbChunk, _timeInput);
|
||||||
|
}
|
||||||
|
if (_outputBuffer != nullptr) {
|
||||||
|
RIVER_VERBOSE("data Output size request :" << _nbChunk << " [BEGIN] status=" << _status << " nbIO=" << m_list.size());
|
||||||
|
newOutput(_outputBuffer, _nbChunk, _timeOutput);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::shared_ptr<river::io::NodePortAudio> river::io::NodePortAudio::create(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config) {
|
||||||
|
return std::shared_ptr<river::io::NodePortAudio>(new river::io::NodePortAudio(_name, _config));
|
||||||
|
}
|
||||||
|
|
||||||
|
river::io::NodePortAudio::NodePortAudio(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config) :
|
||||||
|
Node(_name, _config) {
|
||||||
|
drain::IOFormatInterface interfaceFormat = getInterfaceFormat();
|
||||||
|
drain::IOFormatInterface hardwareFormat = getHarwareFormat();
|
||||||
|
/**
|
||||||
|
map-on:{ # select hardware interface and name
|
||||||
|
interface:"alsa", # interface : "alsa", "pulse", "core", ...
|
||||||
|
name:"default", # name of the interface
|
||||||
|
},
|
||||||
|
nb-chunk:1024 # number of chunk to open device (create the latency anf the frequency to call user)
|
||||||
|
*/
|
||||||
|
enum airtaudio::type typeInterface = airtaudio::type_undefined;
|
||||||
|
std::string streamName = "default";
|
||||||
|
const std::shared_ptr<const ejson::Object> tmpObject = m_config->getObject("map-on");
|
||||||
|
if (tmpObject == nullptr) {
|
||||||
|
RIVER_WARNING("missing node : 'map-on' ==> auto map : 'auto:default'");
|
||||||
|
} else {
|
||||||
|
std::string value = tmpObject->getStringValue("interface", "default");
|
||||||
|
typeInterface = airtaudio::getTypeFromString(value);
|
||||||
|
streamName = tmpObject->getStringValue("name", "default");
|
||||||
|
}
|
||||||
|
int32_t nbChunk = m_config->getNumberValue("nb-chunk", 1024);
|
||||||
|
|
||||||
|
PaError err = 0;
|
||||||
|
if (m_isInput == true) {
|
||||||
|
err = Pa_OpenDefaultStream(&m_stream,
|
||||||
|
hardwareFormat.getMap().size(),
|
||||||
|
0,
|
||||||
|
paInt16,
|
||||||
|
hardwareFormat.getFrequency(),
|
||||||
|
nbChunk,
|
||||||
|
&portAudioStreamCallback,
|
||||||
|
this);
|
||||||
|
} else {
|
||||||
|
err = Pa_OpenDefaultStream(&m_stream,
|
||||||
|
0,
|
||||||
|
hardwareFormat.getMap().size(),
|
||||||
|
paInt16,
|
||||||
|
hardwareFormat.getFrequency(),
|
||||||
|
nbChunk,
|
||||||
|
&portAudioStreamCallback,
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
if( err != paNoError ) {
|
||||||
|
RIVER_ERROR("Can not create Stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " err : " << Pa_GetErrorText(err));
|
||||||
|
}
|
||||||
|
m_process.updateInterAlgo();
|
||||||
|
}
|
||||||
|
|
||||||
|
river::io::NodePortAudio::~NodePortAudio() {
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
RIVER_INFO("close input stream");
|
||||||
|
PaError err = Pa_CloseStream( m_stream );
|
||||||
|
if( err != paNoError ) {
|
||||||
|
RIVER_ERROR("Remove stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not Remove stream ... " << Pa_GetErrorText(err));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void river::io::NodePortAudio::start() {
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
RIVER_INFO("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
|
||||||
|
PaError err = Pa_StartStream(m_stream);
|
||||||
|
if( err != paNoError ) {
|
||||||
|
RIVER_ERROR("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not start stream ... " << Pa_GetErrorText(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void river::io::NodePortAudio::stop() {
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
RIVER_INFO("Stop stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
|
||||||
|
PaError err = Pa_StopStream(m_stream);
|
||||||
|
if( err != paNoError ) {
|
||||||
|
RIVER_ERROR("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not stop stream ... " << Pa_GetErrorText(err));
|
||||||
|
}
|
||||||
|
}
|
46
river/io/NodePortAudio.h
Normal file
46
river/io/NodePortAudio.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/** @file
|
||||||
|
* @author Edouard DUPIN
|
||||||
|
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||||
|
* @license APACHE v2.0 (see license file)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __RIVER_IO_NODE_PORTAUDIO_H__
|
||||||
|
#define __RIVER_IO_NODE_PORTAUDIO_H__
|
||||||
|
|
||||||
|
#include <river/Interface.h>
|
||||||
|
#include <river/io/Node.h>
|
||||||
|
#include <portaudio.h>
|
||||||
|
|
||||||
|
namespace river {
|
||||||
|
namespace io {
|
||||||
|
class Manager;
|
||||||
|
class NodePortAudio : public Node {
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* @brief Constructor
|
||||||
|
*/
|
||||||
|
NodePortAudio(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config);
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<NodePortAudio> create(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config);
|
||||||
|
/**
|
||||||
|
* @brief Destructor
|
||||||
|
*/
|
||||||
|
virtual ~NodePortAudio();
|
||||||
|
protected:
|
||||||
|
PaStream* m_stream;
|
||||||
|
public:
|
||||||
|
int32_t duplexCallback(const void* _inputBuffer,
|
||||||
|
const std::chrono::system_clock::time_point& _timeInput,
|
||||||
|
void* _outputBuffer,
|
||||||
|
const std::chrono::system_clock::time_point& _timeOutput,
|
||||||
|
uint32_t _nbChunk,
|
||||||
|
PaStreamCallbackFlags _status);
|
||||||
|
protected:
|
||||||
|
virtual void start();
|
||||||
|
virtual void stop();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user