[DEV] add AEC interface (no process now)
This commit is contained in:
parent
bc37e70e1e
commit
5cbdf378f8
@ -1,16 +1,26 @@
|
||||
{
|
||||
microphone:{ # name of the device
|
||||
io:"input", # input or output
|
||||
map-on:{ # select hardware interface and name
|
||||
interface:"alsa", # interface : "alsa", "pulse", "core", ...
|
||||
name:"default", # name of the interface
|
||||
# name of the device
|
||||
microphone:{
|
||||
# input or output
|
||||
io:"input",
|
||||
# select hardware interface and name
|
||||
map-on:{
|
||||
# interface : "alsa", "pulse", "core", ...
|
||||
interface:"alsa",
|
||||
# name of the interface
|
||||
name:"default",
|
||||
},
|
||||
frequency:48000, # frequency to open device
|
||||
channel-map:[ # mapping of the harware device (mapping is not get under)
|
||||
# frequency to open device
|
||||
frequency:48000,
|
||||
# mapping of the harware device (mapping is not get under)
|
||||
channel-map:[
|
||||
"front-left", "front-right"
|
||||
],
|
||||
type:"int16", # format to open device (int8, int16, int16-on-ont32, int24, int32, float)
|
||||
nb-chunk:1024 # number of chunk to open device (create the latency anf the frequency to call user)
|
||||
# format to open device (int8, int16, int16-on-ont32, int24, int32, float)
|
||||
type:"int16",
|
||||
# number of chunk to open device (create the latency anf the frequency to call user)
|
||||
nb-chunk:1024,
|
||||
mux-demux-type:"int16",
|
||||
},
|
||||
speaker:{
|
||||
io:"output",
|
||||
@ -24,7 +34,8 @@
|
||||
],
|
||||
type:"int16",
|
||||
nb-chunk:1024,
|
||||
volume-name:"MASTER"
|
||||
volume-name:"MASTER",
|
||||
mux-demux-type:"int16-on-int32",
|
||||
},
|
||||
speaker-pulse:{
|
||||
io:"output",
|
||||
@ -38,7 +49,8 @@
|
||||
],
|
||||
type:"int16",
|
||||
nb-chunk:1024,
|
||||
volume-name:"MASTER"
|
||||
volume-name:"MASTER",
|
||||
mux-demux-type:"int16-on-int32",
|
||||
},
|
||||
speaker-jack:{
|
||||
io:"output",
|
||||
@ -52,6 +64,36 @@
|
||||
],
|
||||
type:"float",
|
||||
nb-chunk:1024,
|
||||
volume-name:"MASTER"
|
||||
volume-name:"MASTER",
|
||||
mux-demux-type:"float",
|
||||
},
|
||||
# virtual Nodes :
|
||||
microphone-clean:{
|
||||
io:"aec",
|
||||
# connect in input mode
|
||||
map-on-microphone:{
|
||||
# generic virtual definition
|
||||
io:"input",
|
||||
map-on:"microphone",
|
||||
resampling-type:"speexdsp",
|
||||
resampling-option:"quality=10"
|
||||
},
|
||||
# connect in feedback mode
|
||||
map-on-feedback:{
|
||||
io:"feedback",
|
||||
map-on:"speaker",
|
||||
resampling-type:"speexdsp",
|
||||
resampling-option:"quality=10",
|
||||
},
|
||||
#classical format configuration:
|
||||
frequency:16000,
|
||||
channel-map:[
|
||||
"front-left", "front-right"
|
||||
],
|
||||
type:"int16",
|
||||
# AEC algo definition
|
||||
algo:"river-remover",
|
||||
algo-mode:"cutter",
|
||||
mux-demux-type:"int16",
|
||||
}
|
||||
}
|
@ -1,9 +1,14 @@
|
||||
{
|
||||
microphone:{ # name of the virtual interface
|
||||
io:"input", # input or output
|
||||
map-on:"microphone", # name of the harware device
|
||||
resampling-type:"speexdsp", # name of the resampler
|
||||
resampling-option:"quality=10" # some option to the resampler
|
||||
# name of the virtual interface
|
||||
microphone:{
|
||||
# input or output
|
||||
io:"input",
|
||||
# name of the harware device
|
||||
map-on:"microphone",
|
||||
# name of the resampler
|
||||
resampling-type:"speexdsp",
|
||||
# some option to the resampler
|
||||
resampling-option:"quality=10"
|
||||
},
|
||||
speaker:{
|
||||
io:"output",
|
||||
@ -12,19 +17,16 @@
|
||||
resampling-option:"quality=10"
|
||||
},
|
||||
feedback:{
|
||||
io:"feedback", # note : Feedback is plugged on an output not an input
|
||||
# note : Feedback is plugged on an output not an input
|
||||
io:"feedback",
|
||||
map-on:"speaker",
|
||||
resampling-type:"speexdsp",
|
||||
resampling-option:"quality=10"
|
||||
},
|
||||
microphone-cleaned:{
|
||||
microphone-clean:{
|
||||
io:"input",
|
||||
map-on:"speaker",
|
||||
map-on:"microphone-clean",
|
||||
resampling-type:"speexdsp",
|
||||
resampling-option:"quality=10",
|
||||
# specific case for AEC : only 3 options
|
||||
aec-map-on:"microphone", # the second input of the AEC (get a single)
|
||||
aec-type:"river-remover", # some type is "airtio-remover",
|
||||
aec-option:"mode=cutter"
|
||||
}
|
||||
resampling-option:"quality=10"
|
||||
},
|
||||
}
|
@ -15,6 +15,8 @@ def create(target):
|
||||
'river/Manager.cpp',
|
||||
'river/Interface.cpp',
|
||||
'river/io/Node.cpp',
|
||||
'river/io/NodeAirTAudio.cpp',
|
||||
'river/io/NodeAEC.cpp',
|
||||
'river/io/Manager.cpp'
|
||||
])
|
||||
|
||||
|
@ -18,9 +18,6 @@
|
||||
|
||||
river::Interface::Interface(void) :
|
||||
m_node(nullptr),
|
||||
m_freq(8000),
|
||||
m_map(),
|
||||
m_format(audio::format_int16),
|
||||
m_name(""),
|
||||
m_volume(0.0f) {
|
||||
|
||||
@ -34,9 +31,6 @@ bool river::Interface::init(const std::string& _name,
|
||||
const std::shared_ptr<const ejson::Object>& _config) {
|
||||
m_name = _name;
|
||||
m_node = _node;
|
||||
m_freq = _freq;
|
||||
m_map = _map;
|
||||
m_format = _format;
|
||||
m_volume = 0.0f;
|
||||
m_config = _config;
|
||||
m_mode = river::modeInterface_unknow;
|
||||
@ -64,9 +58,9 @@ bool river::Interface::init(const std::string& _name,
|
||||
RIVER_INFO(" add volume for node");
|
||||
algo->addVolumeStage(tmpVolume);
|
||||
}
|
||||
m_process.setOutputConfig(drain::IOFormatInterface(m_map, m_format, m_freq));
|
||||
m_process.setOutputConfig(drain::IOFormatInterface(_map, _format, _freq));
|
||||
} else {
|
||||
m_process.setInputConfig(drain::IOFormatInterface(m_map, m_format, m_freq));
|
||||
m_process.setInputConfig(drain::IOFormatInterface(_map, _format, _freq));
|
||||
// add all time the volume stage :
|
||||
std::shared_ptr<drain::Volume> algo = drain::Volume::create();
|
||||
//algo->setOutputFormat(m_node->getInterfaceFormat());
|
||||
@ -114,12 +108,10 @@ void river::Interface::setReadwrite() {
|
||||
if (m_node->isInput() == true) {
|
||||
m_process.removeIfLast<drain::EndPoint>();
|
||||
std::shared_ptr<drain::EndPointRead> algo = drain::EndPointRead::create();
|
||||
//algo->setOutputFormat(drain::IOFormatInterface(m_map, m_format, m_freq));
|
||||
m_process.pushBack(algo);
|
||||
} else {
|
||||
m_process.removeIfFirst<drain::EndPoint>();
|
||||
std::shared_ptr<drain::EndPointWrite> algo = drain::EndPointWrite::create();
|
||||
//algo->setInputFormat(drain::IOFormatInterface(m_map, m_format, m_freq));
|
||||
m_process.pushFront(algo);
|
||||
}
|
||||
}
|
||||
@ -129,8 +121,6 @@ void river::Interface::setOutputCallback(size_t _chunkSize, drain::needDataFunct
|
||||
m_process.removeAlgoDynamic();
|
||||
m_process.removeIfFirst<drain::EndPoint>();
|
||||
std::shared_ptr<drain::Algo> algo = drain::EndPointCallback::create(_function);
|
||||
RIVER_INFO("set property: " << m_map << " " << m_format << " " << m_freq);
|
||||
//algo->setInputFormat(drain::IOFormatInterface(m_map, m_format, m_freq));
|
||||
m_process.pushFront(algo);
|
||||
}
|
||||
|
||||
@ -139,7 +129,6 @@ void river::Interface::setInputCallback(size_t _chunkSize, drain::haveNewDataFun
|
||||
m_process.removeAlgoDynamic();
|
||||
m_process.removeIfLast<drain::EndPoint>();
|
||||
std::shared_ptr<drain::Algo> algo = drain::EndPointCallback::create(_function);
|
||||
//algo->setOutputFormat(drain::IOFormatInterface(m_map, m_format, m_freq));
|
||||
m_process.pushBack(algo);
|
||||
}
|
||||
|
||||
@ -311,9 +300,10 @@ void river::Interface::addVolumeGroup(const std::string& _name) {
|
||||
}
|
||||
}
|
||||
|
||||
void river::Interface::systemNewInputData(std::chrono::system_clock::time_point _time, void* _data, size_t _nbChunk) {
|
||||
void river::Interface::systemNewInputData(std::chrono::system_clock::time_point _time, const void* _data, size_t _nbChunk) {
|
||||
std::unique_lock<std::recursive_mutex> lockProcess(m_mutex);
|
||||
m_process.push(_time, _data, _nbChunk);
|
||||
void * tmpData = const_cast<void*>(_data);
|
||||
m_process.push(_time, tmpData, _nbChunk);
|
||||
}
|
||||
|
||||
void river::Interface::systemNeedOutputData(std::chrono::system_clock::time_point _time, void* _data, size_t _nbChunk, size_t _chunkSize) {
|
||||
|
@ -24,6 +24,8 @@
|
||||
namespace river {
|
||||
namespace io {
|
||||
class Node;
|
||||
class NodeAirTAudio;
|
||||
class NodeAEC;
|
||||
}
|
||||
enum modeInterface {
|
||||
modeInterface_unknow,
|
||||
@ -33,28 +35,9 @@ namespace river {
|
||||
};
|
||||
class Interface : public std::enable_shared_from_this<Interface> {
|
||||
friend class io::Node;
|
||||
friend class io::NodeAirTAudio;
|
||||
friend class io::NodeAEC;
|
||||
friend class Manager;
|
||||
protected:
|
||||
mutable std::recursive_mutex m_mutex;
|
||||
protected:
|
||||
std::shared_ptr<river::io::Node> m_node;
|
||||
float m_freq;
|
||||
std::vector<audio::channel> m_map;
|
||||
audio::format m_format;
|
||||
drain::Process m_process;
|
||||
std::shared_ptr<const ejson::Object> m_config;
|
||||
protected:
|
||||
std::string m_name;
|
||||
public:
|
||||
virtual std::string getName() {
|
||||
return m_name;
|
||||
};
|
||||
protected:
|
||||
enum modeInterface m_mode;
|
||||
public:
|
||||
enum modeInterface getMode() {
|
||||
return m_mode;
|
||||
}
|
||||
protected:
|
||||
/**
|
||||
* @brief Constructor
|
||||
@ -77,6 +60,35 @@ namespace river {
|
||||
audio::format _format,
|
||||
const std::shared_ptr<river::io::Node>& _node,
|
||||
const std::shared_ptr<const ejson::Object>& _config);
|
||||
|
||||
protected:
|
||||
mutable std::recursive_mutex m_mutex;
|
||||
std::shared_ptr<const ejson::Object> m_config;
|
||||
protected:
|
||||
enum modeInterface m_mode;
|
||||
public:
|
||||
enum modeInterface getMode() {
|
||||
return m_mode;
|
||||
}
|
||||
drain::Process m_process;
|
||||
public:
|
||||
const drain::IOFormatInterface& getInterfaceFormat() {
|
||||
if ( m_mode == modeInterface_input
|
||||
|| m_mode == modeInterface_feedback) {
|
||||
return m_process.getOutputConfig();
|
||||
} else {
|
||||
return m_process.getInputConfig();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<river::io::Node> m_node;
|
||||
protected:
|
||||
std::string m_name;
|
||||
public:
|
||||
virtual std::string getName() {
|
||||
return m_name;
|
||||
};
|
||||
/**
|
||||
* @brief set the read/write mode enable.
|
||||
*/
|
||||
@ -186,7 +198,7 @@ namespace river {
|
||||
*/
|
||||
virtual std::chrono::system_clock::time_point getCurrentTime() const;
|
||||
private:
|
||||
virtual void systemNewInputData(std::chrono::system_clock::time_point _time, void* _data, size_t _nbChunk);
|
||||
virtual void systemNewInputData(std::chrono::system_clock::time_point _time, const void* _data, size_t _nbChunk);
|
||||
virtual void systemNeedOutputData(std::chrono::system_clock::time_point _time, void* _data, size_t _nbChunk, size_t _chunkSize);
|
||||
virtual void systemVolumeChange();
|
||||
float m_volume; //!< Local channel Volume
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include <memory>
|
||||
#include <river/debug.h>
|
||||
#include "Node.h"
|
||||
#include "NodeAEC.h"
|
||||
#include "NodeAirTAudio.h"
|
||||
|
||||
#undef __class__
|
||||
#define __class__ "io::Manager"
|
||||
@ -71,9 +73,18 @@ std::shared_ptr<river::io::Node> river::io::Manager::getNode(const std::string&
|
||||
// check if the node can be open :
|
||||
const std::shared_ptr<const ejson::Object> tmpObject = m_config.getObject(_name);
|
||||
if (tmpObject != nullptr) {
|
||||
std::shared_ptr<river::io::Node> tmp = river::io::Node::create(_name, tmpObject);
|
||||
m_list.push_back(tmp);
|
||||
return tmp;
|
||||
// get type : io
|
||||
std::string ioType = tmpObject->getStringValue("io", "error");
|
||||
if ( ioType == "input"
|
||||
|| ioType == "output") {
|
||||
std::shared_ptr<river::io::Node> tmp = river::io::NodeAirTAudio::create(_name, tmpObject);
|
||||
m_list.push_back(tmp);
|
||||
return tmp;
|
||||
} else if (ioType == "aec") {
|
||||
std::shared_ptr<river::io::Node> tmp = river::io::NodeAEC::create(_name, tmpObject);
|
||||
m_list.push_back(tmp);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
RIVER_ERROR("Can not create the interface : '" << _name << "' the node is not DEFINED in the configuration file availlable : " << m_config.getKeys());
|
||||
return nullptr;
|
||||
|
@ -12,115 +12,6 @@
|
||||
#undef __class__
|
||||
#define __class__ "io::Node"
|
||||
|
||||
#ifndef INT16_MAX
|
||||
#define INT16_MAX 0x7fff
|
||||
#endif
|
||||
#ifndef INT16_MIN
|
||||
#define INT16_MIN (-INT16_MAX - 1)
|
||||
#endif
|
||||
#ifndef INT32_MAX
|
||||
#define INT32_MAX 0x7fffffffL
|
||||
#endif
|
||||
#ifndef INT32_MIN
|
||||
#define INT32_MIN (-INT32_MAX - 1L)
|
||||
#endif
|
||||
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int32_t river::io::Node::airtAudioCallback(void* _outputBuffer,
|
||||
void* _inputBuffer,
|
||||
uint32_t _nbChunk,
|
||||
const std::chrono::system_clock::time_point& _time,
|
||||
airtaudio::status _status) {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
//RIVER_INFO("Time=" << _time);
|
||||
/*
|
||||
for (int32_t iii=0; iii<400; ++iii) {
|
||||
RIVER_VERBOSE("dummy=" << uint64_t(dummy[iii]));
|
||||
}
|
||||
*/
|
||||
if (_outputBuffer != nullptr) {
|
||||
RIVER_VERBOSE("data Output size request :" << _nbChunk << " [BEGIN] status=" << _status << " nbIO=" << m_list.size());
|
||||
std::vector<int32_t> output;
|
||||
RIVER_VERBOSE("resize=" << _nbChunk*m_process.getInputConfig().getMap().size());
|
||||
output.resize(_nbChunk*m_process.getInputConfig().getMap().size(), 0);
|
||||
const int32_t* outputTmp = nullptr;
|
||||
std::vector<uint8_t> outputTmp2;
|
||||
RIVER_VERBOSE("resize=" << sizeof(int32_t)*m_process.getInputConfig().getMap().size()*_nbChunk);
|
||||
outputTmp2.resize(sizeof(int32_t)*m_process.getInputConfig().getMap().size()*_nbChunk, 0);
|
||||
for (auto &it : m_list) {
|
||||
if (it == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (it->getMode() != river::modeInterface_output) {
|
||||
continue;
|
||||
}
|
||||
RIVER_VERBOSE(" IO name="<< it->getName());
|
||||
// clear datas ...
|
||||
memset(&outputTmp2[0], 0, sizeof(int32_t)*m_process.getInputConfig().getMap().size()*_nbChunk);
|
||||
RIVER_VERBOSE(" request Data="<< _nbChunk);
|
||||
it->systemNeedOutputData(_time, &outputTmp2[0], _nbChunk, sizeof(int32_t)*m_process.getInputConfig().getMap().size());
|
||||
RIVER_VERBOSE(" Mix it ...");
|
||||
outputTmp = reinterpret_cast<const int32_t*>(&outputTmp2[0]);
|
||||
// Add data to the output tmp buffer :
|
||||
for (size_t kkk=0; kkk<output.size(); ++kkk) {
|
||||
output[kkk] += outputTmp[kkk];
|
||||
}
|
||||
}
|
||||
RIVER_VERBOSE(" End stack process data ...");
|
||||
m_process.processIn(&outputTmp2[0], _nbChunk, _outputBuffer, _nbChunk);
|
||||
RIVER_VERBOSE(" Feedback :");
|
||||
for (auto &it : m_list) {
|
||||
if (it == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (it->getMode() != river::modeInterface_feedback) {
|
||||
continue;
|
||||
}
|
||||
RIVER_VERBOSE(" IO name="<< it->getName() << " (feedback)");
|
||||
it->systemNewInputData(_time, _outputBuffer, _nbChunk);
|
||||
}
|
||||
RIVER_VERBOSE("data Output size request :" << _nbChunk << " [ END ]");
|
||||
}
|
||||
if (_inputBuffer != nullptr) {
|
||||
RIVER_VERBOSE("data Input size request :" << _nbChunk << " [BEGIN] status=" << _status << " nbIO=" << m_list.size());
|
||||
int16_t* inputBuffer = static_cast<int16_t *>(_inputBuffer);
|
||||
for (auto &it : m_list) {
|
||||
if (it == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (it->getMode() != river::modeInterface_input) {
|
||||
continue;
|
||||
}
|
||||
RIVER_INFO(" IO name="<< it->getName());
|
||||
it->systemNewInputData(_time, inputBuffer, _nbChunk);
|
||||
}
|
||||
RIVER_VERBOSE("data Input size request :" << _nbChunk << " [ END ]");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<river::io::Node> river::io::Node::create(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config) {
|
||||
return std::shared_ptr<river::io::Node>(new river::io::Node(_name, _config));
|
||||
}
|
||||
|
||||
river::io::Node::Node(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config) :
|
||||
m_config(_config),
|
||||
@ -132,190 +23,67 @@ river::io::Node::Node(const std::string& _name, const std::shared_ptr<const ejso
|
||||
drain::IOFormatInterface interfaceFormat;
|
||||
drain::IOFormatInterface hardwareFormat;
|
||||
/**
|
||||
io:"input", # input or output
|
||||
map-on:{ # select hardware interface and name
|
||||
interface:"alsa", # interface : "alsa", "pulse", "core", ...
|
||||
name:"default", # name of the interface
|
||||
},
|
||||
io:"input", # input, output or aec
|
||||
frequency:48000, # frequency to open device
|
||||
channel-map:[ # mapping of the harware device (to change map if needed)
|
||||
"front-left", "front-right",
|
||||
"read-left", "rear-right",
|
||||
],
|
||||
type:"int16", # format to open device (int8, int16, int16-on-ont32, int24, int32, float)
|
||||
nb-chunk:1024 # number of chunk to open device (create the latency anf the frequency to call user)
|
||||
# format to open device (int8, int16, int16-on-ont32, int24, int32, float)
|
||||
type:"int16",
|
||||
# muxer/demuxer format type (int8-on-int16, int16-on-int32, int24-on-int32, int32-on-int64, float)
|
||||
mux-demux-type:"int16_on_int32",
|
||||
*/
|
||||
m_isInput = m_config->getStringValue("io") == "input";
|
||||
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'");
|
||||
std::string interfaceType = m_config->getStringValue("io");
|
||||
if ( interfaceType == "input"
|
||||
|| interfaceType == "aec") {
|
||||
m_isInput = true;
|
||||
} else {
|
||||
std::string value = tmpObject->getStringValue("interface", "default");
|
||||
typeInterface = airtaudio::getTypeFromString(value);
|
||||
streamName = tmpObject->getStringValue("name", "default");
|
||||
m_isInput = false;
|
||||
}
|
||||
|
||||
int32_t frequency = m_config->getNumberValue("frequency", 1);
|
||||
// Get audio format type:
|
||||
std::string type = m_config->getStringValue("type", "int16");
|
||||
int32_t nbChunk = m_config->getNumberValue("nb-chunk", 1024);
|
||||
enum audio::format formatType = audio::getFormatFromString(type);
|
||||
// Get volume stage :
|
||||
std::string volumeName = m_config->getStringValue("volume-name", "");
|
||||
if (volumeName != "") {
|
||||
RIVER_INFO("add node volume stage : '" << volumeName << "'");
|
||||
// use global manager for volume ...
|
||||
m_volume = river::io::Manager::getInstance()->getVolumeGroup(volumeName);
|
||||
}
|
||||
|
||||
enum audio::format formatType = audio::getFormatFromString(type);
|
||||
// TODO : MAP ...
|
||||
|
||||
// intanciate specific API ...
|
||||
m_adac.instanciate(typeInterface);
|
||||
// TODO : Check return ...
|
||||
|
||||
if (streamName == "") {
|
||||
streamName = "default";
|
||||
}
|
||||
// Get map type :
|
||||
std::vector<audio::channel> map;
|
||||
// set default channel property :
|
||||
map.push_back(audio::channel_frontLeft);
|
||||
map.push_back(audio::channel_frontRight);
|
||||
|
||||
const std::shared_ptr<const ejson::Array> listChannelMap = m_config->getArray("channel-map");
|
||||
if ( listChannelMap == nullptr
|
||||
|| listChannelMap->size() == 0) {
|
||||
// set default channel property:
|
||||
map.push_back(audio::channel_frontLeft);
|
||||
map.push_back(audio::channel_frontRight);
|
||||
} else {
|
||||
for (size_t iii=0; iii<listChannelMap->size(); ++iii) {
|
||||
std::string value = listChannelMap->getStringValue(iii);
|
||||
map.push_back(audio::getChannelFromString(value));
|
||||
}
|
||||
}
|
||||
hardwareFormat.set(map, formatType, frequency);
|
||||
// TODO : Better view of interface type float -> float, int16 -> int16/int32, ...
|
||||
if (m_isInput == true) {
|
||||
// for input we just transfert audio with no transformation
|
||||
interfaceFormat.set(map, audio::format_int16, frequency);
|
||||
} else {
|
||||
// for output we will do a mix ...
|
||||
interfaceFormat.set(map, audio::format_int16_on_int32, frequency);
|
||||
}
|
||||
|
||||
// search device ID :
|
||||
RIVER_INFO("Open :");
|
||||
RIVER_INFO(" m_streamName=" << streamName);
|
||||
RIVER_INFO(" m_freq=" << hardwareFormat.getFrequency());
|
||||
RIVER_INFO(" m_map=" << hardwareFormat.getMap());
|
||||
RIVER_INFO(" m_format=" << hardwareFormat.getFormat());
|
||||
RIVER_INFO(" m_isInput=" << m_isInput);
|
||||
int32_t deviceId = 0;
|
||||
RIVER_INFO("Device list:");
|
||||
for (int32_t iii=0; iii<m_adac.getDeviceCount(); ++iii) {
|
||||
m_info = m_adac.getDeviceInfo(iii);
|
||||
RIVER_INFO(" " << iii << " name :" << m_info.name);
|
||||
if (m_info.name == streamName) {
|
||||
RIVER_INFO(" Select ...");
|
||||
deviceId = iii;
|
||||
}
|
||||
}
|
||||
// Open specific ID :
|
||||
m_info = m_adac.getDeviceInfo(deviceId);
|
||||
// display property :
|
||||
{
|
||||
RIVER_INFO("Device " << deviceId << " property :");
|
||||
RIVER_INFO(" probe=" << m_info.probed);
|
||||
RIVER_INFO(" name=" << m_info.name);
|
||||
RIVER_INFO(" outputChannels=" << m_info.outputChannels);
|
||||
RIVER_INFO(" inputChannels=" << m_info.inputChannels);
|
||||
RIVER_INFO(" duplexChannels=" << m_info.duplexChannels);
|
||||
RIVER_INFO(" isDefaultOutput=" << m_info.isDefaultOutput);
|
||||
RIVER_INFO(" isDefaultInput=" << m_info.isDefaultInput);
|
||||
RIVER_INFO(" rates=" << m_info.sampleRates);
|
||||
RIVER_INFO(" native Format: " << m_info.nativeFormats);
|
||||
|
||||
if (etk::isIn(hardwareFormat.getFormat(), m_info.nativeFormats) == false) {
|
||||
if (type == "auto") {
|
||||
if (etk::isIn(audio::format_int16, m_info.nativeFormats) == true) {
|
||||
hardwareFormat.setFormat(audio::format_int16);
|
||||
RIVER_INFO("auto set format: " << hardwareFormat.getFormat());
|
||||
} else if (etk::isIn(audio::format_float, m_info.nativeFormats) == true) {
|
||||
hardwareFormat.setFormat(audio::format_float);
|
||||
RIVER_INFO("auto set format: " << hardwareFormat.getFormat());
|
||||
} else if (etk::isIn(audio::format_int16_on_int32, m_info.nativeFormats) == true) {
|
||||
hardwareFormat.setFormat(audio::format_int16_on_int32);
|
||||
RIVER_INFO("auto set format: " << hardwareFormat.getFormat());
|
||||
} else if (etk::isIn(audio::format_int24, m_info.nativeFormats) == true) {
|
||||
hardwareFormat.setFormat(audio::format_int24);
|
||||
RIVER_INFO("auto set format: " << hardwareFormat.getFormat());
|
||||
} else if (m_info.nativeFormats.size() != 0) {
|
||||
hardwareFormat.setFormat(m_info.nativeFormats[0]);
|
||||
RIVER_INFO("auto set format: " << hardwareFormat.getFormat());
|
||||
} else {
|
||||
RIVER_CRITICAL("auto set format no element in the configuration: " << m_info.nativeFormats);
|
||||
}
|
||||
} else {
|
||||
RIVER_CRITICAL("Can not manage input transforamtion: " << hardwareFormat.getFormat() << " not in " << m_info.nativeFormats);
|
||||
}
|
||||
}
|
||||
if (etk::isIn(hardwareFormat.getFrequency(), m_info.sampleRates) == false) {
|
||||
if (etk::isIn(48000, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(48000);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (etk::isIn(44100, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(44100);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (etk::isIn(32000, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(32000);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (etk::isIn(16000, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(16000);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (etk::isIn(8000, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(8000);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (etk::isIn(96000, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(96000);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (m_info.sampleRates.size() != 0) {
|
||||
hardwareFormat.setFrequency(m_info.sampleRates[0]);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency() << "(first element in list) in " << m_info.sampleRates);
|
||||
} else {
|
||||
RIVER_CRITICAL("Can not manage input transforamtion:" << hardwareFormat.getFrequency() << " not in " << m_info.sampleRates);
|
||||
}
|
||||
interfaceFormat.setFrequency(hardwareFormat.getFrequency());
|
||||
}
|
||||
}
|
||||
|
||||
// open Audio device:
|
||||
airtaudio::StreamParameters params;
|
||||
params.deviceId = deviceId;
|
||||
if (m_isInput == true) {
|
||||
m_info.inputChannels = 2;
|
||||
params.nChannels = 2;
|
||||
} else {
|
||||
m_info.outputChannels = 2;
|
||||
params.nChannels = 2;
|
||||
}
|
||||
|
||||
m_rtaudioFrameSize = nbChunk;
|
||||
RIVER_INFO("Open output stream nbChannels=" << params.nChannels);
|
||||
enum airtaudio::error err = airtaudio::error_none;
|
||||
std::string muxerDemuxerConfig = m_config->getStringValue("mux-demux-type", "int16-on-int32");
|
||||
enum audio::format muxerFormatType = audio::getFormatFromString(muxerDemuxerConfig);
|
||||
if (m_isInput == true) {
|
||||
err = m_adac.openStream(nullptr, ¶ms,
|
||||
hardwareFormat.getFormat(), hardwareFormat.getFrequency(), &m_rtaudioFrameSize,
|
||||
std::bind(&river::io::Node::airtAudioCallback,
|
||||
this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2,
|
||||
std::placeholders::_3,
|
||||
std::placeholders::_4,
|
||||
std::placeholders::_5)
|
||||
);
|
||||
if (muxerFormatType != audio::format_int16) {
|
||||
RIVER_CRITICAL("not supported demuxer type ... " << muxerFormatType << " for INPUT set in file:" << muxerDemuxerConfig);
|
||||
}
|
||||
} else {
|
||||
err = m_adac.openStream(¶ms, nullptr,
|
||||
hardwareFormat.getFormat(), hardwareFormat.getFrequency(), &m_rtaudioFrameSize,
|
||||
std::bind(&river::io::Node::airtAudioCallback,
|
||||
this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2,
|
||||
std::placeholders::_3,
|
||||
std::placeholders::_4,
|
||||
std::placeholders::_5)
|
||||
);
|
||||
}
|
||||
if (err != airtaudio::error_none) {
|
||||
RIVER_ERROR("Create stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not create stream " << err);
|
||||
if (muxerFormatType != audio::format_int16_on_int32) {
|
||||
RIVER_CRITICAL("not supported demuxer type ... " << muxerFormatType << " for OUTPUT set in file:" << muxerDemuxerConfig);
|
||||
}
|
||||
}
|
||||
// no map change and no frequency change ...
|
||||
interfaceFormat.set(map, muxerFormatType, frequency);
|
||||
// configure process interface
|
||||
if (m_isInput == true) {
|
||||
m_process.setInputConfig(hardwareFormat);
|
||||
m_process.setOutputConfig(interfaceFormat);
|
||||
@ -323,38 +91,15 @@ river::io::Node::Node(const std::string& _name, const std::shared_ptr<const ejso
|
||||
m_process.setOutputConfig(hardwareFormat);
|
||||
m_process.setInputConfig(interfaceFormat);
|
||||
}
|
||||
m_process.updateInterAlgo();
|
||||
//m_process.updateInterAlgo();
|
||||
}
|
||||
|
||||
river::io::Node::~Node() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
RIVER_INFO("-----------------------------------------------------------------");
|
||||
RIVER_INFO("-- DESTROY NODE --");
|
||||
RIVER_INFO("-----------------------------------------------------------------");
|
||||
RIVER_INFO("close input stream");
|
||||
if (m_adac.isStreamOpen() ) {
|
||||
m_adac.closeStream();
|
||||
}
|
||||
};
|
||||
|
||||
void river::io::Node::start() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
RIVER_INFO("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
|
||||
enum airtaudio::error err = m_adac.startStream();
|
||||
if (err != airtaudio::error_none) {
|
||||
RIVER_ERROR("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not start stream ... " << err);
|
||||
}
|
||||
}
|
||||
|
||||
void river::io::Node::stop() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
RIVER_INFO("Stop stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
|
||||
enum airtaudio::error err = m_adac.stopStream();
|
||||
if (err != airtaudio::error_none) {
|
||||
RIVER_ERROR("Stop stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not stop stream ... " << err);
|
||||
}
|
||||
}
|
||||
|
||||
void river::io::Node::registerAsRemote(const std::shared_ptr<river::Interface>& _interface) {
|
||||
auto it = m_listAvaillable.begin();
|
||||
while (it != m_listAvaillable.end()) {
|
||||
@ -408,3 +153,73 @@ void river::io::Node::volumeChange() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t river::io::Node::newInput(const void* _inputBuffer,
|
||||
uint32_t _nbChunk,
|
||||
const std::chrono::system_clock::time_point& _time) {
|
||||
if (_inputBuffer == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
const int16_t* inputBuffer = static_cast<const int16_t *>(_inputBuffer);
|
||||
for (auto &it : m_list) {
|
||||
if (it == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (it->getMode() != river::modeInterface_input) {
|
||||
continue;
|
||||
}
|
||||
RIVER_VERBOSE(" IO name="<< it->getName());
|
||||
it->systemNewInputData(_time, inputBuffer, _nbChunk);
|
||||
}
|
||||
RIVER_VERBOSE("data Input size request :" << _nbChunk << " [ END ]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t river::io::Node::newOutput(void* _outputBuffer,
|
||||
uint32_t _nbChunk,
|
||||
const std::chrono::system_clock::time_point& _time) {
|
||||
if (_outputBuffer == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
std::vector<int32_t> output;
|
||||
RIVER_VERBOSE("resize=" << _nbChunk*m_process.getInputConfig().getMap().size());
|
||||
output.resize(_nbChunk*m_process.getInputConfig().getMap().size(), 0);
|
||||
const int32_t* outputTmp = nullptr;
|
||||
std::vector<uint8_t> outputTmp2;
|
||||
RIVER_VERBOSE("resize=" << sizeof(int32_t)*m_process.getInputConfig().getMap().size()*_nbChunk);
|
||||
outputTmp2.resize(sizeof(int32_t)*m_process.getInputConfig().getMap().size()*_nbChunk, 0);
|
||||
for (auto &it : m_list) {
|
||||
if (it == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (it->getMode() != river::modeInterface_output) {
|
||||
continue;
|
||||
}
|
||||
RIVER_VERBOSE(" IO name="<< it->getName());
|
||||
// clear datas ...
|
||||
memset(&outputTmp2[0], 0, sizeof(int32_t)*m_process.getInputConfig().getMap().size()*_nbChunk);
|
||||
RIVER_VERBOSE(" request Data="<< _nbChunk);
|
||||
it->systemNeedOutputData(_time, &outputTmp2[0], _nbChunk, sizeof(int32_t)*m_process.getInputConfig().getMap().size());
|
||||
RIVER_VERBOSE(" Mix it ...");
|
||||
outputTmp = reinterpret_cast<const int32_t*>(&outputTmp2[0]);
|
||||
// Add data to the output tmp buffer :
|
||||
for (size_t kkk=0; kkk<output.size(); ++kkk) {
|
||||
output[kkk] += outputTmp[kkk];
|
||||
}
|
||||
}
|
||||
RIVER_VERBOSE(" End stack process data ...");
|
||||
m_process.processIn(&outputTmp2[0], _nbChunk, _outputBuffer, _nbChunk);
|
||||
RIVER_VERBOSE(" Feedback :");
|
||||
for (auto &it : m_list) {
|
||||
if (it == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (it->getMode() != river::modeInterface_feedback) {
|
||||
continue;
|
||||
}
|
||||
RIVER_VERBOSE(" IO name="<< it->getName() << " (feedback)");
|
||||
it->systemNewInputData(_time, _outputBuffer, _nbChunk);
|
||||
}
|
||||
RIVER_VERBOSE("data Output size request :" << _nbChunk << " [ END ]");
|
||||
return 0;
|
||||
}
|
||||
|
@ -25,46 +25,22 @@
|
||||
namespace river {
|
||||
namespace io {
|
||||
class Manager;
|
||||
class Node {
|
||||
private:
|
||||
mutable std::mutex m_mutex;
|
||||
std::shared_ptr<const ejson::Object> m_config;
|
||||
std::shared_ptr<drain::VolumeElement> m_volume; //!< if a volume is set it is set here ...
|
||||
private:
|
||||
class Node : public std::enable_shared_from_this<Node> {
|
||||
protected:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
Node(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config);
|
||||
public:
|
||||
static std::shared_ptr<Node> create(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config);
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~Node();
|
||||
private:
|
||||
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;
|
||||
public:
|
||||
void registerAsRemote(const std::shared_ptr<river::Interface>& _interface);
|
||||
void interfaceAdd(const std::shared_ptr<river::Interface>& _interface);
|
||||
void interfaceRemove(const std::shared_ptr<river::Interface>& _interface);
|
||||
private:
|
||||
airtaudio::Interface m_adac; //!< Real audio interface
|
||||
airtaudio::DeviceInfo m_info;
|
||||
unsigned int m_rtaudioFrameSize;
|
||||
public:
|
||||
int32_t airtAudioCallback(void* _outputBuffer,
|
||||
void * _inputBuffer,
|
||||
uint32_t _nbChunk,
|
||||
const std::chrono::system_clock::time_point& _time,
|
||||
airtaudio::status _status);
|
||||
private:
|
||||
std::string m_name; //!< Harware.json configuration name
|
||||
public:
|
||||
const std::string& getName() {
|
||||
return m_name;
|
||||
}
|
||||
private:
|
||||
|
||||
protected:
|
||||
mutable std::mutex m_mutex;
|
||||
std::shared_ptr<const ejson::Object> m_config;
|
||||
protected:
|
||||
drain::Process m_process;
|
||||
public:
|
||||
const drain::IOFormatInterface& getInterfaceFormat() {
|
||||
@ -74,7 +50,32 @@ namespace river {
|
||||
return m_process.getInputConfig();
|
||||
}
|
||||
}
|
||||
private:
|
||||
protected:
|
||||
const drain::IOFormatInterface& getHarwareFormat() {
|
||||
if (m_isInput == true) {
|
||||
return m_process.getInputConfig();
|
||||
} else {
|
||||
return m_process.getOutputConfig();
|
||||
}
|
||||
}
|
||||
protected:
|
||||
|
||||
std::shared_ptr<drain::VolumeElement> m_volume; //!< if a volume is set it is set here ...
|
||||
|
||||
protected:
|
||||
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;
|
||||
public:
|
||||
void registerAsRemote(const std::shared_ptr<river::Interface>& _interface);
|
||||
void interfaceAdd(const std::shared_ptr<river::Interface>& _interface);
|
||||
void interfaceRemove(const std::shared_ptr<river::Interface>& _interface);
|
||||
protected:
|
||||
std::string m_name; //!< Harware.json configuration name
|
||||
public:
|
||||
const std::string& getName() {
|
||||
return m_name;
|
||||
}
|
||||
protected:
|
||||
bool m_isInput;
|
||||
public:
|
||||
bool isInput() {
|
||||
@ -83,15 +84,22 @@ namespace river {
|
||||
bool isOutput() {
|
||||
return !m_isInput;
|
||||
}
|
||||
private:
|
||||
void start();
|
||||
void stop();
|
||||
protected:
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
public:
|
||||
const std::shared_ptr<drain::VolumeElement>& getVolume() {
|
||||
return m_volume;
|
||||
}
|
||||
public:
|
||||
void volumeChange();
|
||||
protected:
|
||||
int32_t newInput(const void* _inputBuffer,
|
||||
uint32_t _nbChunk,
|
||||
const std::chrono::system_clock::time_point& _time);
|
||||
int32_t newOutput(void* _outputBuffer,
|
||||
uint32_t _nbChunk,
|
||||
const std::chrono::system_clock::time_point& _time);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
254
river/io/NodeAEC.cpp
Normal file
254
river/io/NodeAEC.cpp
Normal file
@ -0,0 +1,254 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <river/io/NodeAEC.h>
|
||||
#include <river/debug.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#undef __class__
|
||||
#define __class__ "io::NodeAEC"
|
||||
|
||||
#if 0
|
||||
int32_t river::io::NodeAEC::airtAudioCallback(void* _outputBuffer,
|
||||
void* _inputBuffer,
|
||||
uint32_t _nbChunk,
|
||||
const std::chrono::system_clock::time_point& _time,
|
||||
airtaudio::status _status) {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
//RIVER_INFO("Time=" << _time);
|
||||
/*
|
||||
for (int32_t iii=0; iii<400; ++iii) {
|
||||
RIVER_VERBOSE("dummy=" << uint64_t(dummy[iii]));
|
||||
}
|
||||
*/
|
||||
if (_outputBuffer != nullptr) {
|
||||
RIVER_VERBOSE("data Output size request :" << _nbChunk << " [BEGIN] status=" << _status << " nbIO=" << m_list.size());
|
||||
std::vector<int32_t> output;
|
||||
RIVER_VERBOSE("resize=" << _nbChunk*m_process.getInputConfig().getMap().size());
|
||||
output.resize(_nbChunk*m_process.getInputConfig().getMap().size(), 0);
|
||||
const int32_t* outputTmp = nullptr;
|
||||
std::vector<uint8_t> outputTmp2;
|
||||
RIVER_VERBOSE("resize=" << sizeof(int32_t)*m_process.getInputConfig().getMap().size()*_nbChunk);
|
||||
outputTmp2.resize(sizeof(int32_t)*m_process.getInputConfig().getMap().size()*_nbChunk, 0);
|
||||
for (auto &it : m_list) {
|
||||
if (it == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (it->getMode() != river::modeInterface_output) {
|
||||
continue;
|
||||
}
|
||||
RIVER_VERBOSE(" IO name="<< it->getName());
|
||||
// clear datas ...
|
||||
memset(&outputTmp2[0], 0, sizeof(int32_t)*m_process.getInputConfig().getMap().size()*_nbChunk);
|
||||
RIVER_VERBOSE(" request Data="<< _nbChunk);
|
||||
it->systemNeedOutputData(_time, &outputTmp2[0], _nbChunk, sizeof(int32_t)*m_process.getInputConfig().getMap().size());
|
||||
RIVER_VERBOSE(" Mix it ...");
|
||||
outputTmp = reinterpret_cast<const int32_t*>(&outputTmp2[0]);
|
||||
// Add data to the output tmp buffer :
|
||||
for (size_t kkk=0; kkk<output.size(); ++kkk) {
|
||||
output[kkk] += outputTmp[kkk];
|
||||
}
|
||||
}
|
||||
RIVER_VERBOSE(" End stack process data ...");
|
||||
m_process.processIn(&outputTmp2[0], _nbChunk, _outputBuffer, _nbChunk);
|
||||
RIVER_VERBOSE(" Feedback :");
|
||||
for (auto &it : m_list) {
|
||||
if (it == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (it->getMode() != river::modeInterface_feedback) {
|
||||
continue;
|
||||
}
|
||||
RIVER_VERBOSE(" IO name="<< it->getName() << " (feedback)");
|
||||
it->systemNewInputData(_time, _outputBuffer, _nbChunk);
|
||||
}
|
||||
RIVER_VERBOSE("data Output size request :" << _nbChunk << " [ END ]");
|
||||
}
|
||||
if (_inputBuffer != nullptr) {
|
||||
RIVER_VERBOSE("data Input size request :" << _nbChunk << " [BEGIN] status=" << _status << " nbIO=" << m_list.size());
|
||||
int16_t* inputBuffer = static_cast<int16_t *>(_inputBuffer);
|
||||
for (auto &it : m_list) {
|
||||
if (it == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (it->getMode() != river::modeInterface_input) {
|
||||
continue;
|
||||
}
|
||||
RIVER_INFO(" IO name="<< it->getName());
|
||||
it->systemNewInputData(_time, inputBuffer, _nbChunk);
|
||||
}
|
||||
RIVER_VERBOSE("data Input size request :" << _nbChunk << " [ END ]");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::shared_ptr<river::io::NodeAEC> river::io::NodeAEC::create(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config) {
|
||||
return std::shared_ptr<river::io::NodeAEC>(new river::io::NodeAEC(_name, _config));
|
||||
}
|
||||
|
||||
std::shared_ptr<river::Interface> river::io::NodeAEC::createInput(float _freq,
|
||||
const std::vector<audio::channel>& _map,
|
||||
audio::format _format,
|
||||
const std::string& _objectName,
|
||||
const std::string& _name) {
|
||||
// check if the output exist
|
||||
const std::shared_ptr<const ejson::Object> tmppp = m_config->getObject(_objectName);
|
||||
if (tmppp == nullptr) {
|
||||
RIVER_ERROR("can not open a non existance virtual interface: '" << _objectName << "' not present in : " << m_config->getKeys());
|
||||
return nullptr;
|
||||
}
|
||||
std::string streamName = tmppp->getStringValue("map-on", "error");
|
||||
|
||||
|
||||
// check if it is an Output:
|
||||
std::string type = tmppp->getStringValue("io", "error");
|
||||
if ( type != "input"
|
||||
&& type != "feedback") {
|
||||
RIVER_ERROR("can not open in output a virtual interface: '" << streamName << "' configured has : " << type);
|
||||
return nullptr;
|
||||
}
|
||||
// get global hardware interface:
|
||||
std::shared_ptr<river::io::Manager> manager = river::io::Manager::getInstance();
|
||||
// get the output or input channel :
|
||||
std::shared_ptr<river::io::Node> node = manager->getNode(streamName);
|
||||
// create user iterface:
|
||||
std::shared_ptr<river::Interface> interface;
|
||||
interface = river::Interface::create(_name, _freq, _map, _format, node, tmppp);
|
||||
return interface;
|
||||
}
|
||||
|
||||
|
||||
river::io::NodeAEC::NodeAEC(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config) :
|
||||
Node(_name, _config) {
|
||||
drain::IOFormatInterface interfaceFormat = getInterfaceFormat();
|
||||
drain::IOFormatInterface hardwareFormat = getHarwareFormat();
|
||||
/**
|
||||
# connect in input mode
|
||||
map-on-microphone:{
|
||||
# generic virtual definition
|
||||
io:"input",
|
||||
map-on:"microphone",
|
||||
resampling-type:"speexdsp",
|
||||
resampling-option:"quality=10"
|
||||
},
|
||||
# connect in feedback mode
|
||||
map-on-feedback:{
|
||||
io:"feedback",
|
||||
map-on:"speaker",
|
||||
resampling-type:"speexdsp",
|
||||
resampling-option:"quality=10",
|
||||
},
|
||||
# AEC algo definition
|
||||
algo:"river-remover",
|
||||
algo-mode:"cutter",
|
||||
*/
|
||||
std::vector<audio::channel> feedbackMap;
|
||||
feedbackMap.push_back(audio::channel_frontCenter);
|
||||
m_interfaceFeedBack = createInput(hardwareFormat.getFrequency(),
|
||||
feedbackMap,
|
||||
hardwareFormat.getFormat(),
|
||||
"map-on-feedback",
|
||||
_name + "-AEC-feedback");
|
||||
if (m_interfaceFeedBack == nullptr) {
|
||||
RIVER_ERROR("Can not opne virtual device ... map-on-feedback in " << _name);
|
||||
return;
|
||||
}
|
||||
m_interfaceMicrophone = createInput(hardwareFormat.getFrequency(),
|
||||
hardwareFormat.getMap(),
|
||||
hardwareFormat.getFormat(),
|
||||
"map-on-microphone",
|
||||
_name + "-AEC-microphone");
|
||||
if (m_interfaceMicrophone == nullptr) {
|
||||
RIVER_ERROR("Can not opne virtual device ... map-on-microphone in " << _name);
|
||||
return;
|
||||
}
|
||||
|
||||
// set callback mode ...
|
||||
m_interfaceFeedBack->setInputCallback(1024,
|
||||
std::bind(&river::io::NodeAEC::onDataReceivedFeedBack,
|
||||
this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2,
|
||||
std::placeholders::_3,
|
||||
std::placeholders::_4,
|
||||
std::placeholders::_5));
|
||||
// set callback mode ...
|
||||
m_interfaceMicrophone->setInputCallback(1024,
|
||||
std::bind(&river::io::NodeAEC::onDataReceivedMicrophone,
|
||||
this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2,
|
||||
std::placeholders::_3,
|
||||
std::placeholders::_4,
|
||||
std::placeholders::_5));
|
||||
|
||||
m_process.updateInterAlgo();
|
||||
}
|
||||
|
||||
river::io::NodeAEC::~NodeAEC() {
|
||||
RIVER_INFO("close input stream");
|
||||
stop();
|
||||
m_interfaceFeedBack.reset();
|
||||
m_interfaceMicrophone.reset();
|
||||
};
|
||||
|
||||
void river::io::NodeAEC::start() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
RIVER_INFO("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
|
||||
if (m_interfaceFeedBack != nullptr) {
|
||||
m_interfaceFeedBack->start();
|
||||
}
|
||||
if (m_interfaceMicrophone != nullptr) {
|
||||
m_interfaceMicrophone->start();
|
||||
}
|
||||
}
|
||||
|
||||
void river::io::NodeAEC::stop() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
if (m_interfaceFeedBack != nullptr) {
|
||||
m_interfaceFeedBack->stop();
|
||||
}
|
||||
if (m_interfaceMicrophone != nullptr) {
|
||||
m_interfaceMicrophone->stop();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void river::io::NodeAEC::onDataReceivedMicrophone(const std::chrono::system_clock::time_point& _time,
|
||||
size_t _nbChunk,
|
||||
const std::vector<audio::channel>& _map,
|
||||
const void* _data,
|
||||
enum audio::format _type) {
|
||||
RIVER_INFO("Microphone Time=" << _time << " _nbChunk=" << _nbChunk << " _map=" << _map << " _type=" << _type);
|
||||
if (_type != audio::format_int16) {
|
||||
RIVER_ERROR("call wrong type ... (need int16_t)");
|
||||
}
|
||||
// push data synchronize
|
||||
|
||||
// if threaded : send event / otherwise, process ...
|
||||
newInput(_data, _nbChunk, _time);
|
||||
}
|
||||
|
||||
void river::io::NodeAEC::onDataReceivedFeedBack(const std::chrono::system_clock::time_point& _time,
|
||||
size_t _nbChunk,
|
||||
const std::vector<audio::channel>& _map,
|
||||
const void* _data,
|
||||
enum audio::format _type) {
|
||||
RIVER_INFO("FeedBack Time=" << _time << " _nbChunk=" << _nbChunk << " _map=" << _map << " _type=" << _type);
|
||||
if (_type != audio::format_int16) {
|
||||
RIVER_ERROR("call wrong type ... (need int16_t)");
|
||||
}
|
||||
// TODO : Call synchro ...
|
||||
}
|
54
river/io/NodeAEC.h
Normal file
54
river/io/NodeAEC.h
Normal file
@ -0,0 +1,54 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __RIVER_IO_NODE_AEC_H__
|
||||
#define __RIVER_IO_NODE_AEC_H__
|
||||
|
||||
#include <river/io/Node.h>
|
||||
#include <river/Interface.h>
|
||||
|
||||
namespace river {
|
||||
namespace io {
|
||||
class Manager;
|
||||
class NodeAEC : public Node {
|
||||
protected:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
NodeAEC(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config);
|
||||
public:
|
||||
static std::shared_ptr<NodeAEC> create(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config);
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~NodeAEC();
|
||||
protected:
|
||||
virtual void start();
|
||||
virtual void stop();
|
||||
std::shared_ptr<river::Interface> m_interfaceMicrophone;
|
||||
std::shared_ptr<river::Interface> m_interfaceFeedBack;
|
||||
std::shared_ptr<river::Interface> createInput(float _freq,
|
||||
const std::vector<audio::channel>& _map,
|
||||
audio::format _format,
|
||||
const std::string& _streamName,
|
||||
const std::string& _name);
|
||||
void onDataReceivedMicrophone(const std::chrono::system_clock::time_point& _playTime,
|
||||
size_t _nbChunk,
|
||||
const std::vector<audio::channel>& _map,
|
||||
const void* _data,
|
||||
enum audio::format _type);
|
||||
|
||||
void onDataReceivedFeedBack(const std::chrono::system_clock::time_point& _readTime,
|
||||
size_t _nbChunk,
|
||||
const std::vector<audio::channel>& _map,
|
||||
const void* _data,
|
||||
enum audio::format _type);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
239
river/io/NodeAirTAudio.cpp
Normal file
239
river/io/NodeAirTAudio.cpp
Normal file
@ -0,0 +1,239 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <river/io/NodeAirTAudio.h>
|
||||
#include <river/debug.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#undef __class__
|
||||
#define __class__ "io::NodeAirTAudio"
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int32_t river::io::NodeAirTAudio::airtAudioCallback(void* _outputBuffer,
|
||||
void* _inputBuffer,
|
||||
uint32_t _nbChunk,
|
||||
const std::chrono::system_clock::time_point& _time,
|
||||
airtaudio::status _status) {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
if (_outputBuffer != nullptr) {
|
||||
RIVER_VERBOSE("data Output size request :" << _nbChunk << " [BEGIN] status=" << _status << " nbIO=" << m_list.size());
|
||||
newOutput(_outputBuffer, _nbChunk, _time);
|
||||
}
|
||||
if (_inputBuffer != nullptr) {
|
||||
RIVER_VERBOSE("data Input size request :" << _nbChunk << " [BEGIN] status=" << _status << " nbIO=" << m_list.size());
|
||||
newInput(_inputBuffer, _nbChunk, _time);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<river::io::NodeAirTAudio> river::io::NodeAirTAudio::create(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config) {
|
||||
return std::shared_ptr<river::io::NodeAirTAudio>(new river::io::NodeAirTAudio(_name, _config));
|
||||
}
|
||||
|
||||
river::io::NodeAirTAudio::NodeAirTAudio(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);
|
||||
|
||||
// intanciate specific API ...
|
||||
m_adac.instanciate(typeInterface);
|
||||
// TODO : Check return ...
|
||||
std::string type = m_config->getStringValue("type", "int16");
|
||||
if (streamName == "") {
|
||||
streamName = "default";
|
||||
}
|
||||
|
||||
// search device ID :
|
||||
RIVER_INFO("Open :");
|
||||
RIVER_INFO(" m_streamName=" << streamName);
|
||||
RIVER_INFO(" m_freq=" << hardwareFormat.getFrequency());
|
||||
RIVER_INFO(" m_map=" << hardwareFormat.getMap());
|
||||
RIVER_INFO(" m_format=" << hardwareFormat.getFormat());
|
||||
RIVER_INFO(" m_isInput=" << m_isInput);
|
||||
int32_t deviceId = 0;
|
||||
RIVER_INFO("Device list:");
|
||||
for (int32_t iii=0; iii<m_adac.getDeviceCount(); ++iii) {
|
||||
m_info = m_adac.getDeviceInfo(iii);
|
||||
RIVER_INFO(" " << iii << " name :" << m_info.name);
|
||||
if (m_info.name == streamName) {
|
||||
RIVER_INFO(" Select ...");
|
||||
deviceId = iii;
|
||||
}
|
||||
}
|
||||
// Open specific ID :
|
||||
m_info = m_adac.getDeviceInfo(deviceId);
|
||||
// display property :
|
||||
{
|
||||
RIVER_INFO("Device " << deviceId << " property :");
|
||||
RIVER_INFO(" probe=" << m_info.probed);
|
||||
RIVER_INFO(" name=" << m_info.name);
|
||||
RIVER_INFO(" outputChannels=" << m_info.outputChannels);
|
||||
RIVER_INFO(" inputChannels=" << m_info.inputChannels);
|
||||
RIVER_INFO(" duplexChannels=" << m_info.duplexChannels);
|
||||
RIVER_INFO(" isDefaultOutput=" << m_info.isDefaultOutput);
|
||||
RIVER_INFO(" isDefaultInput=" << m_info.isDefaultInput);
|
||||
RIVER_INFO(" rates=" << m_info.sampleRates);
|
||||
RIVER_INFO(" native Format: " << m_info.nativeFormats);
|
||||
|
||||
if (etk::isIn(hardwareFormat.getFormat(), m_info.nativeFormats) == false) {
|
||||
if (type == "auto") {
|
||||
if (etk::isIn(audio::format_int16, m_info.nativeFormats) == true) {
|
||||
hardwareFormat.setFormat(audio::format_int16);
|
||||
RIVER_INFO("auto set format: " << hardwareFormat.getFormat());
|
||||
} else if (etk::isIn(audio::format_float, m_info.nativeFormats) == true) {
|
||||
hardwareFormat.setFormat(audio::format_float);
|
||||
RIVER_INFO("auto set format: " << hardwareFormat.getFormat());
|
||||
} else if (etk::isIn(audio::format_int16_on_int32, m_info.nativeFormats) == true) {
|
||||
hardwareFormat.setFormat(audio::format_int16_on_int32);
|
||||
RIVER_INFO("auto set format: " << hardwareFormat.getFormat());
|
||||
} else if (etk::isIn(audio::format_int24, m_info.nativeFormats) == true) {
|
||||
hardwareFormat.setFormat(audio::format_int24);
|
||||
RIVER_INFO("auto set format: " << hardwareFormat.getFormat());
|
||||
} else if (m_info.nativeFormats.size() != 0) {
|
||||
hardwareFormat.setFormat(m_info.nativeFormats[0]);
|
||||
RIVER_INFO("auto set format: " << hardwareFormat.getFormat());
|
||||
} else {
|
||||
RIVER_CRITICAL("auto set format no element in the configuration: " << m_info.nativeFormats);
|
||||
}
|
||||
} else {
|
||||
RIVER_CRITICAL("Can not manage input transforamtion: " << hardwareFormat.getFormat() << " not in " << m_info.nativeFormats);
|
||||
}
|
||||
}
|
||||
if (etk::isIn(hardwareFormat.getFrequency(), m_info.sampleRates) == false) {
|
||||
if (etk::isIn(48000, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(48000);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (etk::isIn(44100, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(44100);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (etk::isIn(32000, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(32000);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (etk::isIn(16000, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(16000);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (etk::isIn(8000, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(8000);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (etk::isIn(96000, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(96000);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (m_info.sampleRates.size() != 0) {
|
||||
hardwareFormat.setFrequency(m_info.sampleRates[0]);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency() << "(first element in list) in " << m_info.sampleRates);
|
||||
} else {
|
||||
RIVER_CRITICAL("Can not manage input transforamtion:" << hardwareFormat.getFrequency() << " not in " << m_info.sampleRates);
|
||||
}
|
||||
interfaceFormat.setFrequency(hardwareFormat.getFrequency());
|
||||
}
|
||||
}
|
||||
|
||||
// open Audio device:
|
||||
airtaudio::StreamParameters params;
|
||||
params.deviceId = deviceId;
|
||||
if (m_isInput == true) {
|
||||
m_info.inputChannels = 2;
|
||||
params.nChannels = 2;
|
||||
} else {
|
||||
m_info.outputChannels = 2;
|
||||
params.nChannels = 2;
|
||||
}
|
||||
|
||||
m_rtaudioFrameSize = nbChunk;
|
||||
RIVER_INFO("Open output stream nbChannels=" << params.nChannels);
|
||||
enum airtaudio::error err = airtaudio::error_none;
|
||||
if (m_isInput == true) {
|
||||
err = m_adac.openStream(nullptr, ¶ms,
|
||||
hardwareFormat.getFormat(), hardwareFormat.getFrequency(), &m_rtaudioFrameSize,
|
||||
std::bind(&river::io::NodeAirTAudio::airtAudioCallback,
|
||||
this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2,
|
||||
std::placeholders::_3,
|
||||
std::placeholders::_4,
|
||||
std::placeholders::_5)
|
||||
);
|
||||
} else {
|
||||
err = m_adac.openStream(¶ms, nullptr,
|
||||
hardwareFormat.getFormat(), hardwareFormat.getFrequency(), &m_rtaudioFrameSize,
|
||||
std::bind(&river::io::NodeAirTAudio::airtAudioCallback,
|
||||
this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2,
|
||||
std::placeholders::_3,
|
||||
std::placeholders::_4,
|
||||
std::placeholders::_5)
|
||||
);
|
||||
}
|
||||
if (err != airtaudio::error_none) {
|
||||
RIVER_ERROR("Create stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not create stream " << err);
|
||||
}
|
||||
m_process.updateInterAlgo();
|
||||
}
|
||||
|
||||
river::io::NodeAirTAudio::~NodeAirTAudio() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
RIVER_INFO("close input stream");
|
||||
if (m_adac.isStreamOpen() ) {
|
||||
m_adac.closeStream();
|
||||
}
|
||||
};
|
||||
|
||||
void river::io::NodeAirTAudio::start() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
RIVER_INFO("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
|
||||
enum airtaudio::error err = m_adac.startStream();
|
||||
if (err != airtaudio::error_none) {
|
||||
RIVER_ERROR("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not start stream ... " << err);
|
||||
}
|
||||
}
|
||||
|
||||
void river::io::NodeAirTAudio::stop() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
RIVER_INFO("Stop stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
|
||||
enum airtaudio::error err = m_adac.stopStream();
|
||||
if (err != airtaudio::error_none) {
|
||||
RIVER_ERROR("Stop stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not stop stream ... " << err);
|
||||
}
|
||||
}
|
45
river/io/NodeAirTAudio.h
Normal file
45
river/io/NodeAirTAudio.h
Normal file
@ -0,0 +1,45 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __RIVER_IO_NODE_AIRTAUDIO_H__
|
||||
#define __RIVER_IO_NODE_AIRTAUDIO_H__
|
||||
|
||||
#include <river/io/Node.h>
|
||||
|
||||
namespace river {
|
||||
namespace io {
|
||||
class Manager;
|
||||
class NodeAirTAudio : public Node {
|
||||
protected:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
NodeAirTAudio(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config);
|
||||
public:
|
||||
static std::shared_ptr<NodeAirTAudio> create(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config);
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~NodeAirTAudio();
|
||||
protected:
|
||||
airtaudio::Interface m_adac; //!< Real audio interface
|
||||
airtaudio::DeviceInfo m_info;
|
||||
unsigned int m_rtaudioFrameSize;
|
||||
public:
|
||||
int32_t airtAudioCallback(void* _outputBuffer,
|
||||
void * _inputBuffer,
|
||||
uint32_t _nbChunk,
|
||||
const std::chrono::system_clock::time_point& _time,
|
||||
airtaudio::status _status);
|
||||
protected:
|
||||
virtual void start();
|
||||
virtual void stop();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -292,7 +292,7 @@ class testInCallback {
|
||||
std::shared_ptr<river::Interface> m_interface;
|
||||
double m_phase;
|
||||
public:
|
||||
testInCallback(std::shared_ptr<river::Manager> _manager) :
|
||||
testInCallback(std::shared_ptr<river::Manager> _manager, const std::string& _input="microphone") :
|
||||
m_manager(_manager),
|
||||
m_phase(0) {
|
||||
//Set stereo output:
|
||||
@ -302,7 +302,7 @@ class testInCallback {
|
||||
m_interface = m_manager->createInput(48000,
|
||||
channelMap,
|
||||
audio::format_int16,
|
||||
"microphone",
|
||||
_input,
|
||||
"WriteModeCallback");
|
||||
// set callback mode ...
|
||||
m_interface->setInputCallback(1024,
|
||||
@ -347,6 +347,16 @@ TEST(TestALL, testInputCallBack) {
|
||||
process.reset();
|
||||
usleep(500000);
|
||||
}
|
||||
TEST(TestALL, testInputCallBackMicClean) {
|
||||
std::shared_ptr<river::Manager> manager;
|
||||
manager = river::Manager::create("testApplication");
|
||||
APPL_INFO("test input (callback mode)");
|
||||
std::shared_ptr<testInCallback> process = std::make_shared<testInCallback>(manager, "microphone-clean");
|
||||
process->run();
|
||||
process.reset();
|
||||
usleep(500000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
class testOutCallbackType {
|
||||
|
Loading…
Reference in New Issue
Block a user