30 Commits

Author SHA1 Message Date
771aae6469 [CI] travis orchestra missing C 2015-06-15 22:17:17 +02:00
9b8351e489 [DEV] update new worktree 2015-06-15 19:36:08 +02:00
50dcfaff82 [DEV] update new orchestra modification 2015-06-14 18:33:24 +02:00
63fb66d045 [DEV] some basic update 2015-06-11 22:17:17 +02:00
2071cf1c46 [DEV] add capacity of example 2015-05-12 21:49:16 +02:00
f2f57d4081 [BUILD] update new lutin 0.5.3 2015-05-08 22:36:02 +02:00
4ba07ac087 [DEV] add log print 2015-04-17 21:49:52 +02:00
f1d937e173 [BUILD] correct cakin build 2015-04-14 22:01:37 +02:00
be5489bc73 [DEV] update Catkin 2015-04-14 21:53:14 +02:00
41f55ab83d [DEV] update audio time interface 2015-04-13 21:49:48 +02:00
620fb8e54f [DEV] update the input output work 2015-04-11 17:43:09 +02:00
8daa32a2f7 [DEBUG] build error in portaudio 2015-04-11 13:21:38 +02:00
26e71b2a92 [DEV] rename lib river in audio::river 2015-04-10 00:07:09 +02:00
bf05f46f24 [STYLE] update 2015-03-26 23:35:43 +01:00
6622cd8237 [DEV] update Log interafce 2015-03-25 21:14:12 +01:00
13043536c2 [DEV] integrate echo cutter 2015-03-24 21:29:06 +01:00
58da3cf125 [DEV] work on the stream of AEC 2015-03-23 22:46:21 +01:00
3f6d736454 [DOC] start doc might be OK 2015-03-23 21:26:45 +01:00
1c34c157ab [DOC] Start riting tutorials and basic documentation. 2015-03-22 20:44:37 +01:00
3891b0ac52 [DOC] write nealy all documentation on the class 2015-03-22 09:29:21 +01:00
4c3fe665cf [DEBUG] correct segfault 2015-03-20 21:45:30 +01:00
ae0aa6aa6c [DEV] replace NULL => nullptr 2015-03-19 21:55:55 +01:00
967b2386bd [BUILD] set catkin etk correct define 2015-03-18 23:43:06 +01:00
d272410836 [DEV] Correct mixer of multiple flow 2015-03-18 21:48:57 +01:00
ee126927cd [DEV] mode Circular buffer in Drain lib 2015-03-16 21:03:23 +01:00
08ae045bf0 [DEV] correct the test and catkin build 2015-03-13 23:54:54 +01:00
0d448bed76 [DEV] rework test with new API 2015-03-13 23:22:17 +01:00
57c4795a1e [DEBUG] compilation OK 2015-03-12 23:17:14 +01:00
f9ae8f23bd [DEV] some River rework 2015-03-12 22:28:15 +01:00
ec4eae4242 [DEV] add basic catkin 2015-03-12 21:33:44 +01:00
73 changed files with 4625 additions and 2888 deletions

44
.travis.yml Normal file
View File

@@ -0,0 +1,44 @@
language: cpp
compiler:
- clang
- gcc
os:
- linux
- osx
before_script:
- cd ..
- git clone https://github.com/generic-library/gtest-lutin.git --recurse-submodules
- git clone https://github.com/generic-library/speex-dsp-lutin.git --recurse-submodules
- git clone https://github.com/generic-library/z-lutin.git --recurse-submodules
- git clone https://github.com/generic-library/portaudio-lutin.git --recurse-submodules
- git clone https://github.com/atria-soft/etk.git
- git clone https://github.com/atria-soft/ejson.git
- git clone https://github.com/musicdsp/audio.git
- git clone https://github.com/musicdsp/audio-algo-drain.git
- git clone https://github.com/musicdsp/audio-drain.git
- git clone https://github.com/musicdsp/audio-orchestra.git
- pwd
- ls -l
- if [ "$CXX" == "clang++" ]; then BUILDER=clang; else BUILDER=gcc; fi
install:
- sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
- sudo apt-get update -qq
- sudo apt-get install -qq g++-4.9
- sudo apt-get install -qq libstdc++-4.9-dev
- sudo rm /usr/bin/gcc /usr/bin/g++
- sudo ln -s /usr/bin/gcc-4.9 /usr/bin/gcc
- sudo ln -s /usr/bin/g++-4.9 /usr/bin/g++
- sudo pip install lutin
script:
- lutin -C -P -c$BUILDER -mdebug -p audio-river-test
- ./out/Linux_x86_64/debug/staging/$BUILDER/audio-river-test/usr/bin/audio-river-test -l6
notifications:
email:
- yui.heero@gmail.com

36
Doxyfile Normal file
View File

@@ -0,0 +1,36 @@
PROJECT_NAME = river
PROJECT_NUMBER = latest
OUTPUT_DIRECTORY = dox/
SORT_MEMBERS_CTORS_1ST = YES
SORT_GROUP_NAMES = YES
SORT_BY_SCOPE_NAME = YES
SHOW_NAMESPACES = YES
RECURSIVE = YES
EXTRACT_ALL = NO
EXCLUDE_PATTERNS = */examples/*
SOURCE_BROWSER = NO
ALPHABETICAL_INDEX = NO
GENERATE_HTML = YES
#HTML_HEADER = /home/edupin/work/master-2015-02-04/doc/templates/doxygen/header.html
#HTML_FOOTER = /home/edupin/work/master-2015-02-04/doc/templates/doxygen/footer.html
#HTML_STYLESHEET = /home/edupin/work/master-2015-02-04/doc/templates/doxygen/doxygen.css
GENERATE_TREEVIEW = YES
CLASS_DIAGRAMS = YES
HAVE_DOT = YES
CLASS_GRAPH = YES
COLLABORATION_GRAPH = NO
INCLUDE_GRAPH = NO
INCLUDED_BY_GRAPH = NO
GRAPHICAL_HIERARCHY = NO
DOT_MULTI_TARGETS = YES
INPUT = river/
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
SKIP_FUNCTION_MACROS = YES
BRIEF_MEMBER_DESC = NO
VERBATIM_HEADERS = NO
TAB_SIZE = 4
#EXAMPLE_PATH = sample/
GENERATE_XML = YES
GENERATE_LATEX = NO
GENERATE_TAGFILE = dox/ruver-doc.tag

524
audio/river/Interface.cpp Normal file
View File

@@ -0,0 +1,524 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include "debug.h"
#include "Interface.h"
#include "io/Node.h"
#include <audio/drain/EndPointCallback.h>
#include <audio/drain/EndPointWrite.h>
#include <audio/drain/EndPointRead.h>
#include <audio/drain/Volume.h>
#undef __class__
#define __class__ "Interface"
audio::river::Interface::Interface(void) :
m_node(),
m_name("") {
static uint32_t uid = 0;
m_uid = uid++;
}
bool audio::river::Interface::init(float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std11::shared_ptr<audio::river::io::Node>& _node,
const std11::shared_ptr<const ejson::Object>& _config) {
std::vector<audio::channel> map(_map);
m_node = _node;
m_config = _config;
m_mode = audio::river::modeInterface_unknow;
std::string type = m_config->getStringValue("io", "error");
static int32_t uid=0;
m_name = _node->getName() + "__" + (_node->isInput()==true?"input":"output") + "__" + type + "__" + etk::to_string(uid++);
if (type == "output") {
m_mode = audio::river::modeInterface_output;
} else if (type == "input") {
m_mode = audio::river::modeInterface_input;
} else if (type == "feedback") {
m_mode = audio::river::modeInterface_feedback;
}
// register interface to be notify from the volume change.
m_node->registerAsRemote(shared_from_this());
if (map.size() == 0) {
RIVER_INFO("Select auto map system ...");
map = m_node->getInterfaceFormat().getMap();
RIVER_INFO(" ==> " << map);
}
// Create convertion interface
if ( m_node->isInput() == true
&& m_mode == audio::river::modeInterface_input) {
m_process.setInputConfig(m_node->getInterfaceFormat());
// Add volume only if the Low level has a volume (otherwise it will be added by the application)
std11::shared_ptr<audio::drain::VolumeElement> tmpVolume = m_node->getVolume();
if (tmpVolume != nullptr) {
// add all time the volume stage :
std11::shared_ptr<audio::drain::Volume> algo = audio::drain::Volume::create();
//algo->setInputFormat(m_node->getInterfaceFormat());
algo->setName("volume");
m_process.pushBack(algo);
RIVER_INFO(" add volume for node");
algo->addVolumeStage(tmpVolume);
}
m_process.setOutputConfig(audio::drain::IOFormatInterface(map, _format, _freq));
} else if ( m_node->isOutput() == true
&& m_mode == audio::river::modeInterface_output) {
m_process.setInputConfig(audio::drain::IOFormatInterface(map, _format, _freq));
// Add volume only if the Low level has a volume (otherwise it will be added by the application)
std11::shared_ptr<audio::drain::VolumeElement> tmpVolume = m_node->getVolume();
if (tmpVolume != nullptr) {
// add all time the volume stage :
std11::shared_ptr<audio::drain::Volume> algo = audio::drain::Volume::create();
//algo->setOutputFormat(m_node->getInterfaceFormat());
algo->setName("volume");
m_process.pushBack(algo);
RIVER_INFO(" add volume for node");
algo->addVolumeStage(tmpVolume);
}
m_process.setOutputConfig(m_node->getInterfaceFormat());
} else if ( m_node->isOutput() == true
&& m_mode == audio::river::modeInterface_feedback) {
m_process.setInputConfig(m_node->getHarwareFormat());
// note : feedback has no volume stage ...
m_process.setOutputConfig(audio::drain::IOFormatInterface(map, _format, _freq));
} else {
RIVER_ERROR("Can not link virtual interface with type : " << m_mode << " to a hardware interface " << (m_node->isInput()==true?"input":"output"));
return false;
}
return true;
}
std11::shared_ptr<audio::river::Interface> audio::river::Interface::create(float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std11::shared_ptr<audio::river::io::Node>& _node,
const std11::shared_ptr<const ejson::Object>& _config) {
std11::shared_ptr<audio::river::Interface> out = std11::shared_ptr<audio::river::Interface>(new audio::river::Interface());
out->init(_freq, _map, _format, _node, _config);
return out;
}
audio::river::Interface::~Interface() {
//stop(true, true);
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
//m_node->interfaceRemove(shared_from_this());
}
/*
bool audio::river::Interface::hasEndPoint() {
}
*/
void audio::river::Interface::setReadwrite() {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
m_process.removeAlgoDynamic();
if (m_process.hasType<audio::drain::EndPoint>() ) {
RIVER_ERROR("Endpoint is already present ==> can not change");
return;
}
if (m_node->isInput() == true) {
m_process.removeIfLast<audio::drain::EndPoint>();
std11::shared_ptr<audio::drain::EndPointRead> algo = audio::drain::EndPointRead::create();
m_process.pushBack(algo);
} else {
m_process.removeIfFirst<audio::drain::EndPoint>();
std11::shared_ptr<audio::drain::EndPointWrite> algo = audio::drain::EndPointWrite::create();
m_process.pushFront(algo);
}
}
void audio::river::Interface::setOutputCallback(audio::drain::playbackFunction _function) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
if (m_mode != audio::river::modeInterface_output) {
RIVER_ERROR("Can not set output endpoint on other than a output IO");
return;
}
m_process.removeAlgoDynamic();
m_process.removeIfFirst<audio::drain::EndPoint>();
std11::shared_ptr<audio::drain::Algo> algo = audio::drain::EndPointCallback::create(_function);
m_process.pushFront(algo);
}
void audio::river::Interface::setInputCallback(audio::drain::recordFunction _function) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
if (m_mode == audio::river::modeInterface_output) {
RIVER_ERROR("Can not set output endpoint on other than a input or feedback IO");
return;
}
m_process.removeAlgoDynamic();
m_process.removeIfLast<audio::drain::EndPoint>();
std11::shared_ptr<audio::drain::Algo> algo = audio::drain::EndPointCallback::create(_function);
m_process.pushBack(algo);
}
void audio::river::Interface::setWriteCallback(audio::drain::playbackFunctionWrite _function) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
if (m_mode != audio::river::modeInterface_output) {
RIVER_ERROR("Can not set output endpoint on other than a output IO");
return;
}
m_process.removeAlgoDynamic();
std11::shared_ptr<audio::drain::EndPointWrite> algo = m_process.get<audio::drain::EndPointWrite>(0);
if (algo == nullptr) {
return;
}
algo->setCallback(_function);
}
void audio::river::Interface::start(const audio::Time& _time) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
RIVER_DEBUG("start [BEGIN]");
m_process.updateInterAlgo();
m_node->interfaceAdd(shared_from_this());
RIVER_DEBUG("start [ END ]");
}
void audio::river::Interface::stop(bool _fast, bool _abort) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
RIVER_DEBUG("stop [BEGIN]");
m_node->interfaceRemove(shared_from_this());
RIVER_DEBUG("stop [ END]");
}
void audio::river::Interface::abort() {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
RIVER_DEBUG("abort [BEGIN]");
// TODO :...
RIVER_DEBUG("abort [ END ]");
}
bool audio::river::Interface::setParameter(const std::string& _filter, const std::string& _parameter, const std::string& _value) {
RIVER_DEBUG("setParameter [BEGIN] : '" << _filter << "':'" << _parameter << "':'" << _value << "'");
bool out = false;
if ( _filter == "volume"
&& _parameter != "FLOW") {
RIVER_ERROR("Interface is not allowed to modify '" << _parameter << "' Volume just allowed to modify 'FLOW' volume");
return false;
}
std11::shared_ptr<audio::drain::Algo> algo = m_process.get<audio::drain::Algo>(_filter);
if (algo == nullptr) {
RIVER_ERROR("setParameter(" << _filter << ") ==> no filter named like this ...");
return false;
}
out = algo->setParameter(_parameter, _value);
RIVER_DEBUG("setParameter [ END ] : '" << out << "'");
return out;
}
std::string audio::river::Interface::getParameter(const std::string& _filter, const std::string& _parameter) const {
RIVER_DEBUG("getParameter [BEGIN] : '" << _filter << "':'" << _parameter << "'");
std::string out;
std11::shared_ptr<const audio::drain::Algo> algo = m_process.get<const audio::drain::Algo>(_filter);
if (algo == nullptr) {
RIVER_ERROR("setParameter(" << _filter << ") ==> no filter named like this ...");
return "[ERROR]";
}
out = algo->getParameter(_parameter);
RIVER_DEBUG("getParameter [ END ] : '" << out << "'");
return out;
}
std::string audio::river::Interface::getParameterProperty(const std::string& _filter, const std::string& _parameter) const {
RIVER_DEBUG("getParameterProperty [BEGIN] : '" << _filter << "':'" << _parameter << "'");
std::string out;
std11::shared_ptr<const audio::drain::Algo> algo = m_process.get<const audio::drain::Algo>(_filter);
if (algo == nullptr) {
RIVER_ERROR("setParameter(" << _filter << ") ==> no filter named like this ...");
return "[ERROR]";
}
out = algo->getParameterProperty(_parameter);
RIVER_DEBUG("getParameterProperty [ END ] : '" << out << "'");
return out;
}
void audio::river::Interface::write(const void* _value, size_t _nbChunk) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
m_process.updateInterAlgo();
std11::shared_ptr<audio::drain::EndPointWrite> algo = m_process.get<audio::drain::EndPointWrite>(0);
if (algo == nullptr) {
return;
}
algo->write(_value, _nbChunk);
}
#if 0
// TODO : add API aCCess mutex for Read and write...
std::vector<int16_t> audio::river::Interface::read(size_t _nbChunk) {
// TODO :...
std::vector<int16_t> data;
/*
data.resize(_nbChunk*m_map.size(), 0);
m_mutex.lock();
int32_t nbChunkBuffer = m_circularBuffer.size() / m_map.size();
m_mutex.unlock();
while (nbChunkBuffer < _nbChunk) {
usleep(1000);
nbChunkBuffer = m_circularBuffer.size() / m_map.size();
}
m_mutex.lock();
for (size_t iii = 0; iii<data.size(); ++iii) {
data[iii] = m_circularBuffer[iii];
}
m_circularBuffer.erase(m_circularBuffer.begin(), m_circularBuffer.begin()+data.size());
m_mutex.unlock();
*/
return data;
}
#endif
void audio::river::Interface::read(void* _value, size_t _nbChunk) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
m_process.updateInterAlgo();
// TODO :...
}
size_t audio::river::Interface::size() const {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
// TODO :...
return 0;
}
void audio::river::Interface::setBufferSize(size_t _nbChunk) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
if (m_node->isInput() == true) {
std11::shared_ptr<audio::drain::EndPointRead> algo = m_process.get<audio::drain::EndPointRead>(m_process.size()-1);
if (algo == nullptr) {
RIVER_ERROR("Request set buffer size for Interface that is not READ or WRITE mode ...");
return;
}
algo->setBufferSize(_nbChunk);
return;
}
std11::shared_ptr<audio::drain::EndPointWrite> algo = m_process.get<audio::drain::EndPointWrite>(0);
if (algo == nullptr) {
RIVER_ERROR("Request set buffer size for Interface that is not READ or WRITE mode ...");
return;
}
algo->setBufferSize(_nbChunk);
}
void audio::river::Interface::setBufferSize(const std11::chrono::microseconds& _time) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
if (m_node->isInput() == true) {
std11::shared_ptr<audio::drain::EndPointRead> algo = m_process.get<audio::drain::EndPointRead>(m_process.size()-1);
if (algo == nullptr) {
RIVER_ERROR("Request set buffer size for Interface that is not READ or WRITE mode ...");
return;
}
algo->setBufferSize(_time);
return;
}
std11::shared_ptr<audio::drain::EndPointWrite> algo = m_process.get<audio::drain::EndPointWrite>(0);
if (algo == nullptr) {
RIVER_ERROR("Request set buffer size for Interface that is not READ or WRITE mode ...");
return;
}
algo->setBufferSize(_time);
}
size_t audio::river::Interface::getBufferSize() {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
if (m_node->isInput() == true) {
std11::shared_ptr<audio::drain::EndPointRead> algo = m_process.get<audio::drain::EndPointRead>(m_process.size()-1);
if (algo == nullptr) {
RIVER_ERROR("Request get buffer size for Interface that is not READ or WRITE mode ...");
return 0;
}
return algo->getBufferSize();
}
std11::shared_ptr<audio::drain::EndPointWrite> algo = m_process.get<audio::drain::EndPointWrite>(0);
if (algo == nullptr) {
RIVER_ERROR("Request get buffer size for Interface that is not READ or WRITE mode ...");
return 0;
}
return algo->getBufferSize();
}
std11::chrono::microseconds audio::river::Interface::getBufferSizeMicrosecond() {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
if (m_node->isInput() == true) {
std11::shared_ptr<audio::drain::EndPointRead> algo = m_process.get<audio::drain::EndPointRead>(m_process.size()-1);
if (algo == nullptr) {
RIVER_ERROR("Request get buffer size for Interface that is not READ or WRITE mode ...");
return std11::chrono::microseconds(0);
}
return algo->getBufferSizeMicrosecond();
}
std11::shared_ptr<audio::drain::EndPointWrite> algo = m_process.get<audio::drain::EndPointWrite>(0);
if (algo == nullptr) {
RIVER_ERROR("Request get buffer size for Interface that is not READ or WRITE mode ...");
return std11::chrono::microseconds(0);
}
return algo->getBufferSizeMicrosecond();
}
size_t audio::river::Interface::getBufferFillSize() {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
if (m_node->isInput() == true) {
std11::shared_ptr<audio::drain::EndPointRead> algo = m_process.get<audio::drain::EndPointRead>(m_process.size()-1);
if (algo == nullptr) {
RIVER_ERROR("Request get buffer size for Interface that is not READ or WRITE mode ...");
return 0;
}
return algo->getBufferFillSize();
}
std11::shared_ptr<audio::drain::EndPointWrite> algo = m_process.get<audio::drain::EndPointWrite>(0);
if (algo == nullptr) {
RIVER_ERROR("Request get buffer size for Interface that is not READ or WRITE mode ...");
return 0;
}
return algo->getBufferFillSize();
}
std11::chrono::microseconds audio::river::Interface::getBufferFillSizeMicrosecond() {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
if (m_node->isInput() == true) {
std11::shared_ptr<audio::drain::EndPointRead> algo = m_process.get<audio::drain::EndPointRead>(m_process.size()-1);
if (algo == nullptr) {
RIVER_ERROR("Request get buffer size for Interface that is not READ or WRITE mode ...");
return std11::chrono::microseconds(0);
}
return algo->getBufferFillSizeMicrosecond();
}
std11::shared_ptr<audio::drain::EndPointWrite> algo = m_process.get<audio::drain::EndPointWrite>(0);
if (algo == nullptr) {
RIVER_ERROR("Request get buffer size for Interface that is not READ or WRITE mode ...");
return std11::chrono::microseconds(0);
}
return algo->getBufferFillSizeMicrosecond();
}
void audio::river::Interface::clearInternalBuffer() {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
m_process.updateInterAlgo();
// TODO :...
}
audio::Time audio::river::Interface::getCurrentTime() const {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
// TODO :...
return audio::Time();
return audio::Time::now();
}
void audio::river::Interface::addVolumeGroup(const std::string& _name) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
RIVER_DEBUG("addVolumeGroup(" << _name << ")");
std11::shared_ptr<audio::drain::Volume> algo = m_process.get<audio::drain::Volume>("volume");
if (algo == nullptr) {
m_process.removeAlgoDynamic();
// add all time the volume stage :
algo = audio::drain::Volume::create();
algo->setName("volume");
if (m_node->isInput() == true) {
m_process.pushFront(algo);
} else {
m_process.pushBack(algo);
}
}
if (_name == "FLOW") {
// Local volume name
algo->addVolumeStage(std11::make_shared<audio::drain::VolumeElement>(_name));
} else {
// get manager unique instance:
std11::shared_ptr<audio::river::io::Manager> mng = audio::river::io::Manager::getInstance();
algo->addVolumeStage(mng->getVolumeGroup(_name));
}
}
void audio::river::Interface::systemNewInputData(audio::Time _time, const void* _data, size_t _nbChunk) {
std11::unique_lock<std11::recursive_mutex> lockProcess(m_mutex);
void * tmpData = const_cast<void*>(_data);
m_process.push(_time, tmpData, _nbChunk);
}
void audio::river::Interface::systemNeedOutputData(audio::Time _time, void* _data, size_t _nbChunk, size_t _chunkSize) {
std11::unique_lock<std11::recursive_mutex> lockProcess(m_mutex);
//RIVER_INFO("time : " << _time);
m_process.pull(_time, _data, _nbChunk, _chunkSize);
}
void audio::river::Interface::systemVolumeChange() {
std11::unique_lock<std11::recursive_mutex> lockProcess(m_mutex);
std11::shared_ptr<audio::drain::Volume> algo = m_process.get<audio::drain::Volume>("volume");
if (algo == nullptr) {
return;
}
algo->volumeChange();
}
static void link(etk::FSNode& _node, const std::string& _first, const std::string& _op, const std::string& _second, bool _isLink=true) {
if (_op == "->") {
if (_isLink) {
_node << " " << _first << " -> " << _second << ";\n";
} else {
_node << " " << _first << " -> " << _second << " [style=dashed];\n";
}
} else if (_op == "<-") {
_node << " " << _first << " -> " <<_second<< " [color=transparent];\n";
if (_isLink) {
_node << " " << _second << " -> " << _first << " [constraint=false];\n";
} else {
_node << " " << _second << " -> " << _first << " [constraint=false, style=dashed];\n";
}
}
}
std::string audio::river::Interface::getDotNodeName() const {
if (m_mode == audio::river::modeInterface_input) {
return "API_" + etk::to_string(m_uid) + "_input";
} else if (m_mode == audio::river::modeInterface_feedback) {
return "API_" + etk::to_string(m_uid) + "_feedback";
} else if (m_mode == audio::river::modeInterface_output) {
return "API_" + etk::to_string(m_uid) + "_output";
}
return "error";
}
void audio::river::Interface::generateDot(etk::FSNode& _node, const std::string& _nameIO, bool _isLink) {
_node << " subgraph clusterInterface_" << m_uid << " {\n";
_node << " color=orange;\n";
_node << " label=\"[" << m_uid << "] Interface : " << m_name << "\";\n";
std::string nameIn;
std::string nameOut;
if ( m_mode == audio::river::modeInterface_input
|| m_mode == audio::river::modeInterface_feedback) {
m_process.generateDot(_node, 3, 10000+m_uid, nameIn, nameOut, false);
} else {
m_process.generateDot(_node, 3, 10000+m_uid, nameIn, nameOut, true);
}
if ( m_mode == audio::river::modeInterface_input
|| m_mode == audio::river::modeInterface_feedback) {
link(_node, _nameIO, "->", nameIn, _isLink);
} else {
link(_node, _nameIO, "<-", nameOut, _isLink);
}
_node << " node [shape=Mdiamond];\n";
if (m_mode == audio::river::modeInterface_input) {
_node << " " << getDotNodeName() << " [ label=\"API\\nINPUT\" ];\n";
link(_node, nameOut, "->", getDotNodeName());
} else if (m_mode == audio::river::modeInterface_feedback) {
_node << " " << getDotNodeName() << " [ label=\"API\\nFEEDBACK\" ];\n";
link(_node, nameOut, "->", getDotNodeName());
} else if (m_mode == audio::river::modeInterface_output) {
_node << " " << getDotNodeName() << " [ label=\"API\\nOUTPUT\" ];\n";
link(_node, nameIn, "<-", getDotNodeName());
}
_node << " }\n \n";
}

335
audio/river/Interface.h Normal file
View File

@@ -0,0 +1,335 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_RIVER_INTERFACE_H__
#define __AUDIO_RIVER_INTERFACE_H__
#include <string>
#include <vector>
#include <stdint.h>
#include <etk/mutex.h>
#include <etk/chrono.h>
#include <etk/functional.h>
#include <etk/memory.h>
#include <audio/format.h>
#include <audio/channel.h>
#include <audio/drain/Process.h>
#include <audio/drain/EndPointCallback.h>
#include <audio/drain/EndPointWrite.h>
#include <ejson/ejson.h>
#include <etk/os/FSNode.h>
#include <audio/Time.h>
namespace audio {
namespace river {
namespace io {
class Node;
class NodeAirTAudio;
class NodeAEC;
class NodeMuxer;
}
enum modeInterface {
modeInterface_unknow,
modeInterface_input,
modeInterface_output,
modeInterface_feedback,
};
/**
* @brief Interface is the basic handle to manage the input output stream
* @note To create this class see @ref audio::river::Manager class
*/
class Interface : public std11::enable_shared_from_this<Interface> {
friend class io::Node;
friend class io::NodeAirTAudio;
friend class io::NodeAEC;
friend class io::NodeMuxer;
friend class Manager;
protected:
uint32_t m_uid; //!< unique ID for interface
protected:
/**
* @brief Constructor (use factory)
*/
Interface();
/**
* @brief Initilize the Class (do all that is not manage by constructor) Call by factory.
* @param[in] _freq Frequency.
* @param[in] _map Channel map organization.
* @param[in] _format Sample format
* @param[in] _node Low level interface to connect the flow.
* @param[in] _config Special configuration of this interface.
* @return true Configuration done corectly.
* @return false the configuration has an error.
*/
bool init(float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std11::shared_ptr<audio::river::io::Node>& _node,
const std11::shared_ptr<const ejson::Object>& _config);
/**
* @brief Factory of this interface (called by class audio::river::Manager)
* @param[in] _freq Frequency.
* @param[in] _map Channel map organization.
* @param[in] _format Sample format
* @param[in] _node Low level interface to connect the flow.
* @param[in] _config Special configuration of this interface.
* @return nullptr The configuration does not work.
* @return pointer The interface has been corectly created.
*/
static std11::shared_ptr<Interface> create(float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std11::shared_ptr<audio::river::io::Node>& _node,
const std11::shared_ptr<const ejson::Object>& _config);
public:
/**
* @brief Destructor
*/
virtual ~Interface();
protected:
mutable std11::recursive_mutex m_mutex; //!< Local mutex to protect data
std11::shared_ptr<const ejson::Object> m_config; //!< configuration set by the user.
protected:
enum modeInterface m_mode; //!< interface type (input/output/feedback)
public:
/**
* @brief Get mode type of the current interface.
* @return The mode requested.
*/
enum modeInterface getMode() {
return m_mode;
}
protected:
audio::drain::Process m_process; //!< Algorithme processing engine
public:
/**
* @brief Get the interface format configuration.
* @return The current format.
*/
const audio::drain::IOFormatInterface& getInterfaceFormat() {
if ( m_mode == modeInterface_input
|| m_mode == modeInterface_feedback) {
return m_process.getOutputConfig();
} else {
return m_process.getInputConfig();
}
}
protected:
std11::shared_ptr<audio::river::io::Node> m_node; //!< Hardware interface to/from stream audio flow.
protected:
std::string m_name; //!< Name of the interface.
public:
/**
* @brief Get interface name.
* @return The current name.
*/
virtual std::string getName() {
return m_name;
};
/**
* @brief Set the interface name
* @param[in] _name new name of the interface
*/
virtual void setName(const std::string& _name) {
m_name = _name;
};
/**
* @brief set the read/write mode enable.
* @note If you not set a output/input callback you must call this function.
*/
virtual void setReadwrite();
/**
* @brief When we want to implement a Callback Mode:
*/
/**
* @brief Set a callback on the write mode interface to know when data is needed in the buffer
* @param[in] _function Function to call
*/
virtual void setWriteCallback(audio::drain::playbackFunctionWrite _function);
/**
* @brief Set Output callback mode with the specify callback.
* @param[in] _function Function to call
*/
virtual void setOutputCallback(audio::drain::playbackFunction _function);
/**
* @brief Set Input callback mode with the specify callback.
* @param[in] _function Function to call
*/
virtual void setInputCallback(audio::drain::recordFunction _function);
/**
* @brief Add a volume group of the current channel.
* @note If you do not call this function with the group "FLOW" you chan not have a channel volume.
* @note the set volume stage can not be set after the start.
* @param[in] _name Name of the group classicle common group:
* - FLOW for channel volume.
* - MEDIA for multimedia volume control (audio player, player video, web streaming ...).
* - TTS for Test-to-speech volume control.
* - COMMUNICATION for user communication volume control.
* - NOTIFICATION for urgent notification volume control.
* - NOISE for small noise volume control.
*/
virtual void addVolumeGroup(const std::string& _name);
public:
/**
* @brief Start the Audio interface flow.
* @param[in] _time Time to start the flow (0) to start as fast as possible...
* @note _time to play buffer when output interface (if possible)
* @note _time to read buffer when inut interface (if possible)
*/
virtual void start(const audio::Time& _time = audio::Time());
/**
* @brief Stop the current flow.
* @param[in] _fast The stream stop as fast as possible (not write all the buffer in speaker) but apply cross fade out.
* @param[in] _abort The stream stop whith no garenty of good audio stop.
*/
virtual void stop(bool _fast=false, bool _abort=false);
/**
* @brief Abort flow (no audio garenty)
*/
virtual void abort();
/**
* @brief Set a parameter in the stream flow
* @param[in] _filter name of the filter (if you added some personels)
* @param[in] _parameter Parameter name.
* @param[in] _value Value to set.
* @return true set done
* @return false An error occured
* @example : setParameter("volume", "FLOW", "-3dB");
* @example : setParameter("LowPassFilter", "cutFrequency", "1000Hz");
*/
virtual bool setParameter(const std::string& _filter, const std::string& _parameter, const std::string& _value);
/**
* @brief Get a parameter value
* @param[in] _filter name of the filter (if you added some personels)
* @param[in] _parameter Parameter name.
* @return The requested value.
* @example : getParameter("volume", "FLOW"); can return something like "-3dB"
* @example : getParameter("LowPassFilter", "cutFrequency"); can return something like "[-120..0]dB"
*/
virtual std::string getParameter(const std::string& _filter, const std::string& _parameter) const;
/**
* @brief Get a parameter value
* @param[in] _filter name of the filter (if you added some personels)
* @param[in] _parameter Parameter name.
* @return The requested value.
* @example : getParameter("volume", "FLOW"); can return something like "[-120..0]dB"
* @example : getParameter("LowPassFilter", "cutFreqiency"); can return something like "]100..10000]Hz"
*/
virtual std::string getParameterProperty(const std::string& _filter, const std::string& _parameter) const;
/**
* @brief write some audio sample in the speakers
* @param[in] _value Data To write on output
* @param[in] _nbChunk Number of audio chunk to write
*/
// TODO : TimeOut ???
virtual void write(const void* _value, size_t _nbChunk);
/**
* @brief read some audio sample from Microphone
* @param[in] _value Data To write on output
* @param[in] _nbChunk Number of audio chunk to write
*/
// TODO : TimeOut ???
virtual void read(void* _value, size_t _nbChunk);
/**
* @brief Get number of chunk in the local buffer
* @return Number of chunk
*/
virtual size_t size() const;
/**
* @brief Set buffer size in chunk number
* @param[in] _nbChunk Number of chunk in the buffer
*/
virtual void setBufferSize(size_t _nbChunk);
/**
* @brief Set buffer size size of the buffer with the stored time in <20>s
* @param[in] _time Time in microsecond of the buffer
*/
virtual void setBufferSize(const std11::chrono::microseconds& _time);
/**
* @brief get buffer size in chunk number
* @return Number of chunk that can be written in the buffer
*/
virtual size_t getBufferSize();
/**
* @brief Set buffer size size of the buffer with the stored time in <20>s
* @return Time in microsecond that can be written in the buffer
*/
virtual std11::chrono::microseconds getBufferSizeMicrosecond();
/**
* @brief Get buffer size filled in chunk number
* @return Number of chunk in the buffer (that might be read/write)
*/
virtual size_t getBufferFillSize();
/**
* @brief Set buffer size size of the buffer with the stored time in <20>s
* @return Time in microsecond of the buffer (that might be read/write)
*/
virtual std11::chrono::microseconds getBufferFillSizeMicrosecond();
/**
* @brief Remove internal Buffer
*/
virtual void clearInternalBuffer();
/**
* @brief Write : Get the time of the next sample time to write in the local buffer
* @brief Read : Get the time of the next sample time to read in the local buffer
*/
virtual audio::Time getCurrentTime() const;
private:
/**
* @brief Node Call interface : Input interface node has new data.
* @param[in] _time Time where the first sample has been capture.
* @param[in] _data Pointer on the new data.
* @param[in] _nbChunk Number of chunk in the buffer.
*/
virtual void systemNewInputData(audio::Time _time, const void* _data, size_t _nbChunk);
/**
* @brief Node Call interface: Output interface node need new data.
* @param[in] _time Time where the data might be played
* @param[in] _data Pointer on the data.
* @param[in] _nbChunk Number of chunk that might be write
* @param[in] _chunkSize Chunk size.
*/
virtual void systemNeedOutputData(audio::Time _time, void* _data, size_t _nbChunk, size_t _chunkSize);
/**
* @brief Node Call interface: A volume has change.
*/
virtual void systemVolumeChange();
public:
/**
* @brief Create the dot in the FileNode stream.
* @param[in,out] _node File node to write data.
* @param[in] _nameIO Name to link the interface node
* @param[in] _isLink True if the node is connected on the current interface.
*/
virtual void generateDot(etk::FSNode& _node, const std::string& _nameIO, bool _isLink=true);
/**
* @brief Get the current 'dot' name of the interface
* @return The anme requested.
*/
virtual std::string getDotNodeName() const;
protected:
/**
* @brief Interfanel generate of status
* @param[in] _origin status source
* @param[in] _status Event status
*/
void generateStatus(const std::string& _origin, const std::string& _status) {
m_process.generateStatus(_origin, _status);
}
public:
/**
* @brief Set status callback
* @param[in] _newFunction Function to call
*/
void setStatusFunction(audio::drain::statusFunction _newFunction) {
m_process.setStatusFunction(_newFunction);
}
};
}
}
#endif

229
audio/river/Manager.cpp Normal file
View File

@@ -0,0 +1,229 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include "Manager.h"
#include "Interface.h"
#include <stdexcept>
#include "io/Manager.h"
#include "io/Node.h"
#include "debug.h"
#include <ejson/ejson.h>
#undef __class__
#define __class__ "Manager"
static std11::mutex g_mutex;
static std::vector<std11::weak_ptr<audio::river::Manager> > g_listOfAllManager;
std11::shared_ptr<audio::river::Manager> audio::river::Manager::create(const std::string& _applicationUniqueId) {
std11::unique_lock<std11::mutex> lock(g_mutex);
for (size_t iii=0; iii<g_listOfAllManager.size() ; ++iii) {
std11::shared_ptr<audio::river::Manager> tmp = g_listOfAllManager[iii].lock();
if (tmp == nullptr) {
continue;
}
if (tmp->m_applicationUniqueId == _applicationUniqueId) {
return tmp;
}
}
// create a new one:
std11::shared_ptr<audio::river::Manager> out = std11::shared_ptr<audio::river::Manager>(new audio::river::Manager(_applicationUniqueId));
// add it at the list:
for (size_t iii=0; iii<g_listOfAllManager.size() ; ++iii) {
if (g_listOfAllManager[iii].expired() == true) {
g_listOfAllManager[iii] = out;
return out;
}
}
g_listOfAllManager.push_back(out);
return out;
}
audio::river::Manager::Manager(const std::string& _applicationUniqueId) :
m_applicationUniqueId(_applicationUniqueId),
m_listOpenInterface() {
}
audio::river::Manager::~Manager() {
// TODO : Stop all interfaces...
}
std::vector<std::string> audio::river::Manager::getListStreamInput() {
std::vector<std::string> output;
std11::shared_ptr<audio::river::io::Manager> manager = audio::river::io::Manager::getInstance();
if (manager == nullptr) {
RIVER_ERROR("Unable to load harware IO manager ... ");
} else {
output = manager->getListStreamInput();
}
return output;
}
std::vector<std::string> audio::river::Manager::getListStreamOutput() {
std::vector<std::string> output;
std11::shared_ptr<audio::river::io::Manager> manager = audio::river::io::Manager::getInstance();
if (manager == nullptr) {
RIVER_ERROR("Unable to load harware IO manager ... ");
} else {
output = manager->getListStreamOutput();
}
return output;
}
std::vector<std::string> audio::river::Manager::getListStreamVirtual() {
std::vector<std::string> output;
std11::shared_ptr<audio::river::io::Manager> manager = audio::river::io::Manager::getInstance();
if (manager == nullptr) {
RIVER_ERROR("Unable to load harware IO manager ... ");
} else {
output = manager->getListStreamVirtual();
}
return output;
}
std::vector<std::string> audio::river::Manager::getListStream() {
std::vector<std::string> output;
std11::shared_ptr<audio::river::io::Manager> manager = audio::river::io::Manager::getInstance();
if (manager == nullptr) {
RIVER_ERROR("Unable to load harware IO manager ... ");
} else {
output = manager->getListStream();
}
return output;
}
bool audio::river::Manager::setVolume(const std::string& _volumeName, float _valuedB) {
std11::shared_ptr<audio::river::io::Manager> manager = audio::river::io::Manager::getInstance();
if (manager == nullptr) {
RIVER_ERROR("Unable to load harware IO manager ... ");
return false;
}
return manager->setVolume(_volumeName, _valuedB);
}
float audio::river::Manager::getVolume(const std::string& _volumeName) const {
std11::shared_ptr<audio::river::io::Manager> manager = audio::river::io::Manager::getInstance();
if (manager == nullptr) {
RIVER_ERROR("Unable to load harware IO manager ... ");
return false;
}
return manager->getVolume(_volumeName);
}
std::pair<float,float> audio::river::Manager::getVolumeRange(const std::string& _volumeName) const {
std11::shared_ptr<audio::river::io::Manager> manager = audio::river::io::Manager::getInstance();
if (manager == nullptr) {
RIVER_ERROR("Unable to load harware IO manager ... ");
return std::make_pair<float,float>(0.0f,0.0f);
}
return manager->getVolumeRange(_volumeName);
}
std11::shared_ptr<audio::river::Interface> audio::river::Manager::createOutput(float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std::string& _streamName,
const std::string& _options) {
// get global hardware interface:
std11::shared_ptr<audio::river::io::Manager> manager = audio::river::io::Manager::getInstance();
if (manager == nullptr) {
RIVER_ERROR("Unable to load harware IO manager ... ");
return std11::shared_ptr<audio::river::Interface>();
}
// get the output or input channel :
std11::shared_ptr<audio::river::io::Node> node = manager->getNode(_streamName);
if (node == nullptr) {
RIVER_ERROR("Can not get the Requested stream '" << _streamName << "' ==> not listed in : " << manager->getListStream());
return std11::shared_ptr<audio::river::Interface>();
}
if (node->isOutput() != true) {
RIVER_ERROR("Can not Connect output on other thing than output ... for stream '" << _streamName << "'");;
return std11::shared_ptr<audio::river::Interface>();
}
// create user iterface:
std11::shared_ptr<audio::river::Interface> interface;
std11::shared_ptr<ejson::Object> tmpOption = ejson::Object::create(_options);
tmpOption->addString("io", "output");
interface = audio::river::Interface::create(_freq, _map, _format, node, tmpOption);
// store it in a list (needed to apply some parameters).
m_listOpenInterface.push_back(interface);
return interface;
}
std11::shared_ptr<audio::river::Interface> audio::river::Manager::createInput(float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std::string& _streamName,
const std::string& _options) {
// get global hardware interface:
std11::shared_ptr<audio::river::io::Manager> manager = audio::river::io::Manager::getInstance();
if (manager == nullptr) {
RIVER_ERROR("Unable to load harware IO manager ... ");
return std11::shared_ptr<audio::river::Interface>();
}
// get the output or input channel :
std11::shared_ptr<audio::river::io::Node> node = manager->getNode(_streamName);
if (node == nullptr) {
RIVER_ERROR("Can not get the Requested stream '" << _streamName << "' ==> not listed in : " << manager->getListStream());
return std11::shared_ptr<audio::river::Interface>();
}
if (node->isInput() != true) {
RIVER_ERROR("Can not Connect input on other thing than input ... for stream '" << _streamName << "'");;
return std11::shared_ptr<audio::river::Interface>();
}
// create user iterface:
std11::shared_ptr<audio::river::Interface> interface;
std11::shared_ptr<ejson::Object> tmpOption = ejson::Object::create(_options);
tmpOption->addString("io", "input");
interface = audio::river::Interface::create(_freq, _map, _format, node, tmpOption);
// store it in a list (needed to apply some parameters).
m_listOpenInterface.push_back(interface);
return interface;
}
std11::shared_ptr<audio::river::Interface> audio::river::Manager::createFeedback(float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std::string& _streamName,
const std::string& _options) {
// get global hardware interface:
std11::shared_ptr<audio::river::io::Manager> manager = audio::river::io::Manager::getInstance();
if (manager == nullptr) {
RIVER_ERROR("Unable to load harware IO manager ... ");
return std11::shared_ptr<audio::river::Interface>();
}
// get the output or input channel :
std11::shared_ptr<audio::river::io::Node> node = manager->getNode(_streamName);
if (node == nullptr) {
RIVER_ERROR("Can not get the Requested stream '" << _streamName << "' ==> not listed in : " << manager->getListStream());
return std11::shared_ptr<audio::river::Interface>();
}
if (node->isOutput() != true) {
RIVER_ERROR("Can not Connect feedback on other thing than output ... for stream '" << _streamName << "'");;
return std11::shared_ptr<audio::river::Interface>();
}
// create user iterface:
std11::shared_ptr<audio::river::Interface> interface;
std11::shared_ptr<ejson::Object> tmpOption = ejson::Object::create(_options);
tmpOption->addString("io", "feedback");
interface = audio::river::Interface::create(_freq, _map, _format, node, tmpOption);
// store it in a list (needed to apply some parameters).
m_listOpenInterface.push_back(interface);
return interface;
}
void audio::river::Manager::generateDotAll(const std::string& _filename) {
// get global hardware interface:
std11::shared_ptr<audio::river::io::Manager> manager = audio::river::io::Manager::getInstance();
if (manager == nullptr) {
RIVER_ERROR("Can not get the harware manager");
return;
}
manager->generateDot(_filename);
}

140
audio/river/Manager.h Normal file
View File

@@ -0,0 +1,140 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_RIVER_MANAGER_H__
#define __AUDIO_RIVER_MANAGER_H__
#include <string>
#include <stdint.h>
#include <etk/memory.h>
#include <audio/river/Interface.h>
#include <audio/format.h>
#include <audio/channel.h>
#include <ejson/ejson.h>
namespace audio {
namespace river {
/**
* @brief Audio interface manager : Single interface for every application that want to access on the Audio input/output
*/
class Manager : public std11::enable_shared_from_this<Manager> {
private:
const std::string& m_applicationUniqueId; //!< name of the application that open the Audio Interface.
std::vector<std11::weak_ptr<audio::river::Interface> > m_listOpenInterface; //!< List of all open Stream.
protected:
/**
* @brief Constructor
*/
Manager(const std::string& _applicationUniqueId);
public:
/**
* @brief factory of the manager. Every Application will have only one maager for all his flow. this permit to manage all of it
* @param[in] _applicationUniqueId Unique name of the application
* @return Pointer on the manager or nullptr if an error occured
*/
static std11::shared_ptr<audio::river::Manager> create(const std::string& _applicationUniqueId);
/**
* @brief Destructor
*/
virtual ~Manager();
public:
/**
* @brief Get all input audio stream.
* @return a list of all availlables input stream name
*/
std::vector<std::string> getListStreamInput();
/**
* @brief Get all output audio stream.
* @return a list of all availlables output stream name
*/
std::vector<std::string> getListStreamOutput();
/**
* @brief Get all audio virtual stream.
* @return a list of all availlables virtual stream name
*/
std::vector<std::string> getListStreamVirtual();
/**
* @brief Get all audio stream.
* @return a list of all availlables stream name
*/
std::vector<std::string> getListStream();
/**
* @brief Set a volume for a specific group
* @param[in] _volumeName Name of the volume (MASTER, MATER_BT ...)
* @param[in] _value Volume in dB to set.
* @return true set done
* @return false An error occured
* @example : setVolume("MASTER", -3.0f);
*/
virtual bool setVolume(const std::string& _volumeName, float _valuedB);
/**
* @brief Get a volume value
* @param[in] _volumeName Name of the volume (MASTER, MATER_BT ...)
* @return The Volume value in dB.
* @example ret = getVolume("MASTER"); can return something like ret = -3.0f
*/
virtual float getVolume(const std::string& _volumeName) const;
/**
* @brief Get a parameter value
* @param[in] _volumeName Name of the volume (MASTER, MATER_BT ...)
* @return The requested value Range.
* @example ret = getVolumeRange("MASTER"); can return something like ret=(-120.0f,0.0f)
*/
virtual std::pair<float,float> getVolumeRange(const std::string& _volumeName) const;
/**
* @brief Create output Interface
* @param[in] _freq Frequency to open Interface [8,16,22,32,48] kHz
* @param[in] _map ChannelMap of the Output
* @param[in] _format Sample Format to open the stream [int8_t, int16_t, ...]
* @param[in] _streamName Stream name to open: "" or "default" open current selected output
* @param[in] _options Json option to configure default resampling and many other things.
* @return a pointer on the interface
*/
virtual std11::shared_ptr<Interface> createOutput(float _freq = 48000,
const std::vector<audio::channel>& _map = std::vector<audio::channel>(),
audio::format _format = audio::format_int16,
const std::string& _streamName = "",
const std::string& _options = "");
/**
* @brief Create input Interface
* @param[in] _freq Frequency to open Interface [8,16,22,32,48] kHz
* @param[in] _map ChannelMap of the Output
* @param[in] _format Sample Format to open the stream [int8_t, int16_t, ...]
* @param[in] _streamName Stream name to open: "" or "default" open current selected input
* @param[in] _options Json option to configure default resampling and many other things.
* @return a pointer on the interface
*/
virtual std11::shared_ptr<Interface> createInput(float _freq = 48000,
const std::vector<audio::channel>& _map = std::vector<audio::channel>(),
audio::format _format = audio::format_int16,
const std::string& _streamName = "",
const std::string& _options = "");
/**
* @brief Create input Feedback Interface
* @param[in] _freq Frequency to open Interface [8,16,22,32,48] kHz
* @param[in] _map ChannelMap of the Output
* @param[in] _format Sample Format to open the stream [int8_t, int16_t, ...]
* @param[in] _streamName Stream name to open: "" or "default" open current selected input
* @param[in] _options Json option to configure default resampling and many other things.
* @return a pointer on the interface
*/
virtual std11::shared_ptr<Interface> createFeedback(float _freq = 48000,
const std::vector<audio::channel>& _map = std::vector<audio::channel>(),
audio::format _format = audio::format_int16,
const std::string& _streamName = "",
const std::string& _options = "");
/**
* @brief Generate the dot file corresponding at all the actif nodes.
* @param[in] _filename Name of the file to write data.
*/
virtual void generateDotAll(const std::string& _filename);
};
}
}
#endif

View File

@@ -4,10 +4,10 @@
* @license APACHE v2.0 (see license file) * @license APACHE v2.0 (see license file)
*/ */
#include <river/debug.h> #include <audio/river/debug.h>
int32_t river::getLogId() { int32_t audio::river::getLogId() {
static int32_t g_val = etk::log::registerInstance("river"); static int32_t g_val = etk::log::registerInstance("river");
return g_val; return g_val;
} }

View File

@@ -5,24 +5,17 @@
*/ */
#ifndef __RIVER_DEBUG_H__ #ifndef __AUDIO_RIVER_DEBUG_H__
#define __RIVER_DEBUG_H__ #define __AUDIO_RIVER_DEBUG_H__
#include <etk/log.h> #include <etk/log.h>
namespace audio {
namespace river { namespace river {
int32_t getLogId(); int32_t getLogId();
}; }
// TODO : Review this problem of multiple intanciation of "std::stringbuf sb" }
#define RIVER_BASE(info,data) \ #define RIVER_BASE(info,data) TK_LOG_BASE(audio::river::getLogId(),info,data)
do { \
if (info <= etk::log::getLevel(river::getLogId())) { \
std::stringbuf sb; \
std::ostream tmpStream(&sb); \
tmpStream << data; \
etk::log::logStream(river::getLogId(), info, __LINE__, __class__, __func__, tmpStream); \
} \
} while(0)
#define RIVER_CRITICAL(data) RIVER_BASE(1, data) #define RIVER_CRITICAL(data) RIVER_BASE(1, data)
#define RIVER_ERROR(data) RIVER_BASE(2, data) #define RIVER_ERROR(data) RIVER_BASE(2, data)
@@ -52,7 +45,7 @@ namespace river {
do { \ do { \
static FILE *pointerOnFile = nullptr; \ static FILE *pointerOnFile = nullptr; \
static bool errorOpen = false; \ static bool errorOpen = false; \
if (NULL==pointerOnFile) { \ if (pointerOnFile == nullptr) { \
RIVER_WARNING("open file '" << fileName << "' type=" << #type); \ RIVER_WARNING("open file '" << fileName << "' type=" << #type); \
pointerOnFile = fopen(fileName,"w"); \ pointerOnFile = fopen(fileName,"w"); \
if ( errorOpen == false \ if ( errorOpen == false \

View File

@@ -4,8 +4,8 @@
* @license APACHE v2.0 (see license file) * @license APACHE v2.0 (see license file)
*/ */
#ifdef __RIVER_DEBUG_H__ #ifdef __AUDIO_RIVER_DEBUG_H__
#undef __RIVER_DEBUG_H__ #undef __AUDIO_RIVER_DEBUG_H__
#undef RIVER_BASE #undef RIVER_BASE
#undef RIVER_CRITICAL #undef RIVER_CRITICAL
@@ -16,5 +16,6 @@
#undef RIVER_VERBOSE #undef RIVER_VERBOSE
#undef RIVER_TODO #undef RIVER_TODO
#undef RIVER_ASSERT #undef RIVER_ASSERT
#endif #endif

View File

@@ -4,18 +4,18 @@
* @license APACHE v2.0 (see license file) * @license APACHE v2.0 (see license file)
*/ */
#include <river/io/Group.h> #include <audio/river/io/Group.h>
#include <river/debug.h> #include <audio/river/debug.h>
#include "Node.h" #include "Node.h"
#include "NodeAEC.h" #include "NodeAEC.h"
#include "NodeAirTAudio.h" #include "NodeOrchestra.h"
#include "NodePortAudio.h" #include "NodePortAudio.h"
#include "Node.h" #include "Node.h"
#undef __class__ #undef __class__
#define __class__ "io::Group" #define __class__ "io::Group"
void river::io::Group::createFrom(const ejson::Document& _obj, const std::string& _name) { void audio::river::io::Group::createFrom(const ejson::Document& _obj, const std::string& _name) {
RIVER_INFO("Create Group[" << _name << "] (START) ___________________________"); RIVER_INFO("Create Group[" << _name << "] (START) ___________________________");
for (size_t iii=0; iii<_obj.size(); ++iii) { for (size_t iii=0; iii<_obj.size(); ++iii) {
const std11::shared_ptr<const ejson::Object> tmpObject = _obj.getObject(_obj.getKey(iii)); const std11::shared_ptr<const ejson::Object> tmpObject = _obj.getObject(_obj.getKey(iii));
@@ -27,18 +27,18 @@ void river::io::Group::createFrom(const ejson::Document& _obj, const std::string
RIVER_INFO("Add element in Group[" << _name << "]: " << _obj.getKey(iii)); RIVER_INFO("Add element in Group[" << _name << "]: " << _obj.getKey(iii));
// get type : io // get type : io
std::string ioType = tmpObject->getStringValue("io", "error"); std::string ioType = tmpObject->getStringValue("io", "error");
#ifdef __AIRTAUDIO_INFERFACE__ #ifdef AUDIO_RIVER_BUILD_ORCHESTRA
if ( ioType == "input" if ( ioType == "input"
|| ioType == "output") { || ioType == "output") {
std11::shared_ptr<river::io::Node> tmp = river::io::NodeAirTAudio::create(_obj.getKey(iii), tmpObject); std11::shared_ptr<audio::river::io::Node> tmp = audio::river::io::NodeOrchestra::create(_obj.getKey(iii), tmpObject);
tmp->setGroup(shared_from_this()); tmp->setGroup(shared_from_this());
m_list.push_back(tmp); m_list.push_back(tmp);
} }
#endif #endif
#ifdef __PORTAUDIO_INFERFACE__ #ifdef AUDIO_RIVER_BUILD_PORTAUDIO
if ( ioType == "PAinput" if ( ioType == "PAinput"
|| ioType == "PAoutput") { || ioType == "PAoutput") {
std11::shared_ptr<river::io::Node> tmp = river::io::NodePortAudio::create(_obj.getKey(iii), tmpObject); std11::shared_ptr<audio::river::io::Node> tmp = audio::river::io::NodePortAudio::create(_obj.getKey(iii), tmpObject);
tmp->setGroup(shared_from_this()); tmp->setGroup(shared_from_this());
m_list.push_back(tmp); m_list.push_back(tmp);
} }
@@ -48,12 +48,12 @@ void river::io::Group::createFrom(const ejson::Document& _obj, const std::string
// Link all the IO together : (not needed if one device ... // Link all the IO together : (not needed if one device ...
// Note : The interlink work only for alsa (NOW) and with AirTAudio... // Note : The interlink work only for alsa (NOW) and with AirTAudio...
if(m_list.size() > 1) { if(m_list.size() > 1) {
#ifdef __AIRTAUDIO_INFERFACE__ #ifdef AUDIO_RIVER_BUILD_ORCHESTRA
std11::shared_ptr<river::io::NodeAirTAudio> linkRef = std11::dynamic_pointer_cast<river::io::NodeAirTAudio>(m_list[0]); std11::shared_ptr<audio::river::io::NodeOrchestra> linkRef = std11::dynamic_pointer_cast<audio::river::io::NodeOrchestra>(m_list[0]);
for (size_t iii=1; iii<m_list.size(); ++iii) { for (size_t iii=1; iii<m_list.size(); ++iii) {
if (m_list[iii] != nullptr) { if (m_list[iii] != nullptr) {
std11::shared_ptr<river::io::NodeAirTAudio> link = std11::dynamic_pointer_cast<river::io::NodeAirTAudio>(m_list[iii]); std11::shared_ptr<audio::river::io::NodeOrchestra> link = std11::dynamic_pointer_cast<audio::river::io::NodeOrchestra>(m_list[iii]);
linkRef->m_adac.isMasterOf(link->m_adac); linkRef->m_interface.isMasterOf(link->m_interface);
} }
} }
#endif #endif
@@ -62,7 +62,7 @@ void river::io::Group::createFrom(const ejson::Document& _obj, const std::string
// manage Link Between Nodes : // manage Link Between Nodes :
if (m_link != nullptr) { if (m_link != nullptr) {
RIVER_INFO("******** START LINK ************"); RIVER_INFO("******** START LINK ************");
std11::shared_ptr<river::io::NodeAirTAudio> link = std11::dynamic_pointer_cast<river::io::NodeAirTAudio>(m_link); std11::shared_ptr<audio::river::io::NodeOrchestra> link = std11::dynamic_pointer_cast<audio::river::io::NodeOrchestra>(m_link);
if (link == nullptr) { if (link == nullptr) {
RIVER_ERROR("Can not link 2 Interface with not the same type (reserved for HW interface)"); RIVER_ERROR("Can not link 2 Interface with not the same type (reserved for HW interface)");
return; return;
@@ -82,7 +82,7 @@ void river::io::Group::createFrom(const ejson::Document& _obj, const std::string
} }
std11::shared_ptr<river::io::Node> river::io::Group::getNode(const std::string& _name) { std11::shared_ptr<audio::river::io::Node> audio::river::io::Group::getNode(const std::string& _name) {
for (size_t iii=0; iii<m_list.size(); ++iii) { for (size_t iii=0; iii<m_list.size(); ++iii) {
if (m_list[iii] != nullptr) { if (m_list[iii] != nullptr) {
if (m_list[iii]->getName() == _name) { if (m_list[iii]->getName() == _name) {
@@ -90,10 +90,10 @@ std11::shared_ptr<river::io::Node> river::io::Group::getNode(const std::string&
} }
} }
} }
return std11::shared_ptr<river::io::Node>(); return std11::shared_ptr<audio::river::io::Node>();
} }
void river::io::Group::start() { void audio::river::io::Group::start() {
RIVER_ERROR("request start "); RIVER_ERROR("request start ");
int32_t count = 0; int32_t count = 0;
for (size_t iii=0; iii<m_list.size(); ++iii) { for (size_t iii=0; iii<m_list.size(); ++iii) {
@@ -113,7 +113,7 @@ void river::io::Group::start() {
} }
} }
void river::io::Group::stop() { void audio::river::io::Group::stop() {
RIVER_ERROR("request stop "); RIVER_ERROR("request stop ");
int32_t count = 0; int32_t count = 0;
for (size_t iii=0; iii<m_list.size(); ++iii) { for (size_t iii=0; iii<m_list.size(); ++iii) {
@@ -133,7 +133,7 @@ void river::io::Group::stop() {
} }
} }
void river::io::Group::generateDot(etk::FSNode& _node, bool _hardwareNode) { void audio::river::io::Group::generateDot(etk::FSNode& _node, bool _hardwareNode) {
for (size_t iii=0; iii<m_list.size(); ++iii) { for (size_t iii=0; iii<m_list.size(); ++iii) {
if (m_list[iii] != nullptr) { if (m_list[iii] != nullptr) {
if (m_list[iii]->isHarwareNode() == _hardwareNode) { if (m_list[iii]->isHarwareNode() == _hardwareNode) {

78
audio/river/io/Group.h Normal file
View File

@@ -0,0 +1,78 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_RIVER_IO_GROUP_H__
#define __AUDIO_RIVER_IO_GROUP_H__
#include <string>
#include <vector>
#include <ejson/ejson.h>
#include <etk/os/FSNode.h>
namespace audio {
namespace river {
namespace io {
class Node;
class Manager;
/**
* @brief Group is contituate to manage some input and output in the same start and stop.
* It link N interface in a group. The start and the sopt is requested in Node inside the
* group they will start and stop when the first start is requested and stop when the last
* is stopped.
* @note For the Alsa interface a low level link is availlable with AirTAudio for Alsa (One thread)
*/
class Group : public std11::enable_shared_from_this<Group> {
public:
/**
* @brief Contructor. No special thing to do.
*/
Group() {}
/**
* @brief Destructor
*/
~Group() {
// TODO : ...
}
private:
std::vector< std11::shared_ptr<Node> > m_list; //!< List of all node in the group
public:
/**
* @brief Create a group with all node needed to syncronize together
* @param[in] _obj json document to create all the node in the group named _name
* @param[in] _name Name of the group to create
*/
void createFrom(const ejson::Document& _obj, const std::string& _name);
/**
* @brief Get a node in the group (if the node is not in the group nothing append).
* @param[in] _name Name of the node requested.
* @return nullptr The node named _name was not found.
* @return pointer The node was find in this group.
*/
std11::shared_ptr<audio::river::io::Node> getNode(const std::string& _name);
/**
* @brief Start the group.
* @note all sub-node will be started.
*/
void start();
/**
* @brief Stop the group.
* @note All sub-node will be stopped at the reserve order that they start.
*/
void stop();
/**
* @brief Create the dot in the FileNode stream.
* @param[in,out] _node File node to write data.
* @param[in] _hardwareNode true if user want only display the hardware
* node and not the software node. false The oposite.
*/
void generateDot(etk::FSNode& _node, bool _hardwareNode);
};
}
}
}
#endif

394
audio/river/io/Manager.cpp Normal file
View File

@@ -0,0 +1,394 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <audio/river/io/Manager.h>
#include <audio/river/debug.h>
#include <audio/river/river.h>
#include <audio/river/io/Node.h>
#include <audio/river/io/NodeAEC.h>
#include <audio/river/io/NodeMuxer.h>
#include <audio/river/io/NodeOrchestra.h>
#include <audio/river/io/NodePortAudio.h>
#include <etk/os/FSNode.h>
#include <etk/memory.h>
#include <etk/types.h>
#include <utility>
#undef __class__
#define __class__ "io::Manager"
#ifdef AUDIO_RIVER_BUILD_PORTAUDIO
#include <portaudio.h>
#endif
static std::string basicAutoConfig =
"{\n"
" microphone:{\n"
" io:'input',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" },\n"
" frequency:0,\n"
" channel-map:[\n"
" 'front-left', 'front-right'\n"
" ],\n"
" type:'auto',\n"
" nb-chunk:1024\n"
" },\n"
" speaker:{\n"
" io:'output',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" },\n"
" frequency:0,\n"
" channel-map:[\n"
" 'front-left', 'front-right',\n"
" ],\n"
" type:'auto',\n"
" nb-chunk:1024,\n"
" volume-name:'MASTER'\n"
" }\n"
"}\n";
audio::river::io::Manager::Manager() {
#ifdef AUDIO_RIVER_BUILD_PORTAUDIO
PaError err = Pa_Initialize();
if(err != paNoError) {
RIVER_WARNING("Can not initialize portaudio : " << Pa_GetErrorText(err));
}
#endif
}
void audio::river::io::Manager::init(const std::string& _filename) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
if (_filename == "") {
RIVER_INFO("Load default config");
m_config.parse(basicAutoConfig);
} else if (m_config.load(_filename) == false) {
RIVER_ERROR("you must set a basic configuration file for harware configuration: '" << _filename << "'");
}
}
void audio::river::io::Manager::initString(const std::string& _data) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
m_config.parse(_data);
}
void audio::river::io::Manager::unInit() {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
// TODO : ...
}
audio::river::io::Manager::~Manager() {
#ifdef AUDIO_RIVER_BUILD_PORTAUDIO
PaError err = Pa_Terminate();
if(err != paNoError) {
RIVER_WARNING("Can not initialize portaudio : " << Pa_GetErrorText(err));
}
#endif
};
std11::shared_ptr<audio::river::io::Manager> audio::river::io::Manager::getInstance() {
if (audio::river::isInit() == false) {
return std11::shared_ptr<audio::river::io::Manager>();
}
static std11::shared_ptr<audio::river::io::Manager> manager(new Manager());
return manager;
}
std::vector<std::string> audio::river::io::Manager::getListStreamInput() {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
std::vector<std::string> output;
std::vector<std::string> keys = m_config.getKeys();
for (size_t iii=0; iii<keys.size(); ++iii) {
const std11::shared_ptr<const ejson::Object> tmppp = m_config.getObject(keys[iii]);
if (tmppp != nullptr) {
std::string type = tmppp->getStringValue("io", "error");
if ( type == "input"
|| type == "PAinput") {
output.push_back(keys[iii]);
}
}
}
return output;
}
std::vector<std::string> audio::river::io::Manager::getListStreamOutput() {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
std::vector<std::string> output;
std::vector<std::string> keys = m_config.getKeys();
for (size_t iii=0; iii<keys.size(); ++iii) {
const std11::shared_ptr<const ejson::Object> tmppp = m_config.getObject(keys[iii]);
if (tmppp != nullptr) {
std::string type = tmppp->getStringValue("io", "error");
if ( type == "output"
|| type == "PAoutput") {
output.push_back(keys[iii]);
}
}
}
return output;
}
std::vector<std::string> audio::river::io::Manager::getListStreamVirtual() {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
std::vector<std::string> output;
std::vector<std::string> keys = m_config.getKeys();
for (size_t iii=0; iii<keys.size(); ++iii) {
const std11::shared_ptr<const ejson::Object> tmppp = m_config.getObject(keys[iii]);
if (tmppp != nullptr) {
std::string type = tmppp->getStringValue("io", "error");
if ( type != "input"
&& type != "PAinput"
&& type != "output"
&& type != "PAoutput"
&& type != "error") {
output.push_back(keys[iii]);
}
}
}
return output;
}
std::vector<std::string> audio::river::io::Manager::getListStream() {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
std::vector<std::string> output;
std::vector<std::string> keys = m_config.getKeys();
for (size_t iii=0; iii<keys.size(); ++iii) {
const std11::shared_ptr<const ejson::Object> tmppp = m_config.getObject(keys[iii]);
if (tmppp != nullptr) {
std::string type = tmppp->getStringValue("io", "error");
if (type != "error") {
output.push_back(keys[iii]);
}
}
}
return output;
}
std11::shared_ptr<audio::river::io::Node> audio::river::io::Manager::getNode(const std::string& _name) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
RIVER_WARNING("Get node : " << _name);
// search in the standalone list :
for (size_t iii=0; iii<m_list.size(); ++iii) {
std11::shared_ptr<audio::river::io::Node> tmppp = m_list[iii].lock();
if ( tmppp != nullptr
&& _name == tmppp->getName()) {
RIVER_WARNING(" find it ... in standalone");
return tmppp;
}
}
// search in the group list:
{
for (std::map<std::string, std11::shared_ptr<audio::river::io::Group> >::iterator it(m_listGroup.begin());
it != m_listGroup.end();
++it) {
if (it->second != nullptr) {
std11::shared_ptr<audio::river::io::Node> node = it->second->getNode(_name);
if (node != nullptr) {
RIVER_WARNING(" find it ... in group: " << it->first);
return node;
}
}
}
}
RIVER_WARNING("Try create a new one : " << _name);
// check if the node can be open :
const std11::shared_ptr<const ejson::Object> tmpObject = m_config.getObject(_name);
if (tmpObject != nullptr) {
//Check if it is in a group:
std::string groupName = tmpObject->getStringValue("group", "");
// get type : io
std::string ioType = tmpObject->getStringValue("io", "error");
if ( groupName != ""
&& ( ioType == "input"
|| ioType == "output"
|| ioType == "PAinput"
|| ioType == "PAoutput") ) {
std11::shared_ptr<audio::river::io::Group> tmpGroup = getGroup(groupName);
if (tmpGroup == nullptr) {
RIVER_WARNING("Can not get group ... '" << groupName << "'");
return std11::shared_ptr<audio::river::io::Node>();
}
return tmpGroup->getNode(_name);
} else {
if (groupName != "") {
RIVER_WARNING("Group is only availlable for Hardware interface ... '" << _name << "'");
}
// TODO : Create a standalone group for every single element ==> simplify understanding ... but not for virtual interface ...
if ( ioType == "input"
|| ioType == "output") {
#ifdef AUDIO_RIVER_BUILD_ORCHESTRA
std11::shared_ptr<audio::river::io::Node> tmp = audio::river::io::NodeOrchestra::create(_name, tmpObject);
m_list.push_back(tmp);
return tmp;
#else
RIVER_WARNING("not present interface");
#endif
}
if ( ioType == "PAinput"
|| ioType == "PAoutput") {
#ifdef AUDIO_RIVER_BUILD_PORTAUDIO
std11::shared_ptr<audio::river::io::Node> tmp = audio::river::io::NodePortAudio::create(_name, tmpObject);
m_list.push_back(tmp);
return tmp;
#else
RIVER_WARNING("not present interface");
#endif
}
if (ioType == "aec") {
std11::shared_ptr<audio::river::io::Node> tmp = audio::river::io::NodeAEC::create(_name, tmpObject);
m_list.push_back(tmp);
return tmp;
}
if (ioType == "muxer") {
std11::shared_ptr<audio::river::io::Node> tmp = audio::river::io::NodeMuxer::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 std11::shared_ptr<audio::river::io::Node>();
}
std11::shared_ptr<audio::drain::VolumeElement> audio::river::io::Manager::getVolumeGroup(const std::string& _name) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
if (_name == "") {
RIVER_ERROR("Try to create an audio group with no name ...");
return std11::shared_ptr<audio::drain::VolumeElement>();
}
for (size_t iii=0; iii<m_volumeGroup.size(); ++iii) {
if (m_volumeGroup[iii] == nullptr) {
continue;
}
if (m_volumeGroup[iii]->getName() == _name) {
return m_volumeGroup[iii];
}
}
RIVER_DEBUG("Add a new volume group : '" << _name << "'");
std11::shared_ptr<audio::drain::VolumeElement> tmpVolume = std11::make_shared<audio::drain::VolumeElement>(_name);
m_volumeGroup.push_back(tmpVolume);
return tmpVolume;
}
bool audio::river::io::Manager::setVolume(const std::string& _volumeName, float _valuedB) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
std11::shared_ptr<audio::drain::VolumeElement> volume = getVolumeGroup(_volumeName);
if (volume == nullptr) {
RIVER_ERROR("Can not set volume ... : '" << _volumeName << "'");
return false;
}
if ( _valuedB < -300
|| _valuedB > 300) {
RIVER_ERROR("Can not set volume ... : '" << _volumeName << "' out of range : [-300..300]");
return false;
}
volume->setVolume(_valuedB);
for (size_t iii=0; iii<m_list.size(); ++iii) {
std11::shared_ptr<audio::river::io::Node> val = m_list[iii].lock();
if (val != nullptr) {
val->volumeChange();
}
}
return true;
}
float audio::river::io::Manager::getVolume(const std::string& _volumeName) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
std11::shared_ptr<audio::drain::VolumeElement> volume = getVolumeGroup(_volumeName);
if (volume == nullptr) {
RIVER_ERROR("Can not get volume ... : '" << _volumeName << "'");
return 0.0f;
}
return volume->getVolume();
}
std::pair<float,float> audio::river::io::Manager::getVolumeRange(const std::string& _volumeName) const {
return std::make_pair<float,float>(-300, 300);
}
void audio::river::io::Manager::generateDot(const std::string& _filename) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
etk::FSNode node(_filename);
RIVER_INFO("Generate the DOT files: " << node);
if (node.fileOpenWrite() == false) {
RIVER_ERROR("Can not Write the dot file (fail to open) : " << node);
return;
}
node << "digraph G {" << "\n";
node << " rankdir=\"LR\";\n";
// First Step : Create all HW interface:
{
// standalone
for (size_t iii=0; iii<m_list.size(); ++iii) {
std11::shared_ptr<audio::river::io::Node> val = m_list[iii].lock();
if (val != nullptr) {
if (val->isHarwareNode() == true) {
val->generateDot(node);
}
}
}
for (std::map<std::string, std11::shared_ptr<audio::river::io::Group> >::iterator it(m_listGroup.begin());
it != m_listGroup.end();
++it) {
if (it->second != nullptr) {
it->second->generateDot(node, true);
}
}
}
// All other ...
{
// standalone
for (size_t iii=0; iii<m_list.size(); ++iii) {
std11::shared_ptr<audio::river::io::Node> val = m_list[iii].lock();
if (val != nullptr) {
if (val->isHarwareNode() == false) {
val->generateDot(node);
}
}
}
for (std::map<std::string, std11::shared_ptr<audio::river::io::Group> >::iterator it(m_listGroup.begin());
it != m_listGroup.end();
++it) {
if (it->second != nullptr) {
it->second->generateDot(node, false);
}
}
}
node << "}" << "\n";
node.fileClose();
RIVER_INFO("Generate the DOT files: " << node << " (DONE)");
}
std11::shared_ptr<audio::river::io::Group> audio::river::io::Manager::getGroup(const std::string& _name) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
std11::shared_ptr<audio::river::io::Group> out;
std::map<std::string, std11::shared_ptr<audio::river::io::Group> >::iterator it = m_listGroup.find(_name);
if (it == m_listGroup.end()) {
RIVER_INFO("Create a new group: " << _name << " (START)");
out = std11::make_shared<audio::river::io::Group>();
if (out != nullptr) {
out->createFrom(m_config, _name);
std::pair<std::string, std11::shared_ptr<audio::river::io::Group> > plop(std::string(_name), out);
m_listGroup.insert(plop);
RIVER_INFO("Create a new group: " << _name << " ( END )");
} else {
RIVER_ERROR("Can not create new group: " << _name << " ( END )");
}
} else {
out = it->second;
}
return out;
}

145
audio/river/io/Manager.h Normal file
View File

@@ -0,0 +1,145 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_RIVER_IO_MANAGER_H__
#define __AUDIO_RIVER_IO_MANAGER_H__
#include <string>
#include <vector>
#include <map>
#include <list>
#include <stdint.h>
#include <etk/mutex.h>
#include <etk/chrono.h>
#include <etk/functional.h>
#include <etk/memory.h>
#include <audio/format.h>
#include <audio/channel.h>
#include <ejson/ejson.h>
#include <audio/drain/Volume.h>
#include <audio/river/io/Group.h>
namespace audio {
namespace river {
namespace io {
class Node;
/**
* @brief Internal sigleton of all Flow hadware and virtuals.
* @note this class will be initialize by the audio::river::init() function at the start of the application.
*/
class Manager : public std11::enable_shared_from_this<Manager> {
private:
mutable std11::recursive_mutex m_mutex; //!< prevent multiple access
private:
/**
* @brief Constructor
*/
Manager();
public:
static std11::shared_ptr<Manager> getInstance();
/**
* @brief Destructor
*/
~Manager();
/**
* @brief Called by audio::river::init() to set the hardware configuration file.
* @param[in] _filename Name of the file to initialize.
*/
void init(const std::string& _filename);
/**
* @brief Called by audio::river::initString() to set the hardware configuration string.
* @param[in] _data json configuration string.
*/
void initString(const std::string& _data);
/**
* @brief Called by audio::river::inInit() to uninitialize all the low level interface.
*/
void unInit();
private:
ejson::Document m_config; //!< harware configuration
std::vector<std11::shared_ptr<audio::river::io::Node> > m_listKeepAlive; //!< list of all Node that might be keep alive sone/all time
std::vector<std11::weak_ptr<audio::river::io::Node> > m_list; //!< List of all IO node
public:
/**
* @brief Get a node with his name (the name is set in the description file.
* @param[in] _name Name of the node
* @return Pointer on the noe or a nullptr if the node does not exist in the file or an error occured.
*/
std11::shared_ptr<audio::river::io::Node> getNode(const std::string& _name);
private:
std::vector<std11::shared_ptr<audio::drain::VolumeElement> > m_volumeGroup; //!< List of All global volume in the Low level interface.
public:
/**
* @brief Get a volume in the global list of vilume
* @param[in] _name Name of the volume.
* @return pointer on the requested volume (create it if does not exist). nullptr if the name is empty.
*/
std11::shared_ptr<audio::drain::VolumeElement> getVolumeGroup(const std::string& _name);
/**
* @brief Get all input audio stream.
* @return a list of all availlables input stream name
*/
std::vector<std::string> getListStreamInput();
/**
* @brief Get all output audio stream.
* @return a list of all availlables output stream name
*/
std::vector<std::string> getListStreamOutput();
/**
* @brief Get all audio virtual stream.
* @return a list of all availlables virtual stream name
*/
std::vector<std::string> getListStreamVirtual();
/**
* @brief Get all audio stream.
* @return a list of all availlables stream name
*/
std::vector<std::string> getListStream();
/**
* @brief Set a volume for a specific group
* @param[in] _volumeName Name of the volume (MASTER, MATER_BT ...)
* @param[in] _value Volume in dB to set.
* @return true set done
* @return false An error occured
* @example : setVolume("MASTER", -3.0f);
*/
bool setVolume(const std::string& _volumeName, float _valuedB);
/**
* @brief Get a volume value
* @param[in] _volumeName Name of the volume (MASTER, MATER_BT ...)
* @return The Volume value in dB.
* @example ret = getVolume("MASTER"); can return something like ret = -3.0f
*/
float getVolume(const std::string& _volumeName);
/**
* @brief Get a parameter value
* @param[in] _volumeName Name of the volume (MASTER, MATER_BT ...)
* @return The requested value Range.
* @example ret = getVolumeRange("MASTER"); can return something like ret=(-120.0f,0.0f)
*/
std::pair<float,float> getVolumeRange(const std::string& _volumeName) const;
/**
* @brief Generate the dot file corresponding at the actif nodes.
* @param[in] _filename Name of the file to write data.
*/
void generateDot(const std::string& _filename);
private:
std::map<std::string, std11::shared_ptr<audio::river::io::Group> > m_listGroup; //!< List of all groups
/**
* @brief get a low level interface group.
* @param[in] _name Name of the group.
* @return Pointer on the requested group or nullptr if the group does not existed.
*/
std11::shared_ptr<audio::river::io::Group> getGroup(const std::string& _name);
};
}
}
}
#endif

View File

@@ -5,13 +5,13 @@
*/ */
#include "Node.h" #include "Node.h"
#include <river/debug.h> #include <audio/river/debug.h>
#undef __class__ #undef __class__
#define __class__ "io::Node" #define __class__ "io::Node"
river::io::Node::Node(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) : audio::river::io::Node::Node(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) :
m_config(_config), m_config(_config),
m_name(_name), m_name(_name),
m_isInput(false) { m_isInput(false) {
@@ -20,8 +20,8 @@ river::io::Node::Node(const std::string& _name, const std11::shared_ptr<const ej
RIVER_INFO("-----------------------------------------------------------------"); RIVER_INFO("-----------------------------------------------------------------");
RIVER_INFO("-- CREATE NODE --"); RIVER_INFO("-- CREATE NODE --");
RIVER_INFO("-----------------------------------------------------------------"); RIVER_INFO("-----------------------------------------------------------------");
drain::IOFormatInterface interfaceFormat; audio::drain::IOFormatInterface interfaceFormat;
drain::IOFormatInterface hardwareFormat; audio::drain::IOFormatInterface hardwareFormat;
/** /**
io:"input", # input, output or aec io:"input", # input, output or aec
frequency:48000, # frequency to open device frequency:48000, # frequency to open device
@@ -35,6 +35,7 @@ river::io::Node::Node(const std::string& _name, const std11::shared_ptr<const ej
mux-demux-type:"int16_on_int32", mux-demux-type:"int16_on_int32",
*/ */
std::string interfaceType = m_config->getStringValue("io"); std::string interfaceType = m_config->getStringValue("io");
RIVER_INFO("interfaceType=" << interfaceType);
if ( interfaceType == "input" if ( interfaceType == "input"
|| interfaceType == "PAinput" || interfaceType == "PAinput"
|| interfaceType == "aec" || interfaceType == "aec"
@@ -53,7 +54,7 @@ river::io::Node::Node(const std::string& _name, const std11::shared_ptr<const ej
if (volumeName != "") { if (volumeName != "") {
RIVER_INFO("add node volume stage : '" << volumeName << "'"); RIVER_INFO("add node volume stage : '" << volumeName << "'");
// use global manager for volume ... // use global manager for volume ...
m_volume = river::io::Manager::getInstance()->getVolumeGroup(volumeName); m_volume = audio::river::io::Manager::getInstance()->getVolumeGroup(volumeName);
} }
// Get map type : // Get map type :
std::vector<audio::channel> map; std::vector<audio::channel> map;
@@ -72,7 +73,12 @@ river::io::Node::Node(const std::string& _name, const std11::shared_ptr<const ej
hardwareFormat.set(map, formatType, frequency); hardwareFormat.set(map, formatType, frequency);
std::string muxerDemuxerConfig = m_config->getStringValue("mux-demux-type", "int16-on-int32"); std::string muxerDemuxerConfig;
if (m_isInput == true) {
muxerDemuxerConfig = m_config->getStringValue("mux-demux-type", "int16");
} else {
muxerDemuxerConfig = m_config->getStringValue("mux-demux-type", "int16-on-int32");
}
enum audio::format muxerFormatType = audio::getFormatFromString(muxerDemuxerConfig); enum audio::format muxerFormatType = audio::getFormatFromString(muxerDemuxerConfig);
if (m_isInput == true) { if (m_isInput == true) {
if (muxerFormatType != audio::format_int16) { if (muxerFormatType != audio::format_int16) {
@@ -96,13 +102,13 @@ river::io::Node::Node(const std::string& _name, const std11::shared_ptr<const ej
//m_process.updateInterAlgo(); //m_process.updateInterAlgo();
} }
river::io::Node::~Node() { audio::river::io::Node::~Node() {
RIVER_INFO("-----------------------------------------------------------------"); RIVER_INFO("-----------------------------------------------------------------");
RIVER_INFO("-- DESTROY NODE --"); RIVER_INFO("-- DESTROY NODE --");
RIVER_INFO("-----------------------------------------------------------------"); RIVER_INFO("-----------------------------------------------------------------");
}; };
size_t river::io::Node::getNumberOfInterface(enum river::modeInterface _interfaceType) { size_t audio::river::io::Node::getNumberOfInterface(enum audio::river::modeInterface _interfaceType) {
size_t out = 0; size_t out = 0;
for (size_t iii=0; iii<m_list.size(); ++iii) { for (size_t iii=0; iii<m_list.size(); ++iii) {
if (m_list[iii] == nullptr) { if (m_list[iii] == nullptr) {
@@ -114,10 +120,10 @@ size_t river::io::Node::getNumberOfInterface(enum river::modeInterface _interfac
} }
return out; return out;
} }
size_t river::io::Node::getNumberOfInterfaceAvaillable(enum river::modeInterface _interfaceType) { size_t audio::river::io::Node::getNumberOfInterfaceAvaillable(enum audio::river::modeInterface _interfaceType) {
size_t out = 0; size_t out = 0;
for (size_t iii=0; iii<m_listAvaillable.size(); ++iii) { for (size_t iii=0; iii<m_listAvaillable.size(); ++iii) {
std11::shared_ptr<river::Interface> element = m_listAvaillable[iii].lock(); std11::shared_ptr<audio::river::Interface> element = m_listAvaillable[iii].lock();
if (element == nullptr) { if (element == nullptr) {
continue; continue;
} }
@@ -128,18 +134,19 @@ size_t river::io::Node::getNumberOfInterfaceAvaillable(enum river::modeInterface
return out; return out;
} }
void river::io::Node::registerAsRemote(const std11::shared_ptr<river::Interface>& _interface) { void audio::river::io::Node::registerAsRemote(const std11::shared_ptr<audio::river::Interface>& _interface) {
std::vector<std11::weak_ptr<river::Interface> >::iterator it = m_listAvaillable.begin(); std::vector<std11::weak_ptr<audio::river::Interface> >::iterator it = m_listAvaillable.begin();
while (it != m_listAvaillable.end()) { while (it != m_listAvaillable.end()) {
if (it->expired() == true) { if (it->expired() == true) {
it = m_listAvaillable.erase(it); it = m_listAvaillable.erase(it);
continue;
} }
++it; ++it;
} }
m_listAvaillable.push_back(_interface); m_listAvaillable.push_back(_interface);
} }
void river::io::Node::interfaceAdd(const std11::shared_ptr<river::Interface>& _interface) { void audio::river::io::Node::interfaceAdd(const std11::shared_ptr<audio::river::Interface>& _interface) {
{ {
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
for (size_t iii=0; iii<m_list.size(); ++iii) { for (size_t iii=0; iii<m_list.size(); ++iii) {
@@ -155,7 +162,7 @@ void river::io::Node::interfaceAdd(const std11::shared_ptr<river::Interface>& _i
} }
} }
void river::io::Node::interfaceRemove(const std11::shared_ptr<river::Interface>& _interface) { void audio::river::io::Node::interfaceRemove(const std11::shared_ptr<audio::river::Interface>& _interface) {
{ {
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
for (size_t iii=0; iii< m_list.size(); ++iii) { for (size_t iii=0; iii< m_list.size(); ++iii) {
@@ -173,45 +180,47 @@ void river::io::Node::interfaceRemove(const std11::shared_ptr<river::Interface>&
} }
void river::io::Node::volumeChange() { void audio::river::io::Node::volumeChange() {
for (size_t iii=0; iii< m_listAvaillable.size(); ++iii) { for (size_t iii=0; iii< m_listAvaillable.size(); ++iii) {
std11::shared_ptr<river::Interface> node = m_listAvaillable[iii].lock(); std11::shared_ptr<audio::river::Interface> node = m_listAvaillable[iii].lock();
if (node != nullptr) { if (node != nullptr) {
node->systemVolumeChange(); node->systemVolumeChange();
} }
} }
} }
int32_t river::io::Node::newInput(const void* _inputBuffer, void audio::river::io::Node::newInput(const void* _inputBuffer,
uint32_t _nbChunk, uint32_t _nbChunk,
const std11::chrono::system_clock::time_point& _time) { const audio::Time& _time) {
if (_inputBuffer == nullptr) { if (_inputBuffer == nullptr) {
return -1; return;
} }
const int16_t* inputBuffer = static_cast<const int16_t *>(_inputBuffer); const int16_t* inputBuffer = static_cast<const int16_t *>(_inputBuffer);
for (size_t iii=0; iii< m_list.size(); ++iii) { for (size_t iii=0; iii< m_list.size(); ++iii) {
if (m_list[iii] == nullptr) { if (m_list[iii] == nullptr) {
continue; continue;
} }
if (m_list[iii]->getMode() != river::modeInterface_input) { if (m_list[iii]->getMode() != audio::river::modeInterface_input) {
continue; continue;
} }
RIVER_VERBOSE(" IO name="<< m_list[iii]->getName()); RIVER_VERBOSE(" IO name="<< m_list[iii]->getName());
m_list[iii]->systemNewInputData(_time, inputBuffer, _nbChunk); m_list[iii]->systemNewInputData(_time, inputBuffer, _nbChunk);
} }
RIVER_VERBOSE("data Input size request :" << _nbChunk << " [ END ]"); RIVER_VERBOSE("data Input size request :" << _nbChunk << " [ END ]");
return 0; return;
} }
int32_t river::io::Node::newOutput(void* _outputBuffer, void audio::river::io::Node::newOutput(void* _outputBuffer,
uint32_t _nbChunk, uint32_t _nbChunk,
const std11::chrono::system_clock::time_point& _time) { const audio::Time& _time) {
if (_outputBuffer == nullptr) { if (_outputBuffer == nullptr) {
return -1; return;
} }
std::vector<int32_t> output; std::vector<int32_t> output;
RIVER_VERBOSE("resize=" << _nbChunk*m_process.getInputConfig().getMap().size()); RIVER_VERBOSE("resize=" << _nbChunk*m_process.getInputConfig().getMap().size());
output.resize(_nbChunk*m_process.getInputConfig().getMap().size(), 0); output.resize(_nbChunk*m_process.getInputConfig().getMap().size(), 0);
// TODO : set here the mixer selection ...
if (true) {
const int32_t* outputTmp = nullptr; const int32_t* outputTmp = nullptr;
std::vector<uint8_t> outputTmp2; std::vector<uint8_t> outputTmp2;
RIVER_VERBOSE("resize=" << sizeof(int32_t)*m_process.getInputConfig().getMap().size()*_nbChunk); RIVER_VERBOSE("resize=" << sizeof(int32_t)*m_process.getInputConfig().getMap().size()*_nbChunk);
@@ -220,36 +229,37 @@ int32_t river::io::Node::newOutput(void* _outputBuffer,
if (m_list[iii] == nullptr) { if (m_list[iii] == nullptr) {
continue; continue;
} }
if (m_list[iii]->getMode() != river::modeInterface_output) { if (m_list[iii]->getMode() != audio::river::modeInterface_output) {
continue; continue;
} }
RIVER_VERBOSE(" IO name="<< m_list[iii]->getName()); RIVER_VERBOSE(" IO name="<< m_list[iii]->getName() << " " << iii);
// clear datas ... // clear datas ...
memset(&outputTmp2[0], 0, sizeof(int32_t)*m_process.getInputConfig().getMap().size()*_nbChunk); memset(&outputTmp2[0], 0, sizeof(int32_t)*m_process.getInputConfig().getMap().size()*_nbChunk);
RIVER_VERBOSE(" request Data="<< _nbChunk << " time=" << _time); RIVER_VERBOSE(" request Data="<< _nbChunk << " time=" << _time);
m_list[iii]->systemNeedOutputData(_time, &outputTmp2[0], _nbChunk, sizeof(int32_t)*m_process.getInputConfig().getMap().size()); m_list[iii]->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]); outputTmp = reinterpret_cast<const int32_t*>(&outputTmp2[0]);
RIVER_VERBOSE(" Mix it ...");
// Add data to the output tmp buffer : // Add data to the output tmp buffer :
for (size_t kkk=0; kkk<output.size(); ++kkk) { for (size_t kkk=0; kkk<output.size(); ++kkk) {
output[kkk] += outputTmp[kkk]; output[kkk] += outputTmp[kkk];
} }
} }
}
RIVER_VERBOSE(" End stack process data ..."); RIVER_VERBOSE(" End stack process data ...");
m_process.processIn(&outputTmp2[0], _nbChunk, _outputBuffer, _nbChunk); m_process.processIn(&output[0], _nbChunk, _outputBuffer, _nbChunk);
RIVER_VERBOSE(" Feedback :"); RIVER_VERBOSE(" Feedback :");
for (size_t iii=0; iii< m_list.size(); ++iii) { for (size_t iii=0; iii< m_list.size(); ++iii) {
if (m_list[iii] == nullptr) { if (m_list[iii] == nullptr) {
continue; continue;
} }
if (m_list[iii]->getMode() != river::modeInterface_feedback) { if (m_list[iii]->getMode() != audio::river::modeInterface_feedback) {
continue; continue;
} }
RIVER_VERBOSE(" IO name="<< m_list[iii]->getName() << " (feedback) time=" << _time); RIVER_VERBOSE(" IO name="<< m_list[iii]->getName() << " (feedback) time=" << _time);
m_list[iii]->systemNewInputData(_time, _outputBuffer, _nbChunk); m_list[iii]->systemNewInputData(_time, _outputBuffer, _nbChunk);
} }
RIVER_VERBOSE("data Output size request :" << _nbChunk << " [ END ]"); RIVER_VERBOSE("data Output size request :" << _nbChunk << " [ END ]");
return 0; return;
} }
static void link(etk::FSNode& _node, const std::string& _first, const std::string& _op, const std::string& _second) { static void link(etk::FSNode& _node, const std::string& _first, const std::string& _op, const std::string& _second) {
@@ -262,7 +272,7 @@ static void link(etk::FSNode& _node, const std::string& _first, const std::strin
} }
void river::io::Node::generateDot(etk::FSNode& _node) { void audio::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=\"[" << m_uid << "] IO::Node : " << m_name << "\";\n"; _node << " label=\"[" << m_uid << "] IO::Node : " << m_name << "\";\n";
@@ -271,21 +281,21 @@ void river::io::Node::generateDot(etk::FSNode& _node) {
_node << " NODE_" << m_uid << "_HW_interface [ label=\"HW interface\\n interface=ALSA\\n stream=" << m_name << "\\n type=input\" ];\n"; _node << " NODE_" << m_uid << "_HW_interface [ label=\"HW interface\\n interface=ALSA\\n stream=" << m_name << "\\n type=input\" ];\n";
std::string nameIn; std::string nameIn;
std::string nameOut; std::string nameOut;
m_process.generateDot(_node, 3, m_uid, nameIn, nameOut, false); m_process.generateDotProcess(_node, 3, m_uid, nameIn, nameOut, false);
_node << " node [shape=square];\n"; _node << " node [shape=square];\n";
_node << " NODE_" << m_uid << "_demuxer [ label=\"DEMUXER\\n format=" << etk::to_string(m_process.getOutputConfig().getFormat()) << "\" ];\n"; _node << " NODE_" << m_uid << "_demuxer [ label=\"DEMUXER\\n format=" << etk::to_string(m_process.getOutputConfig().getFormat()) << "\" ];\n";
// Link all nodes : // Link all nodes :
_node << " NODE_" << m_uid << "_HW_interface -> " << nameIn << " [arrowhead=\"open\"];\n"; _node << " NODE_" << m_uid << "_HW_interface -> " << nameIn << " [arrowhead=\"open\"];\n";
_node << " " << nameOut << " -> NODE_" << m_uid << "_demuxer [arrowhead=\"open\"];\n"; _node << " " << nameOut << " -> NODE_" << m_uid << "_demuxer [arrowhead=\"open\"];\n";
} else { } else {
size_t nbOutput = getNumberOfInterfaceAvaillable(river::modeInterface_output); size_t nbOutput = getNumberOfInterfaceAvaillable(audio::river::modeInterface_output);
size_t nbfeedback = getNumberOfInterfaceAvaillable(river::modeInterface_feedback); size_t nbfeedback = getNumberOfInterfaceAvaillable(audio::river::modeInterface_feedback);
_node << " node [shape=larrow];\n"; _node << " node [shape=larrow];\n";
_node << " NODE_" << m_uid << "_HW_interface [ label=\"HW interface\\n interface=ALSA\\n stream=" << m_name << "\\n type=output\" ];\n"; _node << " NODE_" << m_uid << "_HW_interface [ label=\"HW interface\\n interface=ALSA\\n stream=" << m_name << "\\n type=output\" ];\n";
std::string nameIn; std::string nameIn;
std::string nameOut; std::string nameOut;
if (nbOutput>0) { if (nbOutput>0) {
m_process.generateDot(_node, 3, m_uid, nameIn, nameOut, true); m_process.generateDotProcess(_node, 3, m_uid, nameIn, nameOut, true);
} }
_node << " node [shape=square];\n"; _node << " node [shape=square];\n";
if (nbOutput>0) { if (nbOutput>0) {
@@ -314,7 +324,7 @@ void river::io::Node::generateDot(etk::FSNode& _node) {
if (m_listAvaillable[iii].expired() == true) { if (m_listAvaillable[iii].expired() == true) {
continue; continue;
} }
std11::shared_ptr<river::Interface> element = m_listAvaillable[iii].lock(); std11::shared_ptr<audio::river::Interface> element = m_listAvaillable[iii].lock();
if (element == nullptr) { if (element == nullptr) {
continue; continue;
} }
@@ -339,8 +349,8 @@ void river::io::Node::generateDot(etk::FSNode& _node) {
} }
void river::io::Node::startInGroup() { void audio::river::io::Node::startInGroup() {
std11::shared_ptr<river::io::Group> group = m_group.lock(); std11::shared_ptr<audio::river::io::Group> group = m_group.lock();
if (group != nullptr) { if (group != nullptr) {
group->start(); group->start();
} else { } else {
@@ -348,8 +358,8 @@ void river::io::Node::startInGroup() {
} }
} }
void river::io::Node::stopInGroup() { void audio::river::io::Node::stopInGroup() {
std11::shared_ptr<river::io::Group> group = m_group.lock(); std11::shared_ptr<audio::river::io::Group> group = m_group.lock();
if (group != nullptr) { if (group != nullptr) {
group->stop(); group->stop();
} else { } else {

226
audio/river/io/Node.h Normal file
View File

@@ -0,0 +1,226 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_RIVER_IO_NODE_H__
#define __AUDIO_RIVER_IO_NODE_H__
#include <string>
#include <vector>
#include <list>
#include <stdint.h>
#include <etk/chrono.h>
#include <etk/functional.h>
#include <etk/memory.h>
#include <audio/format.h>
#include <audio/channel.h>
#include "Manager.h"
#include <audio/river/Interface.h>
#include <audio/drain/IOFormatInterface.h>
#include <audio/drain/Volume.h>
#include <etk/os/FSNode.h>
namespace audio {
namespace river {
namespace io {
class Manager;
class Group;
/**
* @brief A node is the base for input/output interface. When a output id declared, we automaticly have a feedback associated.
* this manage the muxing of data for output an the demuxing for input.
*/
class Node : public std11::enable_shared_from_this<Node> {
friend class audio::river::io::Group;
protected:
uint32_t m_uid; //!< uniqueNodeID use for debug an dot generation.
protected:
/**
* @brief Constructor
* @param[in] _name Name of the node.
* @param[in] _config Configuration of the node.
*/
Node(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
public:
/**
* @brief Destructor
*/
virtual ~Node();
/**
* @brief Get the status of this node acces on harware or acces on other node (virtual).
* @return true This is an harware interface.
* @return false this is a virtual interface.
*/
virtual bool isHarwareNode() {
return false;
};
protected:
mutable std11::mutex m_mutex; //!< prevent open/close/write/read access that is multi-threaded.
std11::shared_ptr<const ejson::Object> m_config; //!< configuration description.
protected:
audio::drain::Process m_process; //!< Low level algorithms
public:
/**
* @brief Get the uper client interface configuration.
* @return process configuration.
*/
const audio::drain::IOFormatInterface& getInterfaceFormat() {
if (m_isInput == true) {
return m_process.getOutputConfig();
} else {
return m_process.getInputConfig();
}
}
/**
* @brief Get the harware client interface configuration.
* @return process configuration.
*/
const audio::drain::IOFormatInterface& getHarwareFormat() {
if (m_isInput == true) {
return m_process.getInputConfig();
} else {
return m_process.getOutputConfig();
}
}
protected:
std11::shared_ptr<audio::drain::VolumeElement> m_volume; //!< if a volume is set it is set here ... for hardware interface only.
protected:
std::vector<std11::weak_ptr<audio::river::Interface> > m_listAvaillable; //!< List of all interface that exist on this Node
std::vector<std11::shared_ptr<audio::river::Interface> > m_list; //!< List of all connected interface at this node.
/**
* @brief Get the number of interface with a specific type.
* @param[in] _interfaceType Type of the interface.
* @return Number of interface connected.
*/
size_t getNumberOfInterface(enum audio::river::modeInterface _interfaceType);
/**
* @brief Get the number of interface with a specific type that can connect on the Node.
* @param[in] _interfaceType Type of the interface.
* @return Number of interface that can connect.
*/
size_t getNumberOfInterfaceAvaillable(enum audio::river::modeInterface _interfaceType);
public:
/**
* @brief Get the number of interface connected
* @return Number of interfaces.
*/
size_t getNumberOfInterface() {
return m_list.size();
}
public:
/**
* @brief Register an interface that can connect on it. (might be done in the Interface Init)
* @note We keep a std::weak_ptr. this is the reason why we do not have a remove.
* @param[in] _interface Pointer on the interface to register.
*/
void registerAsRemote(const std11::shared_ptr<audio::river::Interface>& _interface);
/**
* @brief Request this interface might receve/send dat on the flow. (start/resume)
* @param[in] _interface Pointer on the interface to register.
*/
void interfaceAdd(const std11::shared_ptr<audio::river::Interface>& _interface);
/**
* @brief Un-register the interface as an availlable read/write interface. (suspend/stop)
* @param[in] _interface Pointer on the interface to register.
*/
void interfaceRemove(const std11::shared_ptr<audio::river::Interface>& _interface);
protected:
std::string m_name; //!< Name of the interface
public:
/**
* @brief Get the interface name.
* @return Current name.
*/
const std::string& getName() {
return m_name;
}
protected:
bool m_isInput; //!< sense of the stream
public:
/**
* @brief Check if it is an input stream
* @return true if it is an input/ false otherwise
*/
bool isInput() {
return m_isInput;
}
/**
* @brief Check if it is an output stream
* @return true if it is an output/ false otherwise
*/
bool isOutput() {
return !m_isInput;
}
protected:
std11::weak_ptr<audio::river::io::Group> m_group; //!< reference on the group. If available.
public:
/**
* @brief Set this node in a low level group.
* @param[in] _group Group reference.
*/
void setGroup(std11::shared_ptr<audio::river::io::Group> _group) {
m_group = _group;
}
protected:
/**
* @brief Start the flow in the group (start if no group)
*/
void startInGroup();
/**
* @brief Stop the flow in the group (stop if no group)
*/
void stopInGroup();
/**
* @brief Real start of the stream
*/
virtual void start() = 0;
/**
* @brief Real stop of the stream
*/
virtual void stop() = 0;
public:
/**
* @brief If this iss an hardware interface we can have a resuest of the volume stage:
* @return pointer on the requested volume.
*/
const std11::shared_ptr<audio::drain::VolumeElement>& getVolume() {
return m_volume;
}
public:
/**
* @brief Called when a group wolume has been change to update all volume stage.
*/
void volumeChange();
protected:
/**
* @brief Call by child classes to process data in all interface linked on the current Node. Have new input to process.
* @param[in] _inputBuffer Pointer on the data.
* @param[in] _nbChunk Number of chunk in the buffer.
* @param[in] _time Time where the first sample has been capture.
*/
void newInput(const void* _inputBuffer,
uint32_t _nbChunk,
const audio::Time& _time);
/**
* @brief Call by child classes to process data in all interface linked on the current Node. Have new output to get. this call the feedback too.
* @param[in,out] _outputBuffer Pointer on the buffer to write the data.
* @param[in] _nbChunk Number of chunk to write in the buffer.
* @param[in] _time Time where the data might be played.
*/
void newOutput(void* _outputBuffer,
uint32_t _nbChunk,
const audio::Time& _time);
public:
/**
* @brief Generate the node dot file section
* @param[in] _node File node to generate the data.
*/
virtual void generateDot(etk::FSNode& _node);
};
}
}
}
#endif

View File

@@ -4,8 +4,8 @@
* @license APACHE v2.0 (see license file) * @license APACHE v2.0 (see license file)
*/ */
#include <river/io/NodeAEC.h> #include <audio/river/io/NodeAEC.h>
#include <river/debug.h> #include <audio/river/debug.h>
#include <etk/types.h> #include <etk/types.h>
#include <etk/memory.h> #include <etk/memory.h>
#include <etk/functional.h> #include <etk/functional.h>
@@ -13,86 +13,11 @@
#undef __class__ #undef __class__
#define __class__ "io::NodeAEC" #define __class__ "io::NodeAEC"
#if 0 std11::shared_ptr<audio::river::io::NodeAEC> audio::river::io::NodeAEC::create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) {
int32_t river::io::NodeAEC::airtAudioCallback(void* _outputBuffer, return std11::shared_ptr<audio::river::io::NodeAEC>(new audio::river::io::NodeAEC(_name, _config));
void* _inputBuffer,
uint32_t _nbChunk,
const std11::chrono::system_clock::time_point& _time,
airtaudio::status _status) {
std11::unique_lock<std11::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
std11::shared_ptr<river::io::NodeAEC> river::io::NodeAEC::create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) {
return std11::shared_ptr<river::io::NodeAEC>(new river::io::NodeAEC(_name, _config));
} }
std11::shared_ptr<river::Interface> river::io::NodeAEC::createInput(float _freq, std11::shared_ptr<audio::river::Interface> audio::river::io::NodeAEC::createInput(float _freq,
const std::vector<audio::channel>& _map, const std::vector<audio::channel>& _map,
audio::format _format, audio::format _format,
const std::string& _objectName, const std::string& _objectName,
@@ -101,34 +26,42 @@ std11::shared_ptr<river::Interface> river::io::NodeAEC::createInput(float _freq,
const std11::shared_ptr<const ejson::Object> tmppp = m_config->getObject(_objectName); const std11::shared_ptr<const ejson::Object> tmppp = m_config->getObject(_objectName);
if (tmppp == nullptr) { if (tmppp == nullptr) {
RIVER_ERROR("can not open a non existance virtual interface: '" << _objectName << "' not present in : " << m_config->getKeys()); RIVER_ERROR("can not open a non existance virtual interface: '" << _objectName << "' not present in : " << m_config->getKeys());
return std11::shared_ptr<river::Interface>(); return std11::shared_ptr<audio::river::Interface>();
} }
std::string streamName = tmppp->getStringValue("map-on", "error"); std::string streamName = tmppp->getStringValue("map-on", "error");
m_nbChunk = m_config->getNumberValue("nb-chunk", 1024);
// check if it is an Output: // check if it is an Output:
std::string type = tmppp->getStringValue("io", "error"); std::string type = tmppp->getStringValue("io", "error");
if ( type != "input" if ( type != "input"
&& type != "feedback") { && type != "feedback") {
RIVER_ERROR("can not open in output a virtual interface: '" << streamName << "' configured has : " << type); RIVER_ERROR("can not open in output a virtual interface: '" << streamName << "' configured has : " << type);
return std11::shared_ptr<river::Interface>(); return std11::shared_ptr<audio::river::Interface>();
} }
// get global hardware interface: // get global hardware interface:
std11::shared_ptr<river::io::Manager> manager = river::io::Manager::getInstance(); std11::shared_ptr<audio::river::io::Manager> manager = audio::river::io::Manager::getInstance();
// get the output or input channel : // get the output or input channel :
std11::shared_ptr<river::io::Node> node = manager->getNode(streamName); std11::shared_ptr<audio::river::io::Node> node = manager->getNode(streamName);
// create user iterface: // create user iterface:
std11::shared_ptr<river::Interface> interface; std11::shared_ptr<audio::river::Interface> interface;
interface = river::Interface::create(_name, _freq, _map, _format, node, tmppp); interface = audio::river::Interface::create(_freq, _map, _format, node, tmppp);
if (interface != nullptr) {
interface->setName(_name);
}
return interface; return interface;
} }
river::io::NodeAEC::NodeAEC(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) : audio::river::io::NodeAEC::NodeAEC(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) :
Node(_name, _config) { Node(_name, _config),
drain::IOFormatInterface interfaceFormat = getInterfaceFormat(); m_P_attaqueTime(1),
drain::IOFormatInterface hardwareFormat = getHarwareFormat(); m_P_releaseTime(100),
m_sampleTime = std11::chrono::nanoseconds(1000000000/int64_t(hardwareFormat.getFrequency())); m_P_minimumGain(10),
m_P_threshold(2),
m_P_latencyTime(100) {
audio::drain::IOFormatInterface interfaceFormat = getInterfaceFormat();
audio::drain::IOFormatInterface hardwareFormat = getHarwareFormat();
m_sampleTime = audio::Duration(1000000000/int64_t(hardwareFormat.getFrequency()));
/** /**
# connect in input mode # connect in input mode
map-on-microphone:{ map-on-microphone:{
@@ -173,7 +106,7 @@ river::io::NodeAEC::NodeAEC(const std::string& _name, const std11::shared_ptr<co
} }
// set callback mode ... // set callback mode ...
m_interfaceFeedBack->setInputCallback(std11::bind(&river::io::NodeAEC::onDataReceivedFeedBack, m_interfaceFeedBack->setInputCallback(std11::bind(&audio::river::io::NodeAEC::onDataReceivedFeedBack,
this, this,
std11::placeholders::_1, std11::placeholders::_1,
std11::placeholders::_2, std11::placeholders::_2,
@@ -182,7 +115,7 @@ river::io::NodeAEC::NodeAEC(const std::string& _name, const std11::shared_ptr<co
std11::placeholders::_5, std11::placeholders::_5,
std11::placeholders::_6)); std11::placeholders::_6));
// set callback mode ... // set callback mode ...
m_interfaceMicrophone->setInputCallback(std11::bind(&river::io::NodeAEC::onDataReceivedMicrophone, m_interfaceMicrophone->setInputCallback(std11::bind(&audio::river::io::NodeAEC::onDataReceivedMicrophone,
this, this,
std11::placeholders::_1, std11::placeholders::_1,
std11::placeholders::_2, std11::placeholders::_2,
@@ -201,14 +134,14 @@ river::io::NodeAEC::NodeAEC(const std::string& _name, const std11::shared_ptr<co
m_process.updateInterAlgo(); m_process.updateInterAlgo();
} }
river::io::NodeAEC::~NodeAEC() { audio::river::io::NodeAEC::~NodeAEC() {
RIVER_INFO("close input stream"); RIVER_INFO("close input stream");
stop(); stop();
m_interfaceFeedBack.reset(); m_interfaceFeedBack.reset();
m_interfaceMicrophone.reset(); m_interfaceMicrophone.reset();
}; };
void river::io::NodeAEC::start() { void audio::river::io::NodeAEC::start() {
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
RIVER_INFO("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") ); RIVER_INFO("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
if (m_interfaceFeedBack != nullptr) { if (m_interfaceFeedBack != nullptr) {
@@ -221,7 +154,7 @@ void river::io::NodeAEC::start() {
} }
} }
void river::io::NodeAEC::stop() { void audio::river::io::NodeAEC::stop() {
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
if (m_interfaceFeedBack != nullptr) { if (m_interfaceFeedBack != nullptr) {
m_interfaceFeedBack->stop(); m_interfaceFeedBack->stop();
@@ -232,14 +165,14 @@ void river::io::NodeAEC::stop() {
} }
void river::io::NodeAEC::onDataReceivedMicrophone(const void* _data, void audio::river::io::NodeAEC::onDataReceivedMicrophone(const void* _data,
const std11::chrono::system_clock::time_point& _time, const audio::Time& _time,
size_t _nbChunk, size_t _nbChunk,
enum audio::format _format, enum audio::format _format,
uint32_t _frequency, uint32_t _frequency,
const std::vector<audio::channel>& _map) { const std::vector<audio::channel>& _map) {
RIVER_DEBUG("Microphone Time=" << _time << " _nbChunk=" << _nbChunk << " _map=" << _map << " _format=" << _format << " freq=" << _frequency); RIVER_DEBUG("Microphone Time=" << _time << " _nbChunk=" << _nbChunk << " _map=" << _map << " _format=" << _format << " freq=" << _frequency);
RIVER_DEBUG(" next=" << _time + std11::chrono::nanoseconds(_nbChunk*1000000000LL/int64_t(_frequency)) ); RIVER_DEBUG(" next=" << _time + audio::Duration(0, _nbChunk*1000000000LL/int64_t(_frequency)) );
if (_format != audio::format_int16) { if (_format != audio::format_int16) {
RIVER_ERROR("call wrong type ... (need int16_t)"); RIVER_ERROR("call wrong type ... (need int16_t)");
} }
@@ -250,14 +183,14 @@ void river::io::NodeAEC::onDataReceivedMicrophone(const void* _data,
process(); process();
} }
void river::io::NodeAEC::onDataReceivedFeedBack(const void* _data, void audio::river::io::NodeAEC::onDataReceivedFeedBack(const void* _data,
const std11::chrono::system_clock::time_point& _time, const audio::Time& _time,
size_t _nbChunk, size_t _nbChunk,
enum audio::format _format, enum audio::format _format,
uint32_t _frequency, uint32_t _frequency,
const std::vector<audio::channel>& _map) { const std::vector<audio::channel>& _map) {
RIVER_DEBUG("FeedBack Time=" << _time << " _nbChunk=" << _nbChunk << " _map=" << _map << " _format=" << _format << " freq=" << _frequency); RIVER_DEBUG("FeedBack Time=" << _time << " _nbChunk=" << _nbChunk << " _map=" << _map << " _format=" << _format << " freq=" << _frequency);
RIVER_DEBUG(" next=" << _time + std11::chrono::nanoseconds(_nbChunk*1000000000LL/int64_t(_frequency)) ); RIVER_DEBUG(" next=" << _time + audio::Duration(0, _nbChunk*1000000000LL/int64_t(_frequency)) );
if (_format != audio::format_int16) { if (_format != audio::format_int16) {
RIVER_ERROR("call wrong type ... (need int16_t)"); RIVER_ERROR("call wrong type ... (need int16_t)");
} }
@@ -268,16 +201,14 @@ void river::io::NodeAEC::onDataReceivedFeedBack(const void* _data,
process(); process();
} }
void river::io::NodeAEC::process() { void audio::river::io::NodeAEC::process() {
if (m_bufferMicrophone.getSize() <= 256) { if ( m_bufferMicrophone.getSize() <= m_nbChunk
|| m_bufferFeedBack.getSize() <= m_nbChunk) {
return; return;
} }
if (m_bufferFeedBack.getSize() <= 256) { audio::Time MicTime = m_bufferMicrophone.getReadTimeStamp();
return; audio::Time fbTime = m_bufferFeedBack.getReadTimeStamp();
} audio::Duration delta;
std11::chrono::system_clock::time_point MicTime = m_bufferMicrophone.getReadTimeStamp();
std11::chrono::system_clock::time_point fbTime = m_bufferFeedBack.getReadTimeStamp();
std11::chrono::nanoseconds delta;
if (MicTime < fbTime) { if (MicTime < fbTime) {
delta = fbTime - MicTime; delta = fbTime - MicTime;
} else { } else {
@@ -301,10 +232,8 @@ void river::io::NodeAEC::process() {
} }
} }
// check if enought time after synchronisation ... // check if enought time after synchronisation ...
if (m_bufferMicrophone.getSize() <= 256) { if ( m_bufferMicrophone.getSize() <= m_nbChunk
return; || m_bufferFeedBack.getSize() <= m_nbChunk) {
}
if (m_bufferFeedBack.getSize() <= 256) {
return; return;
} }
@@ -317,58 +246,93 @@ void river::io::NodeAEC::process() {
} }
std::vector<uint8_t> dataMic; std::vector<uint8_t> dataMic;
std::vector<uint8_t> dataFB; std::vector<uint8_t> dataFB;
dataMic.resize(256*sizeof(int16_t)*2, 0); dataMic.resize(m_nbChunk*sizeof(int16_t)*2, 0);
dataFB.resize(256*sizeof(int16_t), 0); dataFB.resize(m_nbChunk*sizeof(int16_t), 0);
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=" << MicTime << " fbTime=" << fbTime << " delta = " << (MicTime-fbTime).count()); RIVER_INFO(" process 256 samples ... micTime=" << MicTime << " fbTime=" << fbTime << " delta = " << (MicTime-fbTime).count());
m_bufferMicrophone.read(&dataMic[0], 256); m_bufferMicrophone.read(&dataMic[0], m_nbChunk);
m_bufferFeedBack.read(&dataFB[0], 256); m_bufferFeedBack.read(&dataFB[0], m_nbChunk);
RIVER_SAVE_FILE_MACRO(int16_t, "REC_Microphone_sync.raw", &dataMic[0], 256*2); RIVER_SAVE_FILE_MACRO(int16_t, "REC_Microphone_sync.raw", &dataMic[0], m_nbChunk*getHarwareFormat().getMap().size());
RIVER_SAVE_FILE_MACRO(int16_t, "REC_FeedBack_sync.raw", &dataFB[0], 256); RIVER_SAVE_FILE_MACRO(int16_t, "REC_FeedBack_sync.raw", &dataFB[0], m_nbChunk);
// if threaded : send event / otherwise, process ... // if threaded : send event / otherwise, process ...
//processAEC(&dataMic[0], &dataFB[0], 256, _time); processAEC(&dataMic[0], &dataFB[0], m_nbChunk, MicTime);
if (m_bufferMicrophone.getSize() <= 256) { if ( m_bufferMicrophone.getSize() <= m_nbChunk
return; || m_bufferFeedBack.getSize() <= m_nbChunk) {
}
if (m_bufferFeedBack.getSize() <= 256) {
return; return;
} }
} }
} }
void river::io::NodeAEC::processAEC(void* _dataMic, void* _dataFB, uint32_t _nbChunk, const std11::chrono::system_clock::time_point& _time) { void audio::river::io::NodeAEC::processAEC(void* _dataMic, void* _dataFB, uint32_t _nbChunk, const audio::Time& _time) {
audio::drain::IOFormatInterface hardwareFormat = getHarwareFormat();
// TODO : Set all these parameter in the parameter configuration section ...
int32_t attaqueTime = std::min(std::max(0,m_P_attaqueTime),1000);
int32_t releaseTime = std::min(std::max(0,m_P_releaseTime),1000);
int32_t min_gain = 32767 * std::min(std::max(0,m_P_minimumGain),1000) / 1000;
int32_t threshold = 32767 * std::min(std::max(0,m_P_threshold),1000) / 1000;
int32_t latencyTime = std::min(std::max(0,m_P_latencyTime),1000);
int32_t nb_sample_latency = (hardwareFormat.getFrequency()/1000)*latencyTime;
int32_t increaseSample = 32767;
if (attaqueTime != 0) {
increaseSample = 32767/(hardwareFormat.getFrequency() * attaqueTime / 1000);
}
int32_t decreaseSample = 32767;
if (attaqueTime != 0) {
decreaseSample = 32767/(hardwareFormat.getFrequency() * releaseTime / 1000);
}
// Process section:
int16_t* dataMic = static_cast<int16_t*>(_dataMic);
int16_t* dataFB = static_cast<int16_t*>(_dataFB);
for (size_t iii=0; iii<_nbChunk; ++iii) {
if (abs(*dataFB++) > threshold) {
m_sampleCount = 0;
} else {
m_sampleCount++;
}
if (m_sampleCount > nb_sample_latency) {
m_gainValue += decreaseSample;
if (m_gainValue >= 32767) {
m_gainValue = 32767;
}
} else {
if (m_gainValue <= increaseSample) {
m_gainValue = 0;
} else {
m_gainValue -= increaseSample;
}
if (m_gainValue < min_gain) {
m_gainValue = min_gain;
}
}
for (size_t jjj=0; jjj<hardwareFormat.getMap().size(); ++jjj) {
*dataMic = static_cast<int16_t>((*dataMic * m_gainValue) >> 15);
dataMic++;
}
}
RIVER_SAVE_FILE_MACRO(int16_t, "REC_Microphone_clean.raw", _dataMic, _nbChunk*getHarwareFormat().getMap().size());
// simply send to upper requester...
newInput(_dataMic, _nbChunk, _time); newInput(_dataMic, _nbChunk, _time);
} }
void river::io::NodeAEC::generateDot(etk::FSNode& _node) { void audio::river::io::NodeAEC::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=\"[" << m_uid << "] IO::Node : " << m_name << "\";\n"; _node << " label=\"[" << m_uid << "] IO::Node : " << m_name << "\";\n";
_node << " NODE_" << m_uid << "_HW_AEC [ label=\"AEC\\n channelMap=" << etk::to_string(getInterfaceFormat().getMap()) << "\" ];\n";
_node << " node [shape=box];\n"; std::string nameIn;
// TODO : Create a structure ... std::string nameOut;
_node << " NODE_" << m_uid << "_HW_AEC [ label=\"AEC\" ];\n"; m_process.generateDot(_node, 3, m_uid, nameIn, nameOut, false);
_node << " subgraph clusterNode_" << m_uid << "_process {\n";
_node << " label=\"Drain::Process\";\n";
_node << " node [shape=ellipse];\n";
_node << " node_ALGO_" << m_uid << "_in [ label=\"format=" << etk::to_string(m_process.getInputConfig().getFormat())
<< "\\n freq=" << m_process.getInputConfig().getFrequency()
<< "\\n channelMap=" << etk::to_string(m_process.getInputConfig().getMap()) << "\" ];\n";
_node << " node_ALGO_" << m_uid << "_out [ label=\"format=" << etk::to_string(m_process.getOutputConfig().getFormat())
<< "\\n freq=" << m_process.getOutputConfig().getFrequency()
<< "\\n channelMap=" << etk::to_string(m_process.getOutputConfig().getMap()) << "\" ];\n";
_node << " }\n";
_node << " node [shape=square];\n"; _node << " node [shape=square];\n";
_node << " NODE_" << m_uid << "_demuxer [ label=\"DEMUXER\\n format=" << etk::to_string(m_process.getOutputConfig().getFormat()) << "\" ];\n"; _node << " NODE_" << m_uid << "_demuxer [ label=\"DEMUXER\\n format=" << etk::to_string(m_process.getOutputConfig().getFormat()) << "\" ];\n";
// Link all nodes : // Link all nodes :
_node << " NODE_" << m_uid << "_HW_AEC -> node_ALGO_" << m_uid << "_in;\n"; _node << " NODE_" << m_uid << "_HW_AEC -> " << nameIn << ";\n";
_node << " node_ALGO_" << m_uid << "_in -> node_ALGO_" << m_uid << "_out;\n"; _node << " " << nameOut << " -> NODE_" << m_uid << "_demuxer;\n";
_node << " node_ALGO_" << m_uid << "_out -> NODE_" << m_uid << "_demuxer;\n";
_node << " }\n"; _node << " }\n";
if (m_interfaceMicrophone != nullptr) { if (m_interfaceMicrophone != nullptr) {
_node << " " << m_interfaceMicrophone->getDotNodeName() << " -> NODE_" << m_uid << "_HW_AEC;\n"; _node << " " << m_interfaceMicrophone->getDotNodeName() << " -> NODE_" << m_uid << "_HW_AEC;\n";
@@ -376,15 +340,29 @@ void river::io::NodeAEC::generateDot(etk::FSNode& _node) {
if (m_interfaceFeedBack != nullptr) { if (m_interfaceFeedBack != nullptr) {
_node << " " << m_interfaceFeedBack->getDotNodeName() << " -> NODE_" << m_uid << "_HW_AEC;\n"; _node << " " << m_interfaceFeedBack->getDotNodeName() << " -> NODE_" << m_uid << "_HW_AEC;\n";
} }
_node << " \n";
for (size_t iii=0; iii<m_list.size(); ++iii) { for (size_t iii=0; iii< m_listAvaillable.size(); ++iii) {
if (m_list[iii] != nullptr) { if (m_listAvaillable[iii].expired() == true) {
if (m_list[iii]->getMode() == modeInterface_input) { continue;
m_list[iii]->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_demuxer"); }
} else if (m_list[iii]->getMode() == modeInterface_output) { std11::shared_ptr<audio::river::Interface> element = m_listAvaillable[iii].lock();
m_list[iii]->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_muxer"); if (element == nullptr) {
} else if (m_list[iii]->getMode() == modeInterface_feedback) { continue;
m_list[iii]->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_demuxer"); }
bool isLink = false;
for (size_t jjj=0; jjj<m_list.size(); ++jjj) {
if (element == m_list[jjj]) {
isLink = true;
}
}
if (element != nullptr) {
if (element->getMode() == modeInterface_input) {
element->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_demuxer", isLink);
} else if (element->getMode() == modeInterface_output) {
element->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_muxer", isLink);
} else if (element->getMode() == modeInterface_feedback) {
element->generateDot(_node, "NODE_" + etk::to_string(m_uid) + "_demuxer", isLink);
} else { } else {
} }

110
audio/river/io/NodeAEC.h Normal file
View File

@@ -0,0 +1,110 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_RIVER_IO_NODE_AEC_H__
#define __AUDIO_RIVER_IO_NODE_AEC_H__
#include <audio/river/io/Node.h>
#include <audio/river/Interface.h>
#include <audio/drain/CircularBuffer.h>
namespace audio {
namespace river {
namespace io {
class Manager;
class NodeAEC : public Node {
protected:
/**
* @brief Constructor
*/
NodeAEC(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
public:
/**
* @brief Factory of this Virtual Node.
* @param[in] _name Name of the node.
* @param[in] _config Configuration of the node.
*/
static std11::shared_ptr<NodeAEC> create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
/**
* @brief Destructor
*/
virtual ~NodeAEC();
protected:
virtual void start();
virtual void stop();
std11::shared_ptr<audio::river::Interface> m_interfaceMicrophone; //!< Interface on the Microphone.
std11::shared_ptr<audio::river::Interface> m_interfaceFeedBack; //!< Interface on the feedback of speaker.
/**
* @brief Internal: create an input with the specific parameter:
* @param[in] _freq Frequency.
* @param[in] _map Channel map organization.
* @param[in] _format Sample format
* @param[in] _streamName
* @param[in] _name
* @return Interfae Pointer.
*/
std11::shared_ptr<audio::river::Interface> createInput(float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std::string& _streamName,
const std::string& _name);
/**
* @brief Stream data input callback
* @todo : copy doc ..
*/
void onDataReceivedMicrophone(const void* _data,
const audio::Time& _time,
size_t _nbChunk,
enum audio::format _format,
uint32_t _frequency,
const std::vector<audio::channel>& _map);
/**
* @brief Stream data input callback
* @todo : copy doc ..
*/
void onDataReceivedFeedBack(const void* _data,
const audio::Time& _time,
size_t _nbChunk,
enum audio::format _format,
uint32_t _frequency,
const std::vector<audio::channel>& _map);
protected:
audio::drain::CircularBuffer m_bufferMicrophone; //!< temporary buffer to synchronize data.
audio::drain::CircularBuffer m_bufferFeedBack; //!< temporary buffer to synchronize data.
audio::Duration m_sampleTime; //!< represent the sample time at the specify frequency.
/**
* @brief Process synchronization on the 2 flow.
*/
void process();
/**
* @brief Process algorithm on the current 2 syncronize flow.
* @param[in] _dataMic Pointer in the Microphione interface.
* @param[in] _dataFB Pointer on the beedback buffer.
* @param[in] _nbChunk Number of chunk to process.
* @param[in] _time Time on the firsta sample that data has been captured.
* @return
*/
void processAEC(void* _dataMic, void* _dataFB, uint32_t _nbChunk, const audio::Time& _time);
public:
virtual void generateDot(etk::FSNode& _node);
private:
int32_t m_nbChunk;
int32_t m_gainValue;
int32_t m_sampleCount;
int32_t m_P_attaqueTime; //ms
int32_t m_P_releaseTime; //ms
int32_t m_P_minimumGain; // %
int32_t m_P_threshold; // %
int32_t m_P_latencyTime; // ms
};
}
}
}
#endif

View File

@@ -4,8 +4,8 @@
* @license APACHE v2.0 (see license file) * @license APACHE v2.0 (see license file)
*/ */
#include <river/io/NodeMuxer.h> #include <audio/river/io/NodeMuxer.h>
#include <river/debug.h> #include <audio/river/debug.h>
#include <etk/types.h> #include <etk/types.h>
#include <etk/memory.h> #include <etk/memory.h>
#include <etk/functional.h> #include <etk/functional.h>
@@ -13,11 +13,11 @@
#undef __class__ #undef __class__
#define __class__ "io::NodeMuxer" #define __class__ "io::NodeMuxer"
std11::shared_ptr<river::io::NodeMuxer> river::io::NodeMuxer::create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) { std11::shared_ptr<audio::river::io::NodeMuxer> audio::river::io::NodeMuxer::create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) {
return std11::shared_ptr<river::io::NodeMuxer>(new river::io::NodeMuxer(_name, _config)); return std11::shared_ptr<audio::river::io::NodeMuxer>(new audio::river::io::NodeMuxer(_name, _config));
} }
std11::shared_ptr<river::Interface> river::io::NodeMuxer::createInput(float _freq, std11::shared_ptr<audio::river::Interface> audio::river::io::NodeMuxer::createInput(float _freq,
const std::vector<audio::channel>& _map, const std::vector<audio::channel>& _map,
audio::format _format, audio::format _format,
const std::string& _objectName, const std::string& _objectName,
@@ -26,7 +26,7 @@ std11::shared_ptr<river::Interface> river::io::NodeMuxer::createInput(float _fre
const std11::shared_ptr<const ejson::Object> tmppp = m_config->getObject(_objectName); const std11::shared_ptr<const ejson::Object> tmppp = m_config->getObject(_objectName);
if (tmppp == nullptr) { if (tmppp == nullptr) {
RIVER_ERROR("can not open a non existance virtual interface: '" << _objectName << "' not present in : " << m_config->getKeys()); RIVER_ERROR("can not open a non existance virtual interface: '" << _objectName << "' not present in : " << m_config->getKeys());
return std11::shared_ptr<river::Interface>(); return std11::shared_ptr<audio::river::Interface>();
} }
std::string streamName = tmppp->getStringValue("map-on", "error"); std::string streamName = tmppp->getStringValue("map-on", "error");
@@ -36,24 +36,27 @@ std11::shared_ptr<river::Interface> river::io::NodeMuxer::createInput(float _fre
if ( type != "input" if ( type != "input"
&& type != "feedback") { && type != "feedback") {
RIVER_ERROR("can not open in output a virtual interface: '" << streamName << "' configured has : " << type); RIVER_ERROR("can not open in output a virtual interface: '" << streamName << "' configured has : " << type);
return std11::shared_ptr<river::Interface>(); return std11::shared_ptr<audio::river::Interface>();
} }
// get global hardware interface: // get global hardware interface:
std11::shared_ptr<river::io::Manager> manager = river::io::Manager::getInstance(); std11::shared_ptr<audio::river::io::Manager> manager = audio::river::io::Manager::getInstance();
// get the output or input channel : // get the output or input channel :
std11::shared_ptr<river::io::Node> node = manager->getNode(streamName); std11::shared_ptr<audio::river::io::Node> node = manager->getNode(streamName);
// create user iterface: // create user iterface:
std11::shared_ptr<river::Interface> interface; std11::shared_ptr<audio::river::Interface> interface;
interface = river::Interface::create(_name, _freq, _map, _format, node, tmppp); interface = audio::river::Interface::create(_freq, _map, _format, node, tmppp);
if (interface != nullptr) {
interface->setName(_name);
}
return interface; return interface;
} }
river::io::NodeMuxer::NodeMuxer(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) : audio::river::io::NodeMuxer::NodeMuxer(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) :
Node(_name, _config) { Node(_name, _config) {
drain::IOFormatInterface interfaceFormat = getInterfaceFormat(); audio::drain::IOFormatInterface interfaceFormat = getInterfaceFormat();
drain::IOFormatInterface hardwareFormat = getHarwareFormat(); audio::drain::IOFormatInterface hardwareFormat = getHarwareFormat();
m_sampleTime = std11::chrono::nanoseconds(1000000000/int64_t(hardwareFormat.getFrequency())); m_sampleTime = audio::Duration(1000000000/int64_t(hardwareFormat.getFrequency()));
/** /**
# connect in input mode # connect in input mode
map-on-input-1:{ map-on-input-1:{
@@ -128,7 +131,7 @@ river::io::NodeMuxer::NodeMuxer(const std::string& _name, const std11::shared_pt
} }
// set callback mode ... // set callback mode ...
m_interfaceInput1->setInputCallback(std11::bind(&river::io::NodeMuxer::onDataReceivedInput1, m_interfaceInput1->setInputCallback(std11::bind(&audio::river::io::NodeMuxer::onDataReceivedInput1,
this, this,
std11::placeholders::_1, std11::placeholders::_1,
std11::placeholders::_2, std11::placeholders::_2,
@@ -137,7 +140,7 @@ river::io::NodeMuxer::NodeMuxer(const std::string& _name, const std11::shared_pt
std11::placeholders::_5, std11::placeholders::_5,
std11::placeholders::_6)); std11::placeholders::_6));
// set callback mode ... // set callback mode ...
m_interfaceInput2->setInputCallback(std11::bind(&river::io::NodeMuxer::onDataReceivedInput2, m_interfaceInput2->setInputCallback(std11::bind(&audio::river::io::NodeMuxer::onDataReceivedInput2,
this, this,
std11::placeholders::_1, std11::placeholders::_1,
std11::placeholders::_2, std11::placeholders::_2,
@@ -156,14 +159,14 @@ river::io::NodeMuxer::NodeMuxer(const std::string& _name, const std11::shared_pt
m_process.updateInterAlgo(); m_process.updateInterAlgo();
} }
river::io::NodeMuxer::~NodeMuxer() { audio::river::io::NodeMuxer::~NodeMuxer() {
RIVER_INFO("close input stream"); RIVER_INFO("close input stream");
stop(); stop();
m_interfaceInput1.reset(); m_interfaceInput1.reset();
m_interfaceInput2.reset(); m_interfaceInput2.reset();
}; };
void river::io::NodeMuxer::start() { void audio::river::io::NodeMuxer::start() {
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
RIVER_INFO("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") ); RIVER_INFO("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
if (m_interfaceInput1 != nullptr) { if (m_interfaceInput1 != nullptr) {
@@ -176,7 +179,7 @@ void river::io::NodeMuxer::start() {
} }
} }
void river::io::NodeMuxer::stop() { void audio::river::io::NodeMuxer::stop() {
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
if (m_interfaceInput1 != nullptr) { if (m_interfaceInput1 != nullptr) {
m_interfaceInput1->stop(); m_interfaceInput1->stop();
@@ -187,18 +190,17 @@ void river::io::NodeMuxer::stop() {
} }
void river::io::NodeMuxer::onDataReceivedInput1(const void* _data, void audio::river::io::NodeMuxer::onDataReceivedInput1(const void* _data,
const std11::chrono::system_clock::time_point& _time, const audio::Time& _time,
size_t _nbChunk, size_t _nbChunk,
enum audio::format _format, enum audio::format _format,
uint32_t _frequency, uint32_t _frequency,
const std::vector<audio::channel>& _map) { const std::vector<audio::channel>& _map) {
RIVER_DEBUG("Microphone Time=" << _time << " _nbChunk=" << _nbChunk << " _map=" << _map << " _format=" << _format << " freq=" << _frequency); RIVER_DEBUG("Microphone Time=" << _time << " _nbChunk=" << _nbChunk << " _map=" << _map << " _format=" << _format << " freq=" << _frequency);
RIVER_DEBUG(" next=" << _time + std11::chrono::nanoseconds(_nbChunk*1000000000LL/int64_t(_frequency)) ); RIVER_DEBUG(" next=" << _time + audio::Duration(0, _nbChunk*1000000000LL/int64_t(_frequency)) );
if (_format != audio::format_int16) { if (_format != audio::format_int16) {
RIVER_ERROR("call wrong type ... (need int16_t)"); RIVER_ERROR("call wrong type ... (need int16_t)");
} }
RIVER_SAVE_FILE_MACRO(int16_t, "REC_input1.raw", _data, _nbChunk*_map.size());
// push data synchronize // push data synchronize
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
m_bufferInput1.write(_data, _nbChunk, _time); m_bufferInput1.write(_data, _nbChunk, _time);
@@ -206,18 +208,17 @@ void river::io::NodeMuxer::onDataReceivedInput1(const void* _data,
process(); process();
} }
void river::io::NodeMuxer::onDataReceivedInput2(const void* _data, void audio::river::io::NodeMuxer::onDataReceivedInput2(const void* _data,
const std11::chrono::system_clock::time_point& _time, const audio::Time& _time,
size_t _nbChunk, size_t _nbChunk,
enum audio::format _format, enum audio::format _format,
uint32_t _frequency, uint32_t _frequency,
const std::vector<audio::channel>& _map) { const std::vector<audio::channel>& _map) {
RIVER_DEBUG("FeedBack Time=" << _time << " _nbChunk=" << _nbChunk << " _map=" << _map << " _format=" << _format << " freq=" << _frequency); RIVER_DEBUG("FeedBack Time=" << _time << " _nbChunk=" << _nbChunk << " _map=" << _map << " _format=" << _format << " freq=" << _frequency);
RIVER_DEBUG(" next=" << _time + std11::chrono::nanoseconds(_nbChunk*1000000000LL/int64_t(_frequency)) ); RIVER_DEBUG(" next=" << _time + audio::Duration(0, _nbChunk*1000000000LL/int64_t(_frequency)) );
if (_format != audio::format_int16) { if (_format != audio::format_int16) {
RIVER_ERROR("call wrong type ... (need int16_t)"); RIVER_ERROR("call wrong type ... (need int16_t)");
} }
RIVER_SAVE_FILE_MACRO(int16_t, "REC_input2.raw", _data, _nbChunk*_map.size());
// push data synchronize // push data synchronize
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
m_bufferInput2.write(_data, _nbChunk, _time); m_bufferInput2.write(_data, _nbChunk, _time);
@@ -225,16 +226,16 @@ void river::io::NodeMuxer::onDataReceivedInput2(const void* _data,
process(); process();
} }
void river::io::NodeMuxer::process() { void audio::river::io::NodeMuxer::process() {
if (m_bufferInput1.getSize() <= 256) { if (m_bufferInput1.getSize() <= 256) {
return; return;
} }
if (m_bufferInput2.getSize() <= 256) { if (m_bufferInput2.getSize() <= 256) {
return; return;
} }
std11::chrono::system_clock::time_point in1Time = m_bufferInput1.getReadTimeStamp(); audio::Time in1Time = m_bufferInput1.getReadTimeStamp();
std11::chrono::system_clock::time_point in2Time = m_bufferInput2.getReadTimeStamp(); audio::Time in2Time = m_bufferInput2.getReadTimeStamp();
std11::chrono::nanoseconds delta; audio::Duration delta;
if (in1Time < in2Time) { if (in1Time < in2Time) {
delta = in2Time - in1Time; delta = in2Time - in1Time;
} else { } else {
@@ -295,12 +296,12 @@ void river::io::NodeMuxer::process() {
} }
void river::io::NodeMuxer::reorder(void* _output, uint32_t _nbChunk, void* _input, const std::vector<audio::channel>& _mapInput) { void audio::river::io::NodeMuxer::reorder(void* _output, uint32_t _nbChunk, void* _input, const std::vector<audio::channel>& _mapInput) {
// real process: (only depend of data size): // real process: (only depend of data size):
switch (getInterfaceFormat().getFormat()) { switch (getInterfaceFormat().getFormat()) {
case audio::format_int8: case audio::format_int8:
{ {
DRAIN_VERBOSE("convert " << _mapInput << " ==> " << getInterfaceFormat().getMap()); RIVER_VERBOSE("convert " << _mapInput << " ==> " << getInterfaceFormat().getMap());
int8_t* in = static_cast<int8_t*>(_input); int8_t* in = static_cast<int8_t*>(_input);
int8_t* out = static_cast<int8_t*>(_output); int8_t* out = static_cast<int8_t*>(_output);
for (size_t kkk=0; kkk<getInterfaceFormat().getMap().size(); ++kkk) { for (size_t kkk=0; kkk<getInterfaceFormat().getMap().size(); ++kkk) {
@@ -316,7 +317,7 @@ void river::io::NodeMuxer::reorder(void* _output, uint32_t _nbChunk, void* _inpu
} }
} }
} }
DRAIN_VERBOSE(" " << convertId << " ==> " << kkk); RIVER_VERBOSE(" " << convertId << " ==> " << kkk);
if (convertId != -1) { if (convertId != -1) {
for (size_t iii=0; iii<_nbChunk; ++iii) { for (size_t iii=0; iii<_nbChunk; ++iii) {
out[iii*getInterfaceFormat().getMap().size()+kkk] = in[iii*_mapInput.size()+convertId]; out[iii*getInterfaceFormat().getMap().size()+kkk] = in[iii*_mapInput.size()+convertId];
@@ -328,7 +329,7 @@ void river::io::NodeMuxer::reorder(void* _output, uint32_t _nbChunk, void* _inpu
default: default:
case audio::format_int16: case audio::format_int16:
if (getInterfaceFormat().getMap().size() == 1) { if (getInterfaceFormat().getMap().size() == 1) {
DRAIN_VERBOSE("convert " << _mapInput << " ==> " << getInterfaceFormat().getMap()); RIVER_VERBOSE("convert " << _mapInput << " ==> " << getInterfaceFormat().getMap());
int16_t* in = static_cast<int16_t*>(_input); int16_t* in = static_cast<int16_t*>(_input);
int16_t* out = static_cast<int16_t*>(_output); int16_t* out = static_cast<int16_t*>(_output);
for (size_t iii=0; iii<_nbChunk; ++iii) { for (size_t iii=0; iii<_nbChunk; ++iii) {
@@ -339,7 +340,7 @@ void river::io::NodeMuxer::reorder(void* _output, uint32_t _nbChunk, void* _inpu
out[iii] = val/_mapInput.size(); out[iii] = val/_mapInput.size();
} }
} else { } else {
DRAIN_VERBOSE("convert " << _mapInput << " ==> " << getInterfaceFormat().getMap()); RIVER_VERBOSE("convert " << _mapInput << " ==> " << getInterfaceFormat().getMap());
int16_t* in = static_cast<int16_t*>(_input); int16_t* in = static_cast<int16_t*>(_input);
int16_t* out = static_cast<int16_t*>(_output); int16_t* out = static_cast<int16_t*>(_output);
for (size_t kkk=0; kkk<getInterfaceFormat().getMap().size(); ++kkk) { for (size_t kkk=0; kkk<getInterfaceFormat().getMap().size(); ++kkk) {
@@ -355,7 +356,7 @@ void river::io::NodeMuxer::reorder(void* _output, uint32_t _nbChunk, void* _inpu
} }
} }
} }
DRAIN_VERBOSE(" " << convertId << " ==> " << kkk); RIVER_VERBOSE(" " << convertId << " ==> " << kkk);
if (convertId != -1) { if (convertId != -1) {
for (size_t iii=0; iii<_nbChunk; ++iii) { for (size_t iii=0; iii<_nbChunk; ++iii) {
out[iii*getInterfaceFormat().getMap().size()+kkk] = in[iii*_mapInput.size()+convertId]; out[iii*getInterfaceFormat().getMap().size()+kkk] = in[iii*_mapInput.size()+convertId];
@@ -369,7 +370,7 @@ void river::io::NodeMuxer::reorder(void* _output, uint32_t _nbChunk, void* _inpu
case audio::format_int32: case audio::format_int32:
case audio::format_float: case audio::format_float:
{ {
DRAIN_VERBOSE("convert (2) " << _mapInput << " ==> " << getInterfaceFormat().getMap()); RIVER_VERBOSE("convert (2) " << _mapInput << " ==> " << getInterfaceFormat().getMap());
uint32_t* in = static_cast<uint32_t*>(_input); uint32_t* in = static_cast<uint32_t*>(_input);
uint32_t* out = static_cast<uint32_t*>(_output); uint32_t* out = static_cast<uint32_t*>(_output);
for (size_t kkk=0; kkk<getInterfaceFormat().getMap().size(); ++kkk) { for (size_t kkk=0; kkk<getInterfaceFormat().getMap().size(); ++kkk) {
@@ -395,7 +396,7 @@ void river::io::NodeMuxer::reorder(void* _output, uint32_t _nbChunk, void* _inpu
break; break;
case audio::format_double: case audio::format_double:
{ {
DRAIN_VERBOSE("convert (2) " << _mapInput << " ==> " << getInterfaceFormat().getMap()); RIVER_VERBOSE("convert (2) " << _mapInput << " ==> " << getInterfaceFormat().getMap());
uint64_t* in = static_cast<uint64_t*>(_input); uint64_t* in = static_cast<uint64_t*>(_input);
uint64_t* out = static_cast<uint64_t*>(_output); uint64_t* out = static_cast<uint64_t*>(_output);
for (size_t kkk=0; kkk<getInterfaceFormat().getMap().size(); ++kkk) { for (size_t kkk=0; kkk<getInterfaceFormat().getMap().size(); ++kkk) {
@@ -422,7 +423,7 @@ void river::io::NodeMuxer::reorder(void* _output, uint32_t _nbChunk, void* _inpu
} }
} }
void river::io::NodeMuxer::processMuxer(void* _dataIn1, void* _dataIn2, uint32_t _nbChunk, const std11::chrono::system_clock::time_point& _time) { void audio::river::io::NodeMuxer::processMuxer(void* _dataIn1, void* _dataIn2, uint32_t _nbChunk, const audio::Time& _time) {
//RIVER_INFO("must Mux data : " << m_mapInput1 << " + " << m_mapInput2 << " ==> " << getInterfaceFormat().getMap()); //RIVER_INFO("must Mux data : " << m_mapInput1 << " + " << m_mapInput2 << " ==> " << getInterfaceFormat().getMap());
memset(&m_data[0], 0, m_data.size()); memset(&m_data[0], 0, m_data.size());
reorder(&m_data[0], _nbChunk, _dataIn1, m_mapInput1); reorder(&m_data[0], _nbChunk, _dataIn1, m_mapInput1);
@@ -431,7 +432,7 @@ void river::io::NodeMuxer::processMuxer(void* _dataIn1, void* _dataIn2, uint32_t
} }
void river::io::NodeMuxer::generateDot(etk::FSNode& _node) { void audio::river::io::NodeMuxer::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=\"[" << m_uid << "] IO::Node : " << m_name << "\";\n"; _node << " label=\"[" << m_uid << "] IO::Node : " << m_name << "\";\n";
@@ -459,7 +460,7 @@ void river::io::NodeMuxer::generateDot(etk::FSNode& _node) {
if (m_listAvaillable[iii].expired() == true) { if (m_listAvaillable[iii].expired() == true) {
continue; continue;
} }
std11::shared_ptr<river::Interface> element = m_listAvaillable[iii].lock(); std11::shared_ptr<audio::river::Interface> element = m_listAvaillable[iii].lock();
if (element == nullptr) { if (element == nullptr) {
continue; continue;
} }

View File

@@ -0,0 +1,70 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_RIVER_IO_NODE_MUXER_H__
#define __AUDIO_RIVER_IO_NODE_MUXER_H__
#include <audio/river/io/Node.h>
#include <audio/river/Interface.h>
#include <audio/drain/CircularBuffer.h>
namespace audio {
namespace river {
namespace io {
class Manager;
class NodeMuxer : public Node {
protected:
/**
* @brief Constructor
*/
NodeMuxer(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
public:
static std11::shared_ptr<NodeMuxer> create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
/**
* @brief Destructor
*/
virtual ~NodeMuxer();
protected:
virtual void start();
virtual void stop();
std11::shared_ptr<audio::river::Interface> m_interfaceInput1;
std11::shared_ptr<audio::river::Interface> m_interfaceInput2;
std11::shared_ptr<audio::river::Interface> createInput(float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std::string& _streamName,
const std::string& _name);
void onDataReceivedInput1(const void* _data,
const audio::Time& _time,
size_t _nbChunk,
enum audio::format _format,
uint32_t _frequency,
const std::vector<audio::channel>& _map);
void onDataReceivedInput2(const void* _data,
const audio::Time& _time,
size_t _nbChunk,
enum audio::format _format,
uint32_t _frequency,
const std::vector<audio::channel>& _map);
std::vector<audio::channel> m_mapInput1;
std::vector<audio::channel> m_mapInput2;
audio::drain::CircularBuffer m_bufferInput1;
audio::drain::CircularBuffer m_bufferInput2;
audio::Duration m_sampleTime; //!< represent the sample time at the specify frequency.
void process();
void processMuxer(void* _dataMic, void* _dataFB, uint32_t _nbChunk, const audio::Time& _time);
std::vector<uint8_t> m_data;
public:
virtual void generateDot(etk::FSNode& _node);
private:
void reorder(void* _output, uint32_t _nbChunk, void* _input, const std::vector<audio::channel>& _mapInput);
};
}
}
}
#endif

View File

@@ -4,48 +4,19 @@
* @license APACHE v2.0 (see license file) * @license APACHE v2.0 (see license file)
*/ */
#ifdef __AIRTAUDIO_INFERFACE__ #ifdef AUDIO_RIVER_BUILD_ORCHESTRA
#include <river/io/NodeAirTAudio.h> #include <audio/river/io/NodeOrchestra.h>
#include <river/debug.h> #include <audio/river/debug.h>
#include <etk/memory.h> #include <etk/memory.h>
#undef __class__ #undef __class__
#define __class__ "io::NodeAirTAudio" #define __class__ "io::NodeOrchestra"
static std::string asString(const std11::chrono::system_clock::time_point& tp) { int32_t audio::river::io::NodeOrchestra::recordCallback(const void* _inputBuffer,
// convert to system time: const audio::Time& _timeInput,
std::time_t t = std11::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;
}
int32_t river::io::NodeAirTAudio::duplexCallback(const void* _inputBuffer,
const std11::chrono::system_clock::time_point& _timeInput,
void* _outputBuffer,
const std11::chrono::system_clock::time_point& _timeOutput,
uint32_t _nbChunk, uint32_t _nbChunk,
const std::vector<airtaudio::status>& _status) { const std::vector<audio::orchestra::status>& _status) {
std11::unique_lock<std11::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;
}
int32_t river::io::NodeAirTAudio::recordCallback(const void* _inputBuffer,
const std11::chrono::system_clock::time_point& _timeInput,
uint32_t _nbChunk,
const std::vector<airtaudio::status>& _status) {
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
// TODO : Manage status ... // TODO : Manage status ...
RIVER_VERBOSE("data Input size request :" << _nbChunk << " [BEGIN] status=" << _status << " nbIO=" << m_list.size()); RIVER_VERBOSE("data Input size request :" << _nbChunk << " [BEGIN] status=" << _status << " nbIO=" << m_list.size());
@@ -53,10 +24,10 @@ int32_t river::io::NodeAirTAudio::recordCallback(const void* _inputBuffer,
return 0; return 0;
} }
int32_t river::io::NodeAirTAudio::playbackCallback(void* _outputBuffer, int32_t audio::river::io::NodeOrchestra::playbackCallback(void* _outputBuffer,
const std11::chrono::system_clock::time_point& _timeOutput, const audio::Time& _timeOutput,
uint32_t _nbChunk, uint32_t _nbChunk,
const std::vector<airtaudio::status>& _status) { const std::vector<audio::orchestra::status>& _status) {
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
// TODO : Manage status ... // TODO : Manage status ...
RIVER_VERBOSE("data Output size request :" << _nbChunk << " [BEGIN] status=" << _status << " nbIO=" << m_list.size()); RIVER_VERBOSE("data Output size request :" << _nbChunk << " [BEGIN] status=" << _status << " nbIO=" << m_list.size());
@@ -66,14 +37,14 @@ int32_t river::io::NodeAirTAudio::playbackCallback(void* _outputBuffer,
std11::shared_ptr<river::io::NodeAirTAudio> river::io::NodeAirTAudio::create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) { std11::shared_ptr<audio::river::io::NodeOrchestra> audio::river::io::NodeOrchestra::create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) {
return std11::shared_ptr<river::io::NodeAirTAudio>(new river::io::NodeAirTAudio(_name, _config)); return std11::shared_ptr<audio::river::io::NodeOrchestra>(new audio::river::io::NodeOrchestra(_name, _config));
} }
river::io::NodeAirTAudio::NodeAirTAudio(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) : audio::river::io::NodeOrchestra::NodeOrchestra(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) :
Node(_name, _config) { Node(_name, _config) {
drain::IOFormatInterface interfaceFormat = getInterfaceFormat(); audio::drain::IOFormatInterface interfaceFormat = getInterfaceFormat();
drain::IOFormatInterface hardwareFormat = getHarwareFormat(); audio::drain::IOFormatInterface hardwareFormat = getHarwareFormat();
/** /**
map-on:{ # select hardware interface and name map-on:{ # select hardware interface and name
interface:"alsa", # interface : "alsa", "pulse", "core", ... interface:"alsa", # interface : "alsa", "pulse", "core", ...
@@ -81,21 +52,23 @@ river::io::NodeAirTAudio::NodeAirTAudio(const std::string& _name, const std11::s
}, },
nb-chunk:1024 # number of chunk to open device (create the latency anf the frequency to call user) 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 typeInterface = audio::orchestra::type_undefined;
std::string streamName = "default"; std::string streamName = "default";
const std11::shared_ptr<const ejson::Object> tmpObject = m_config->getObject("map-on"); const std11::shared_ptr<const ejson::Object> tmpObject = m_config->getObject("map-on");
if (tmpObject == nullptr) { if (tmpObject == nullptr) {
RIVER_WARNING("missing node : 'map-on' ==> auto map : 'auto:default'"); RIVER_WARNING("missing node : 'map-on' ==> auto map : 'auto:default'");
} else { } else {
std::string value = tmpObject->getStringValue("interface", "default"); typeInterface = tmpObject->getStringValue("interface", audio::orchestra::type_undefined);
typeInterface = airtaudio::getTypeFromString(value); if (typeInterface == "auto") {
typeInterface = audio::orchestra::type_undefined;
}
streamName = tmpObject->getStringValue("name", "default"); streamName = tmpObject->getStringValue("name", "default");
} }
int32_t nbChunk = m_config->getNumberValue("nb-chunk", 1024); int32_t nbChunk = m_config->getNumberValue("nb-chunk", 1024);
// intanciate specific API ... // intanciate specific API ...
m_adac.instanciate(typeInterface); m_interface.instanciate(typeInterface);
m_adac.setName(_name); m_interface.setName(_name);
// TODO : Check return ... // TODO : Check return ...
std::string type = m_config->getStringValue("type", "int16"); std::string type = m_config->getStringValue("type", "int16");
if (streamName == "") { if (streamName == "") {
@@ -110,23 +83,25 @@ river::io::NodeAirTAudio::NodeAirTAudio(const std::string& _name, const std11::s
RIVER_INFO(" m_format=" << hardwareFormat.getFormat()); RIVER_INFO(" m_format=" << hardwareFormat.getFormat());
RIVER_INFO(" m_isInput=" << m_isInput); RIVER_INFO(" m_isInput=" << m_isInput);
int32_t deviceId = -1; int32_t deviceId = -1;
/*
// TODO : Remove this from here (create an extern interface ...) // TODO : Remove this from here (create an extern interface ...)
RIVER_INFO("Device list:"); RIVER_INFO("Device list:");
for (int32_t iii=0; iii<m_adac.getDeviceCount(); ++iii) { for (int32_t iii=0; iii<m_interface.getDeviceCount(); ++iii) {
m_info = m_adac.getDeviceInfo(iii); m_info = m_interface.getDeviceInfo(iii);
RIVER_INFO(" " << iii << " name :" << m_info.name); RIVER_INFO(" " << iii << " name :" << m_info.name);
m_info.display(2); m_info.display(2);
} }
*/
// special case for default IO: // special case for default IO:
if (streamName == "default") { if (streamName == "default") {
if (m_isInput == true) { if (m_isInput == true) {
deviceId = m_adac.getDefaultInputDevice(); deviceId = m_interface.getDefaultInputDevice();
} else { } else {
deviceId = m_adac.getDefaultOutputDevice(); deviceId = m_interface.getDefaultOutputDevice();
} }
} else { } else {
for (int32_t iii=0; iii<m_adac.getDeviceCount(); ++iii) { for (int32_t iii=0; iii<m_interface.getDeviceCount(); ++iii) {
m_info = m_adac.getDeviceInfo(iii); m_info = m_interface.getDeviceInfo(iii);
if (m_info.name == streamName) { if (m_info.name == streamName) {
RIVER_INFO(" Select ... id =" << iii); RIVER_INFO(" Select ... id =" << iii);
deviceId = iii; deviceId = iii;
@@ -143,9 +118,9 @@ river::io::NodeAirTAudio::NodeAirTAudio(const std::string& _name, const std11::s
// Open specific ID : // Open specific ID :
if (deviceId == -1) { if (deviceId == -1) {
m_info = m_adac.getDeviceInfo(streamName); m_info = m_interface.getDeviceInfo(streamName);
} else { } else {
m_info = m_adac.getDeviceInfo(deviceId); m_info = m_interface.getDeviceInfo(deviceId);
} }
// display property : // display property :
{ {
@@ -206,27 +181,28 @@ river::io::NodeAirTAudio::NodeAirTAudio(const std::string& _name, const std11::s
} }
// open Audio device: // open Audio device:
airtaudio::StreamParameters params; audio::orchestra::StreamParameters params;
params.deviceId = deviceId; params.deviceId = deviceId;
params.deviceName = streamName; params.deviceName = streamName;
// TODO : Remove limit of 2 channels ... params.nChannels = hardwareFormat.getMap().size();
if (m_isInput == true) { if (m_info.channels.size() < params.nChannels) {
m_info.inputChannels = 2; RIVER_CRITICAL("Can not open hardware device with more channel (" << params.nChannels << ") that is autorized by hardware (" << m_info.channels.size() << ").");
params.nChannels = 2;
} else {
m_info.outputChannels = 2;
params.nChannels = 2;
} }
airtaudio::StreamOptions option; audio::orchestra::StreamOptions option;
etk::from_string(option.mode, tmpObject->getStringValue("timestamp-mode", "soft")); etk::from_string(option.mode, tmpObject->getStringValue("timestamp-mode", "soft"));
RIVER_DEBUG("interfaceFormat=" << interfaceFormat);
RIVER_DEBUG("hardwareFormat=" << hardwareFormat);
m_rtaudioFrameSize = nbChunk; m_rtaudioFrameSize = nbChunk;
RIVER_INFO("Open output stream nbChannels=" << params.nChannels); RIVER_INFO("Open output stream nbChannels=" << params.nChannels);
enum airtaudio::error err = airtaudio::error_none; enum audio::orchestra::error err = audio::orchestra::error_none;
if (m_isInput == true) { if (m_isInput == true) {
err = m_adac.openStream(nullptr, &params, m_process.setInputConfig(hardwareFormat);
m_process.setOutputConfig(interfaceFormat);
err = m_interface.openStream(nullptr, &params,
hardwareFormat.getFormat(), hardwareFormat.getFrequency(), &m_rtaudioFrameSize, hardwareFormat.getFormat(), hardwareFormat.getFrequency(), &m_rtaudioFrameSize,
std11::bind(&river::io::NodeAirTAudio::recordCallback, std11::bind(&audio::river::io::NodeOrchestra::recordCallback,
this, this,
std11::placeholders::_1, std11::placeholders::_1,
std11::placeholders::_2, std11::placeholders::_2,
@@ -235,9 +211,11 @@ river::io::NodeAirTAudio::NodeAirTAudio(const std::string& _name, const std11::s
option option
); );
} else { } else {
err = m_adac.openStream(&params, nullptr, m_process.setInputConfig(interfaceFormat);
m_process.setOutputConfig(hardwareFormat);
err = m_interface.openStream(&params, nullptr,
hardwareFormat.getFormat(), hardwareFormat.getFrequency(), &m_rtaudioFrameSize, hardwareFormat.getFormat(), hardwareFormat.getFrequency(), &m_rtaudioFrameSize,
std11::bind(&river::io::NodeAirTAudio::playbackCallback, std11::bind(&audio::river::io::NodeOrchestra::playbackCallback,
this, this,
std11::placeholders::_3, std11::placeholders::_3,
std11::placeholders::_4, std11::placeholders::_4,
@@ -246,34 +224,34 @@ river::io::NodeAirTAudio::NodeAirTAudio(const std::string& _name, const std11::s
option option
); );
} }
if (err != airtaudio::error_none) { if (err != audio::orchestra::error_none) {
RIVER_ERROR("Create stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not create stream " << err); RIVER_ERROR("Create stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not create stream " << err);
} }
m_process.updateInterAlgo(); m_process.updateInterAlgo();
} }
river::io::NodeAirTAudio::~NodeAirTAudio() { audio::river::io::NodeOrchestra::~NodeOrchestra() {
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
RIVER_INFO("close input stream"); RIVER_INFO("close input stream");
if (m_adac.isStreamOpen() ) { if (m_interface.isStreamOpen() ) {
m_adac.closeStream(); m_interface.closeStream();
} }
}; };
void river::io::NodeAirTAudio::start() { void audio::river::io::NodeOrchestra::start() {
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
RIVER_INFO("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") ); RIVER_INFO("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
enum airtaudio::error err = m_adac.startStream(); enum audio::orchestra::error err = m_interface.startStream();
if (err != airtaudio::error_none) { if (err != audio::orchestra::error_none) {
RIVER_ERROR("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not start stream ... " << err); RIVER_ERROR("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not start stream ... " << err);
} }
} }
void river::io::NodeAirTAudio::stop() { void audio::river::io::NodeOrchestra::stop() {
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
RIVER_INFO("Stop stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") ); RIVER_INFO("Stop stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
enum airtaudio::error err = m_adac.stopStream(); enum audio::orchestra::error err = m_interface.stopStream();
if (err != airtaudio::error_none) { if (err != audio::orchestra::error_none) {
RIVER_ERROR("Stop stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not stop stream ... " << err); RIVER_ERROR("Stop stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not stop stream ... " << err);
} }
} }

View File

@@ -0,0 +1,78 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_RIVER_IO_NODE_AIRTAUDIO_H__
#define __AUDIO_RIVER_IO_NODE_AIRTAUDIO_H__
#ifdef AUDIO_RIVER_BUILD_ORCHESTRA
#include <audio/river/io/Node.h>
#include <audio/orchestra/Interface.h>
namespace audio {
namespace river {
namespace io {
class Manager;
class Group;
/**
* @brief Low level node that is manage on the interface with the extern lib airtaudio
*/
class NodeOrchestra : public audio::river::io::Node {
friend class audio::river::io::Group;
protected:
/**
* @brief Constructor
*/
NodeOrchestra(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
public:
static std11::shared_ptr<NodeOrchestra> create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
/**
* @brief Destructor
*/
virtual ~NodeOrchestra();
virtual bool isHarwareNode() {
return true;
};
protected:
audio::orchestra::Interface m_interface; //!< Real airtaudio interface
audio::orchestra::DeviceInfo m_info; //!< information on the stream.
unsigned int m_rtaudioFrameSize; // DEPRECATED soon...
public:
/**
* @brief Input Callback . Have recaive new data to process.
* @param[in] _inputBuffer Pointer on the data buffer.
* @param[in] _timeInput Time on the fist sample has been recorded.
* @param[in] _nbChunk Number of chunk in the buffer
* @param[in] _status DEPRECATED soon
* @return DEPRECATED soon
*/
int32_t recordCallback(const void* _inputBuffer,
const audio::Time& _timeInput,
uint32_t _nbChunk,
const std::vector<audio::orchestra::status>& _status);
/**
* @brief Playback callback. Request new data on output
* @param[in,out] _outputBuffer Pointer on the buffer to fill data.
* @param[in] _timeOutput Time on wich the data might be played.
* @param[in] _nbChunk Number of chunk in the buffer
* @param[in] _status DEPRECATED soon
* @return DEPRECATED soon
*/
int32_t playbackCallback(void* _outputBuffer,
const audio::Time& _timeOutput,
uint32_t _nbChunk,
const std::vector<audio::orchestra::status>& _status);
protected:
virtual void start();
virtual void stop();
};
}
}
}
#endif
#endif

View File

@@ -4,38 +4,30 @@
* @license APACHE v2.0 (see license file) * @license APACHE v2.0 (see license file)
*/ */
#ifdef __PORTAUDIO_INFERFACE__ #ifdef AUDIO_RIVER_BUILD_PORTAUDIO
#include <river/io/NodePortAudio.h> #include <audio/river/io/NodePortAudio.h>
#include <river/debug.h> #include <audio/river/debug.h>
#include <etk/memory.h> #include <etk/memory.h>
#include <audio/Time.h>
#include <audio/Duration.h>
#undef __class__ #undef __class__
#define __class__ "io::NodePortAudio" #define __class__ "io::NodePortAudio"
static std::string asString(const std11::chrono::system_clock::time_point& tp) {
// convert to system time:
std::time_t t = std11::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;
}
static int portAudioStreamCallback(const void *_input, static int portAudioStreamCallback(const void *_input,
void *_output, void *_output,
unsigned long _frameCount, unsigned long _frameCount,
const PaStreamCallbackTimeInfo* _timeInfo, const PaStreamCallbackTimeInfo* _timeInfo,
PaStreamCallbackFlags _statusFlags, PaStreamCallbackFlags _statusFlags,
void *_userData) { void *_userData) {
river::io::NodePortAudio* myClass = reinterpret_cast<river::io::NodePortAudio*>(_userData); audio::river::io::NodePortAudio* myClass = reinterpret_cast<audio::river::io::NodePortAudio*>(_userData);
int64_t sec = int64_t(_timeInfo->inputBufferAdcTime); int64_t sec = int64_t(_timeInfo->inputBufferAdcTime);
int64_t nsec = (_timeInfo->inputBufferAdcTime-double(sec))*1000000000LL; int64_t nsec = (_timeInfo->inputBufferAdcTime-double(sec))*1000000000LL;
std11::chrono::system_clock::time_point timeInput = std11::chrono::system_clock::from_time_t(sec) + std11::chrono::nanoseconds(nsec); audio::Time timeInput(sec, nsec);
sec = int64_t(_timeInfo->outputBufferDacTime); sec = int64_t(_timeInfo->outputBufferDacTime);
nsec = (_timeInfo->outputBufferDacTime-double(sec))*1000000000LL; nsec = (_timeInfo->outputBufferDacTime-double(sec))*1000000000LL;
std11::chrono::system_clock::time_point timeOutput = std11::chrono::system_clock::from_time_t(sec) + std11::chrono::nanoseconds(nsec); audio::Time timeOutput(sec, nsec);
return myClass->duplexCallback(_input, return myClass->duplexCallback(_input,
timeInput, timeInput,
_output, _output,
@@ -44,10 +36,10 @@ static int portAudioStreamCallback(const void *_input,
_statusFlags); _statusFlags);
} }
int32_t river::io::NodePortAudio::duplexCallback(const void* _inputBuffer, int32_t audio::river::io::NodePortAudio::duplexCallback(const void* _inputBuffer,
const std11::chrono::system_clock::time_point& _timeInput, const audio::Time& _timeInput,
void* _outputBuffer, void* _outputBuffer,
const std11::chrono::system_clock::time_point& _timeOutput, const audio::Time& _timeOutput,
uint32_t _nbChunk, uint32_t _nbChunk,
PaStreamCallbackFlags _status) { PaStreamCallbackFlags _status) {
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
@@ -64,14 +56,14 @@ int32_t river::io::NodePortAudio::duplexCallback(const void* _inputBuffer,
} }
std11::shared_ptr<river::io::NodePortAudio> river::io::NodePortAudio::create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) { std11::shared_ptr<audio::river::io::NodePortAudio> audio::river::io::NodePortAudio::create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) {
return std11::shared_ptr<river::io::NodePortAudio>(new river::io::NodePortAudio(_name, _config)); return std11::shared_ptr<audio::river::io::NodePortAudio>(new audio::river::io::NodePortAudio(_name, _config));
} }
river::io::NodePortAudio::NodePortAudio(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) : audio::river::io::NodePortAudio::NodePortAudio(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) :
Node(_name, _config) { Node(_name, _config) {
drain::IOFormatInterface interfaceFormat = getInterfaceFormat(); audio::drain::IOFormatInterface interfaceFormat = getInterfaceFormat();
drain::IOFormatInterface hardwareFormat = getHarwareFormat(); audio::drain::IOFormatInterface hardwareFormat = getHarwareFormat();
/** /**
map-on:{ # select hardware interface and name map-on:{ # select hardware interface and name
interface:"alsa", # interface : "alsa", "pulse", "core", ... interface:"alsa", # interface : "alsa", "pulse", "core", ...
@@ -79,14 +71,12 @@ river::io::NodePortAudio::NodePortAudio(const std::string& _name, const std11::s
}, },
nb-chunk:1024 # number of chunk to open device (create the latency anf the frequency to call user) 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"; std::string streamName = "default";
const std11::shared_ptr<const ejson::Object> tmpObject = m_config->getObject("map-on"); const std11::shared_ptr<const ejson::Object> tmpObject = m_config->getObject("map-on");
if (tmpObject == nullptr) { if (tmpObject == nullptr) {
RIVER_WARNING("missing node : 'map-on' ==> auto map : 'auto:default'"); RIVER_WARNING("missing node : 'map-on' ==> auto map : 'auto:default'");
} else { } else {
std::string value = tmpObject->getStringValue("interface", "default"); std::string value = tmpObject->getStringValue("interface", "default");
typeInterface = airtaudio::getTypeFromString(value);
streamName = tmpObject->getStringValue("name", "default"); streamName = tmpObject->getStringValue("name", "default");
} }
int32_t nbChunk = m_config->getNumberValue("nb-chunk", 1024); int32_t nbChunk = m_config->getNumberValue("nb-chunk", 1024);
@@ -117,7 +107,7 @@ river::io::NodePortAudio::NodePortAudio(const std::string& _name, const std11::s
m_process.updateInterAlgo(); m_process.updateInterAlgo();
} }
river::io::NodePortAudio::~NodePortAudio() { audio::river::io::NodePortAudio::~NodePortAudio() {
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
RIVER_INFO("close input stream"); RIVER_INFO("close input stream");
PaError err = Pa_CloseStream( m_stream ); PaError err = Pa_CloseStream( m_stream );
@@ -126,7 +116,7 @@ river::io::NodePortAudio::~NodePortAudio() {
} }
}; };
void river::io::NodePortAudio::start() { void audio::river::io::NodePortAudio::start() {
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
RIVER_INFO("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") ); RIVER_INFO("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
PaError err = Pa_StartStream(m_stream); PaError err = Pa_StartStream(m_stream);
@@ -135,7 +125,7 @@ void river::io::NodePortAudio::start() {
} }
} }
void river::io::NodePortAudio::stop() { void audio::river::io::NodePortAudio::stop() {
std11::unique_lock<std11::mutex> lock(m_mutex); std11::unique_lock<std11::mutex> lock(m_mutex);
RIVER_INFO("Stop stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") ); RIVER_INFO("Stop stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
PaError err = Pa_StopStream(m_stream); PaError err = Pa_StopStream(m_stream);

View File

@@ -0,0 +1,55 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_RIVER_IO_NODE_PORTAUDIO_H__
#define __AUDIO_RIVER_IO_NODE_PORTAUDIO_H__
#ifdef AUDIO_RIVER_BUILD_PORTAUDIO
#include <audio/river/Interface.h>
#include <audio/river/io/Node.h>
#include <portaudio.h>
namespace audio {
namespace river {
namespace io {
class Manager;
//! @not-in-doc
class NodePortAudio : public Node {
protected:
/**
* @brief Constructor
*/
NodePortAudio(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
public:
static std11::shared_ptr<NodePortAudio> create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
/**
* @brief Destructor
*/
virtual ~NodePortAudio();
virtual bool isHarwareNode() {
return true;
};
protected:
PaStream* m_stream;
public:
int32_t duplexCallback(const void* _inputBuffer,
const audio::Time& _timeInput,
void* _outputBuffer,
const audio::Time& _timeOutput,
uint32_t _nbChunk,
PaStreamCallbackFlags _status);
protected:
virtual void start();
virtual void stop();
};
}
}
}
#endif
#endif

59
audio/river/river.cpp Normal file
View File

@@ -0,0 +1,59 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <audio/river/river.h>
#include <audio/river/debug.h>
#include <audio/river/io/Manager.h>
static bool river_isInit = false;
static std::string river_configFile = "";
void audio::river::init(const std::string& _filename) {
if (river_isInit == false) {
river_isInit = true;
river_configFile = _filename;
RIVER_DEBUG("init RIVER :" << river_configFile);
std11::shared_ptr<audio::river::io::Manager> mng = audio::river::io::Manager::getInstance();
if (mng != nullptr) {
mng->init(river_configFile);
}
} else {
RIVER_ERROR("River is already init not use : " << _filename);
}
}
void audio::river::initString(const std::string& _config) {
if (river_isInit == false) {
river_isInit = true;
river_configFile = _config;
RIVER_DEBUG("init RIVER with config.");
std11::shared_ptr<audio::river::io::Manager> mng = audio::river::io::Manager::getInstance();
if (mng != nullptr) {
mng->initString(river_configFile);
}
} else {
RIVER_ERROR("River is already init not use Data ...");
}
}
void audio::river::unInit() {
if (river_isInit == true) {
river_isInit = false;
RIVER_DEBUG("un-init RIVER.");
std11::shared_ptr<audio::river::io::Manager> mng = audio::river::io::Manager::getInstance();
if (mng != nullptr) {
RIVER_ERROR("Can not get on the RIVER hardware manager !!!");
mng->unInit();
}
}
}
bool audio::river::isInit() {
return river_isInit;
}

40
audio/river/river.h Normal file
View File

@@ -0,0 +1,40 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_RIVER_H__
#define __AUDIO_RIVER_H__
#include <etk/types.h>
namespace audio {
namespace river {
/**
* @brief Initialize the River Library
* @param[in] _filename Name of the configuration file (if "" ==> default config file)
*/
void init(const std::string& _filename = "");
/**
* @brief Initialize the River Library with a json data string
* @param[in] _config json sting data
*/
void initString(const std::string& _config);
/**
* @brief Un-initialize the River Library
* @note this close all stream of all interfaces.
* @note really good for test.
*/
void unInit();
/**
* @brief Get the status of initialisation
* @return true River is init
* @return false River is NOT init
*/
bool isInit();
}
}
#endif

79
catkin/CMakeLists.txt Normal file
View File

@@ -0,0 +1,79 @@
cmake_minimum_required(VERSION 2.8.3)
project(audio_river)
set(CMAKE_VERBOSE_MAKEFILE ON)
## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
audio_drain
audio
audio_orchestra
ejson
)
###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if you package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
INCLUDE_DIRS ../
LIBRARIES ${PROJECT_NAME}
CATKIN_DEPENDS audio_orchestra audio audio_drain ejson
DEPENDS system_lib
)
###########
## Build ##
###########
## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
..
${catkin_INCLUDE_DIRS}
)
## Declare a cpp library
add_library(${PROJECT_NAME}
../audio/river/debug.cpp
../audio/river/river.cpp
../audio/river/Manager.cpp
../audio/river/Interface.cpp
../audio/river/io/Group.cpp
../audio/river/io/Node.cpp
../audio/river/io/NodeOrchestra.cpp
../audio/river/io/NodePortAudio.cpp
../audio/river/io/NodeAEC.cpp
../audio/river/io/NodeMuxer.cpp
../audio/river/io/Manager.cpp
)
add_definitions(-DAUDIO_RIVER_BUILD_ORCHESTRA)
## Specify libraries to link a library or executable target against
target_link_libraries(${PROJECT_NAME}
${catkin_LIBRARIES}
)
#############
## Install ##
#############
## Mark executables and/or libraries for installation
install(TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
)
## Mark cpp header files for installation
install(DIRECTORY ../audio/river/
DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
FILES_MATCHING PATTERN "*.h"
)

17
catkin/package.xml Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0"?>
<package>
<name>audio_river</name>
<version>0.3.0</version>
<description>Ewol River Intrerface : Is to multiple acces on different format on Audio IO</description>
<maintainer email="yui.heero@gmail.com">Edouard DUPIN</maintainer>
<license>Apache-2.0</license>
<build_depend>ejson</build_depend>
<build_depend>audio</build_depend>
<build_depend>audio_orchestra</build_depend>
<build_depend>audio_drain</build_depend>
<buildtool_depend>catkin</buildtool_depend>
<run_depend>ejson</run_depend>
<run_depend>audio</run_depend>
<run_depend>audio_orchestra</run_depend>
<run_depend>audio_drain</run_depend>
</package>

View File

@@ -4,8 +4,7 @@
map-on:{ map-on:{
interface:"alsa", interface:"alsa",
#name:"default", #name:"default",
#name:"hw:0,0", name:"hw:0,0",
name:"default",
timestamp-mode:"trigered", timestamp-mode:"trigered",
}, },
group:"baseIOSynchrone", group:"baseIOSynchrone",

View File

@@ -5,6 +5,7 @@
interface:"alsa", interface:"alsa",
name:"hw:0,0", name:"hw:0,0",
#name:"default", #name:"default",
#name:"AD1989A_outputs",
timestamp-mode:"trigered", timestamp-mode:"trigered",
}, },
#group:"baseIOSynchrone", #group:"baseIOSynchrone",
@@ -17,12 +18,46 @@
#volume-name:"MASTER", #volume-name:"MASTER",
mux-demux-type:"int16-on-int32", mux-demux-type:"int16-on-int32",
}, },
microphone-virtual-alsa:{
io:"input",
map-on:{
interface:"alsa",
name:"AD1989A_inputs",
#name:"hw:0,0",
timestamp-mode:"trigered",
},
frequency:48000,
channel-map:[
"front-left", "front-right"
, "rear-left", "rear-right"
],
type:"int16",
nb-chunk:6000,
mux-demux-type:"int16",
},
speakerGroup:{
io:"output",
map-on:{
interface:"alsa",
name:"hw:0,0",
timestamp-mode:"trigered",
},
group:"baseIOSynchrone",
frequency:48000,
channel-map:[
"front-left", "front-right",
],
type:"int16",
nb-chunk:1024,
volume-name:"MASTER",
mux-demux-type:"int16-on-int32",
},
microphone-front:{ microphone-front:{
io:"input", io:"input",
map-on:{ map-on:{
interface:"alsa", interface:"alsa",
#name:"hw:0,0,0", name:"hw:0,0,1",
name:"AD1989A_inputs", #name:"AD1989A_inputs",
#name:"default", #name:"default",
timestamp-mode:"trigered", timestamp-mode:"trigered",
}, },
@@ -30,17 +65,16 @@
frequency:48000, frequency:48000,
channel-map:[ channel-map:[
"front-left", "front-right" "front-left", "front-right"
, "rear-left", "rear-right"
], ],
type:"int16", type:"int16",
nb-chunk:6000, #16384, nb-chunk:128,
mux-demux-type:"int16", mux-demux-type:"int16",
}, },
microphone-rear:{ microphone-rear:{
io:"input", io:"input",
map-on:{ map-on:{
interface:"alsa", interface:"alsa",
name:"hw:0,0,1", name:"hw:0,0,0",
timestamp-mode:"trigered", timestamp-mode:"trigered",
}, },
#group:"baseIOSynchrone", #group:"baseIOSynchrone",

View File

@@ -1,38 +0,0 @@
{
# name of the virtual interface
microphone:{
# input or output
io:"input",
# name of the harware device
map-on:"microphone-front",
# name of the resampler
resampling-type:"speexdsp",
# some option to the resampler
resampling-option:"quality=10"
},
speaker:{
io:"output",
map-on:"speaker",
resampling-type:"speexdsp",
resampling-option:"quality=10"
},
feedback:{
# note : Feedback is plugged on an output not an input
io:"feedback",
map-on:"speaker",
resampling-type:"speexdsp",
resampling-option:"quality=10"
},
microphone-clean:{
io:"input",
map-on:"microphone-clean",
resampling-type:"speexdsp",
resampling-option:"quality=10"
},
microphone-muxed:{
io:"input",
map-on:"microphone-muxed",
resampling-type:"speexdsp",
resampling-option:"quality=10"
},
}

41
doc/001_bases.bb Normal file
View File

@@ -0,0 +1,41 @@
=?=RIVER: Bases =?=
__________________________________________________
[right][tutorial[000_Build | Next: Tutorals]][/right]
=== Overview:===
===User requires:===
To use ewol you need to know only C++ language. It could be usefull to know:
:** [b]Python[/b] for all build tool.
:** [b]git[/b] for all version management
:** [b]Audio[/b] Basic knowlege of audio streaming af data organisation.
=== Architecture:===
River has been designed to replace the pulseAudio basic asyncronous interface that create
more problem that it will solve. The second point is that is not enougth portable to be
embended in a proprietary software without distributing all the sources (Ios).
Start at this point we will have simple objectives :
:** manage multiple Low level interface: (done by the [lib[airtaudio | AirTAudio]] interface):
::** for linux
:::** Alsa
:::** Pulse
:::** Oss
::** for Mac-OsX
:::** CoreAudio
::** for IOs
:::** CoreAudio (embended version)
::** for Windows
:::** ASIO
::** For Android
:::** Java (JDK-6)
:** Synchronous interface ==> no delay and reduce latency
:** Manage the thread priority (need sometimes to be more reactive)
:** manage mixing of some flow (2 inputs stereo and the user want 1 input quad)
:** AEC Acoustic Echo Cancelation (TODO : in the current implementation we have a simple sound cutter)
:** Equalizer (done with [lib[drain | Drain])
:** Resmpling (done by the libspeexDSP)
:** Correct volume management (and configurable)
:** Fade-in and Fade-out (done with [lib[drain | Drain])
:** Channel reorganisation (done with [lib[drain | Drain])
:** A correct feedback interface

36
doc/faq.bb Normal file
View File

@@ -0,0 +1,36 @@
=?= FAQ =?=
== What is ewol licence ==
This is really simple : APACHE-2 :
Copyright ewol Edouard DUPIN
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
[[http://www.apache.org/licenses/LICENSE-2.0]]
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
== Why we use "DECLARE_FACTORY" Macro ? ==
For some reason!!! But everything might be clear:
:** In ewol we masively use std::shared_ptr<xxx> (I have create my own but it is not "standard" (I like when we use genecic system)).
:** The main class : [class[ewol::Object]] herited from [i]std::enable_shared_from_this<Object>[/i] to permit to access at his own [i]std::shared_ptr[/i].
:** Acces At his own [i]std::shared_ptr[/i] is not allowed in the class contructor/destructor.
:** Many time for meta-widget we need to propagate our [i]std::shared_ptr[/i] in child.
Then for all these reasons, I have create a simple MACRO that create a static template funtion that create the object and just after
creation call the init(...) function to permit to create a complex widget or others with some writing convinience.

69
doc/index.bb Normal file
View File

@@ -0,0 +1,69 @@
== [center]RIVER library[/center] ==
__________________________________________________
===What is RIVER, and how can I use it?===
RIVER is a multi-platform library to manage the input and output audio flow.
It can be compared with PulseAudio or Jack, but at the difference at the 2 interfaces
it is designed to be multi-platform and is based on licence that permit to integrate it
on every program we want.
===Where can I use it?===
Everywhere! RIVER is cross-platform devolopped to support bases OS:
: ** Linux (over Alsa, Pulseaudio, JackD)
: ** Windows (over ASIO)
: ** MacOs (over CoreAudio)
: ** Android (Over Ewol wrapper little complicated need to be change later)
: ** IOs (over CoreAudio for ios)
===What languages are supported?===
RIVER is written in C++11 with posibilities to compile it with C++03 + Boost
===Are there any licensing restrictions?===
RIVER is [b]FREE software[/b] and [i]all sub-library are FREE and staticly linkable !!![/i]
That allow you to use it for every program you want, including those developing proprietary software, without any license fees or royalties.
[note]The static support is important for some platform like IOs, and this limit the external library use at some license like :
:** BSD*
:** MIT
:** APPACHE-2
:** PNG
:** ZLIB
This exclude the classical extern library with licence:
:** L-GPL
:** GPL
[/note]
==== License (APACHE 2) ====
Copyright ewol Edouard DUPIN
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
[[http://www.apache.org/licenses/LICENSE-2.0]]
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==== Depends library: ====
===== License: =====
:** [b][lib[etk | e-tk]][/b] : APACHE-2
:** [b][lib[airtaudio | airtaudio]][/b] : MIT/APACHE-2
:** [b][lib[ejson | e-json]][/b] : APACHE-2
:** [b][lib[drain | Drain]][/b] : APACHE-2
===== Program Using RIVER =====
:** [b][[http://play.google.com/store/apps/details?id=com.edouarddupin.worddown | worddown]][/b] : (Proprietary) Worddown is a simple word game threw [lib[ewolsa | ewol-simple-audio]].
== Main documentation: ==
[doc[001_bases | Global Documantation]]
[tutorial[000_Build | Tutorials]]

57
doc/tutorial/000_Build.bb Normal file
View File

@@ -0,0 +1,57 @@
=?=River extract and build examples an example=?=
All developpement software will start by getting the dependency and the sources.
=== Linux dependency packages ===
[code style=shell]
sudo apt-get install g++ zlib1g-dev libasound2-dev
# if you want to compile with clang :
sudo apt-get install clang
[/code]
=== Download instructions ===
Download the software : This is the simple way You really need only a part of the ewol framework
[code style=shell]
# create a working directory path
mkdir your_workspace_path
cd your_workspace_path
# clone ewol and all sub-library
git clone git://github.com/HeeroYui/ewol.git
cd ewol
git submodule init
git submodule update
cd ..
[/code]
If you prefer creating with the packege you needed :
[code style=shell]
mkdir -p your_workspace_path
cd your_workspace_path
# download all you needs
git clone git://github.com/HeeroYui/lutin.git
git clone git://github.com/HeeroYui/etk.git
git clone git://github.com/HeeroYui/audio.git
git clone git://github.com/HeeroYui/ejson.git
git clone git://github.com/HeeroYui/airtaudio.git
git clone git://github.com/HeeroYui/drain.git
git clone git://github.com/HeeroYui/river.git
[/code]
[note]
The full build tool documentation is availlable here : [[http://heeroyui.github.io/lutin/ | lutin]]
[/note]
=== Common build instructions ===
Build the basic examples & test:
[code style=shell]
./ewol/build/lutin.py -mdebug river_sample_read
[/code]
To run an application you will find it directly on the out 'staging' tree :
[code style=shell]
./out/Linux/debug/staging/clang/river_sample_read/usr/bin/river_sample_read -l4
[/code]

158
doc/tutorial/001_Read.bb Normal file
View File

@@ -0,0 +1,158 @@
=== Objectif ===
:** Understand basis of river
:** Create a simple recording interface that print the average of sample absolute value.
=== sample source: ===
[[http://github.com/HeeroYui/river.git/sample/read/ | sample source]]
=== Bases: ===
When you will create an application based on the river audio interface you need :
==== Include: ====
Include manager and interface node
[code style=c++]
#include <river/river.h>
#include <river/Manager.h>
#include <river/Interface.h>
[/code]
==== Initilize the River library: ====
We first need to initialize etk sub library (needed to select the log level of sub-libraries and file access abstraction
[code style=c++]
// the only one init for etk:
etk::init(_argc, _argv);
[/code]
Now we will initilaize the river library.
To do this We have 2 posibilities:
:** With a file:
[code style=c++]
// initialize river interface
river::init("DATA:configFileName.json");
[/code]
:** With a json string:
[code style=c++]
static const std::string configurationRiver =
"{\n"
" microphone:{\n"
" io:'input',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" },\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024\n"
" }\n"
"}\n";
// initialize river interface
river::initString(configurationRiver);
[/code]
For the example we select the second solution (faster to implement example and resource at the same position.
river::init / river::initString must be called only one time for all the application, this represent the hardware configuration.
It is Nearly not dynamic
To understand the configuration file Please see [tutorial[004_ConfigurationFile | Configuration file]]
[note]
This json is parsed by the [lib[ejson | e-json library]] it containe some update like:
:** Optionnal " in the name of element.
:** The possibilities to remplace " with '.
[/note]
==== Get the river interface manager: ====
An application can have many interface and only one Manager, And a process can contain many application.
Then, we will get the first application manager handle.
[code style=c++]
// Create the River manager for tha application or part of the application.
std11::shared_ptr<river::Manager> manager = river::Manager::create("river_sample_read");
[/code]
[note]
You can get back the application handle when you create a new one with the same name.
[/note]
==== Create your read interface: ====
[code style=c++]
// create interface:
std11::shared_ptr<river::Interface> interface;
//Get the generic input:
interface = manager->createInput(48000,
std::vector<audio::channel>(),
audio::format_int16,
"microphone");
[/code]
Here we create an interface with:
:** The frequency of 48000 Hz.
:** The default Low level definition channel
:** A data interface of 16 bits samples coded in [-32768..32767]
:** Select input interaface name "microphone"
==== Get datas: ====
The best way to get data is to instanciate a simple callback.
The callback is called when sample arrive and you have the nbChunk/frequency
to process the data, otherwise you can generate error in data stream.
[code style=c++]
// set callback mode ...
interface->setInputCallback(std11::bind(&onDataReceived,
std11::placeholders::_1,
std11::placeholders::_2,
std11::placeholders::_3,
std11::placeholders::_4,
std11::placeholders::_5,
std11::placeholders::_6));
[/code]
==== Callback inplementation: ====
Simply declare your function and do what you want inside.
[code style=c++]
void onDataReceived(const void* _data,
const std11::chrono::system_clock::time_point& _time,
size_t _nbChunk,
enum audio::format _format,
uint32_t _frequency,
const std::vector<audio::channel>& _map) {
if (_format == audio::format_int16) {
// stuff here
}
}
[/code]
==== start and stop: ====
[code style=c++]
// start the stream
interface->start();
// wait 10 second ...
sleep(10);
// stop the stream
interface->stop();
[/code]
==== Remove interfaces: ====
[code style=c++]
// remove interface and manager.
interface.reset();
manager.reset();
[/code]

84
doc/tutorial/002_Write.bb Normal file
View File

@@ -0,0 +1,84 @@
=== Objectif ===
:** Understand write audio stream
=== sample source: ===
[[http://github.com/HeeroYui/river.git/sample/write/ | sample source]]
=== Bases: ===
The writing work nearly like the read turoral. Then we will just see what has change.
==== File configuration: ====
[code style=c++]
static const std::string configurationRiver =
"{\n"
" speaker:{\n"
" io:'output',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" },\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024,\n"
" volume-name:'MASTER'\n"
" }\n"
"}\n";
[/code]
==== Create your write interface: ====
[code style=c++]
// create interface:
std11::shared_ptr<river::Interface> interface;
//Get the generic input:
interface = manager->createOutput(48000,
std::vector<audio::channel>(),
audio::format_int16,
"speaker");
[/code]
Here we create an interface with:
:** The frequency of 48000 Hz.
:** The default Low level definition channel
:** A data interface of 16 bits samples coded in [-32768..32767]
:** Select input interaface name "speaker"
==== write datas: ====
The best way to get data is to instanciate a simple callback.
The callback is called when sample are needed and you have the nbChunk/frequency
to generate the data, otherwise you can generate error in data stream.
[code style=c++]
// set callback mode ...
interface->setOutputCallback(std11::bind(&onDataNeeded,
std11::placeholders::_1,
std11::placeholders::_2,
std11::placeholders::_3,
std11::placeholders::_4,
std11::placeholders::_5,
std11::placeholders::_6));
[/code]
==== Callback inplementation: ====
Simply declare your function and do what you want inside.
[code style=c++]
void onDataNeeded(void* _data,
const std11::chrono::system_clock::time_point& _time,
size_t _nbChunk,
enum audio::format _format,
uint32_t _frequency,
const std::vector<audio::channel>& _map) {
if (_format == audio::format_int16) {
// stuff here
}
}
[/code]

View File

@@ -0,0 +1,25 @@
=== Objectif ===
:** Implement a feedback.
=== Bases: ===
A feedback is a stream that is generated by an output.
To get a feedback this is the same implementation of an input and link it on an output.
What change :
[code style=c++]
//Get the generic feedback on speaker:
interface = manager->createFeedback(48000,
std::vector<audio::channel>(),
audio::format_int16,
"speaker");
[/code]
[note]
Input interface does not provide feedback.
[/note]

View File

@@ -0,0 +1,70 @@
=== Objectif ===
:** Understand the architecture of the configuration file.
:** all that can be done with it.
=== Basis: ===
The river configuration file is a json file. We use [lib[ejson | e-json library]] to parse it then we have some writing facilities.
River provide a list a harware interface and virtual interface.
The hardware interface are provided by [lib[airtaudio | AirTAudio library]] then we will plug on every platform.
The file is simply architecture around a list of object:
[code style=json]
{
"speaker":{
},
"microphone":{
},
"mixed-in-out":{
},
}
[/code]
With this config we declare 3 interfaces : speaker, microphone and mixed-in-out.
=== Harware configuration: ===
In every interface we need to define some Element:
:** "io" :
:: Can be input/output/... depending of virtual interface...
:** "map-on": An object to configure airtaudio interface.
:** "frequency": 0 to automatic select one. Or the frequency to open harware device
:** "channel-map": List of all channel in the stream:
::** "front-left"
::** "front-center"
::** "front-right"
::** "rear-left"
::** "rear-center"
::** "rear-right"
::** "surround-left",
::** "surround-right",
::** "sub-woofer",
::** "lfe"
:** "type": Fomat to open the stream:
::** "auto": Detect the best type
::** "int8",
::** "int8-on-int16",
::** "int16",
::** "int16-on-int32",
::** "int24",
::** "int32",
::** "int32-on-int64",
::** "int64",
::** "float",
::** "double"
:** "nb-chunk": Number of chunk to open the stream.

18
lutin_audio-river-test.py Normal file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/python
import lutin.module as module
import lutin.tools as tools
def get_desc():
return "Multi-nodal audio interface test"
def create(target):
myModule = module.Module(__file__, 'audio-river-test', 'BINARY')
myModule.add_src_file([
'test/main.cpp',
'test/debug.cpp'
])
myModule.add_module_depend(['audio-river', 'gtest', 'etk'])
return myModule

40
lutin_audio-river.py Normal file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/python
import lutin.module as module
import lutin.tools as tools
def get_desc():
return "Multi-nodal audio interface"
def create(target):
myModule = module.Module(__file__, 'audio-river', 'LIBRARY')
myModule.add_src_file([
'audio/river/debug.cpp',
'audio/river/river.cpp',
'audio/river/Manager.cpp',
'audio/river/Interface.cpp',
'audio/river/io/Group.cpp',
'audio/river/io/Node.cpp',
'audio/river/io/NodeOrchestra.cpp',
'audio/river/io/NodePortAudio.cpp',
'audio/river/io/NodeAEC.cpp',
'audio/river/io/NodeMuxer.cpp',
'audio/river/io/Manager.cpp'
])
myModule.add_optionnal_module_depend('audio-orchestra', ["c++", "-DAUDIO_RIVER_BUILD_ORCHESTRA"])
myModule.add_optionnal_module_depend('portaudio', ["c++", "-DAUDIO_RIVER_BUILD_PORTAUDIO"])
myModule.add_module_depend(['audio', 'audio-drain', 'ejson'])
myModule.add_export_path(tools.get_current_path(__file__))
# add the currrent module at the
return myModule

View File

@@ -1,41 +0,0 @@
#!/usr/bin/python
import lutinModule as module
import lutinTools as tools
import lutinDebug as debug
def get_desc():
return "river : Multi-nodal audio interface"
def create(target):
myModule = module.Module(__file__, 'river', 'LIBRARY')
myModule.add_src_file([
'river/debug.cpp',
'river/Manager.cpp',
'river/Interface.cpp',
'river/CircularBuffer.cpp',
'river/io/Group.cpp',
'river/io/Node.cpp',
'river/io/NodeAirTAudio.cpp',
'river/io/NodePortAudio.cpp',
'river/io/NodeAEC.cpp',
'river/io/NodeMuxer.cpp',
'river/io/Manager.cpp'
])
myModule.add_optionnal_module_depend('airtaudio', "__AIRTAUDIO_INFERFACE__")
myModule.add_optionnal_module_depend('portaudio', "__PORTAUDIO_INFERFACE__")
myModule.add_module_depend(['audio', 'drain', 'ejson'])
myModule.add_export_path(tools.get_current_path(__file__))
# add the currrent module at the
return myModule

View File

@@ -1,45 +0,0 @@
#!/usr/bin/python
import lutinModule as module
import lutinTools as tools
import lutinDebug as debug
def get_desc():
return "river_test : Multi-nodal audio interface test"
def create(target):
myModule = module.Module(__file__, 'river_test', 'BINARY')
myModule.add_src_file([
'test/main.cpp',
'test/debug.cpp'
])
if target.name=="Windows":
myModule.copy_file('data/hardwareWindows.json', 'hardware.json')
elif target.name=="Linux":
#myModule.copy_file('data/hardwareLinux.json', 'hardware.json')
myModule.copy_file('data/hardwareNao.json', 'hardware.json')
elif target.name=="MacOs":
myModule.copy_file('data/hardwareMacOs.json', 'hardware.json')
elif target.name=="IOs":
myModule.copy_file('data/hardwareIOs.json', 'hardware.json')
elif target.name=="Android":
myModule.copy_file('data/hardwareAndroid.json', 'hardware.json')
else:
debug.warning("unknow target for AIRTAudio : " + target.name);
myModule.copy_file('data/virtual.json', 'virtual.json')
myModule.add_module_depend(['river', 'gtest', 'etk'])
# add the currrent module at the
return myModule

19
monk_river.py Normal file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/python
import monkModule as module
import monkTools as tools
def get_desc():
return "river : Multiple flow input output audio"
def create():
# module name is 'edn' and type binary.
myModule = module.Module(__file__, 'river', 'LIBRARY')
# enable doculentation :
myModule.set_website("http://heeroyui.github.io/river/")
myModule.set_website_sources("http://github.com/heeroyui/river/")
myModule.set_path(tools.get_current_path(__file__) + "/river/")
myModule.set_path_general_doc(tools.get_current_path(__file__) + "/doc/")
# add the currrent module at the
return myModule

View File

@@ -1,279 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <river/CircularBuffer.h>
#include <river/debug.h>
river::CircularBuffer::CircularBuffer(const river::CircularBuffer& _obj) :
m_data(),
m_write(nullptr),
m_read(nullptr),
m_timeRead(),
m_capacity(0),
m_sizeChunk(0),
m_size(0) {
RIVER_CRITICAL("error");
};
/**
* @brief copy operator.
*/
river::CircularBuffer& river::CircularBuffer::operator=(const river::CircularBuffer& _obj) {
RIVER_CRITICAL("error");
return *this;
};
river::CircularBuffer::CircularBuffer() :
m_data(),
m_write(nullptr),
m_read(nullptr),
m_timeRead(),
m_capacity(0),
m_sizeChunk(0),
m_size(0) {
// nothing to do ...
}
river::CircularBuffer::~CircularBuffer() {
m_data.clear();
m_read = nullptr;
m_write = nullptr;
}
void river::CircularBuffer::setCapacity(size_t _capacity, size_t _chunkSize, uint32_t _frequency) {
if ( _chunkSize == m_sizeChunk
&& _capacity == m_capacity) {
clear();
return;
}
RIVER_DEBUG("buffer setCapacity(" << _capacity << "," << _chunkSize << ")");
m_data.clear();
m_write = nullptr;
m_read = nullptr;
m_frequency = _frequency;
m_capacity = _capacity;
m_sizeChunk = _chunkSize;
m_size = 0;
if ( _capacity == 0
|| _chunkSize == 0) {
m_capacity = 0;
m_sizeChunk = 0;
return;
}
m_data.resize(m_capacity*m_sizeChunk, 0);
m_read = &m_data[0];
m_write = &m_data[0];
}
void river::CircularBuffer::setCapacity(std11::chrono::milliseconds _capacity, size_t _chunkSize, uint32_t _frequency) {
uint32_t nbSampleNeeded = _frequency*_capacity.count()/1000;
RIVER_DEBUG("buffer setCapacity(" << _capacity.count() << "ms ," << _chunkSize << ")");
setCapacity(nbSampleNeeded, _chunkSize, _frequency);
}
size_t river::CircularBuffer::getUsedSizeBeforEnd() const {
size_t size;
if (m_read < m_write) {
size = static_cast<uint8_t*>(m_write) - static_cast<uint8_t*>(m_read);
// the size result is in bytes we need to have it in element
size /= m_sizeChunk;
} else if ( m_read == m_write
&& m_size == 0) {
// no element in the buffer
size = 0;
} else {
size = &m_data[0] + (m_capacity*m_sizeChunk) - static_cast<uint8_t*>(m_read);
// the size result is in bytes we need to have it in element
size /= m_sizeChunk;
}
return size;
}
size_t river::CircularBuffer::getFreeSizeBeforEnd() const {
size_t size;
size = &m_data[0]
+ (m_capacity*m_sizeChunk)
- static_cast<uint8_t*>(m_write);
// the size result is in Octet we need to have it in element
size /= m_sizeChunk;
return size;
}
size_t river::CircularBuffer::write(const void* _data, size_t _nbChunk, const std11::chrono::system_clock::time_point& _time) {
size_t nbElementDrop = 0;
size_t freeSizeBeforeEnd = getFreeSizeBeforEnd();
size_t freeSize = m_capacity - m_size;
// Write element in all case
// calculate the number of element that are overwritten
if (freeSize < _nbChunk) {
nbElementDrop = _nbChunk - freeSize;
}
// if User Request a write more important than the size of the buffer ==> update the pointer to feet only on the buffer size
if (m_capacity < _nbChunk) {
RIVER_WARNING("CircularBuffer Write too BIG " << _nbChunk << " buffer max size : " << m_capacity << " (keep last Elements)");
// Move data pointer
_data = static_cast<const uint8_t*>(_data) + (_nbChunk - m_capacity) * m_sizeChunk;
// update size
_nbChunk = m_capacity;
}
// if no element in the FIFO ==> first time write or no more data inside ==> start set the file of the read data ...
if (m_size == 0) {
m_timeRead = _time;
}
// TODO : Check time to push continuous data ...
if (freeSizeBeforeEnd >= _nbChunk) {
// all Data will be copy
memcpy(m_write, _data, _nbChunk * m_sizeChunk);
// update Writing pointer
if (freeSizeBeforeEnd == _nbChunk) {
// update to the end of FIFO ==> update to the start
m_write = &m_data[0];
} else {
m_write = static_cast<uint8_t*>(m_write) + _nbChunk * m_sizeChunk;
}
// Update the number of element in the buffer.
m_size += _nbChunk;
} else {
// copy data to the end of buffer
memcpy(m_write, _data, freeSizeBeforeEnd * m_sizeChunk);
// update Writing pointer ==> end of buffer ==> go to the start
m_write = &m_data[0];
// update data pointer
_data = static_cast<const uint8_t*>(_data) + freeSizeBeforeEnd * m_sizeChunk;
m_size += freeSizeBeforeEnd;
// get the number of element we need to write
_nbChunk -= freeSizeBeforeEnd;
// Copy the las data if needed
if (_nbChunk != 0) {
memcpy(m_write, _data, _nbChunk * m_sizeChunk);
// update Writing pointer
m_write = static_cast<uint8_t*>(m_write) + _nbChunk * m_sizeChunk;
m_size += _nbChunk;
}
}
if (nbElementDrop > 0) {
// if drop element we need to update the reading pointer
m_read = m_write;
m_size = m_capacity;
}
// return the number of element Overwrite
return nbElementDrop;
}
size_t river::CircularBuffer::read(void* _data, size_t _nbChunk) {
return read(_data, _nbChunk, m_timeRead);
}
size_t river::CircularBuffer::read(void* _data, size_t _nbChunk, const std11::chrono::system_clock::time_point& _time) {
size_t nbElementDrop = 0;
// Critical section (theoriquely protected by Mutex)
size_t usedSizeBeforeEnd = getUsedSizeBeforEnd();
// verify if we have elements in the Buffer
if (0 < m_size) {
// check the time of the read :
std11::chrono::nanoseconds deltaTime = m_timeRead - _time;
if (deltaTime.count() == 0) {
// nothing to do ==> just copy data ...
} else if (deltaTime.count() > 0) {
// Add empty sample in the output buffer ...
size_t nbSampleEmpty = m_frequency*deltaTime.count()/100000000;
nbSampleEmpty = std::min(nbSampleEmpty, _nbChunk);
RIVER_WARNING("add Empty sample in the output buffer " << nbSampleEmpty << " / " << _nbChunk);
memset(_data, 0, nbSampleEmpty * m_sizeChunk);
if (nbSampleEmpty == _nbChunk) {
return 0;
}
_nbChunk -= nbSampleEmpty;
} else {
// Remove data from the FIFO
setReadPosition(_time);
}
if (m_size < _nbChunk) {
nbElementDrop = _nbChunk - m_size;
_nbChunk = m_size;
}
m_timeRead += std11::chrono::microseconds(_nbChunk*1000000/m_frequency);
if (usedSizeBeforeEnd >= _nbChunk) {
// all Data will be copy
memcpy(_data, m_read, _nbChunk * m_sizeChunk);
// update Writing pointer
m_read = static_cast<uint8_t*>(m_read) + _nbChunk * m_sizeChunk;
m_size -= _nbChunk;
// update output pointer in case of flush with 0 data
_data = static_cast<uint8_t*>(_data) + _nbChunk * m_sizeChunk;
} else {
// copy data to the end of buffer
memcpy(_data, m_read, usedSizeBeforeEnd * m_sizeChunk);
// update Writing pointer ==> end of buffer ==> go to the start
m_read = &m_data[0];
_data = static_cast<uint8_t*>(_data) + usedSizeBeforeEnd * m_sizeChunk;
m_size -= usedSizeBeforeEnd;
// get the number of element we need to write
_nbChunk -= usedSizeBeforeEnd;
// Copy the last data if needed
if (0 != _nbChunk) {
memcpy(_data, m_read, _nbChunk * m_sizeChunk);
// update Writing pointer
m_read = static_cast<uint8_t*>(m_read) + _nbChunk * m_sizeChunk;
m_size -= _nbChunk;
// update output pointer in case of flush with 0 data
_data = static_cast<uint8_t*>(_data) + _nbChunk * m_sizeChunk;
}
}
} else {
nbElementDrop = _nbChunk;
}
if (0 < nbElementDrop) {
// set 0 in last element of the output
memset(_data, 0, m_sizeChunk * nbElementDrop);
}
// return the number of element droped
return nbElementDrop;
}
void river::CircularBuffer::setReadPosition(const std11::chrono::system_clock::time_point& _time) {
// Critical section (theoriquely protected by Mutex)
size_t usedSizeBeforeEnd = getUsedSizeBeforEnd();
if (0 < m_size) {
// check the time of the read :
std11::chrono::nanoseconds deltaTime = _time - m_timeRead;
size_t nbSampleToRemove = int64_t(m_frequency)*int64_t(deltaTime.count())/1000000000LL;
nbSampleToRemove = std::min(nbSampleToRemove, m_size);
RIVER_VERBOSE("Remove sample in the buffer " << nbSampleToRemove << " / " << m_size);
std11::chrono::nanoseconds updateTime((int64_t(nbSampleToRemove)*1000000000LL)/int64_t(m_frequency));
RIVER_VERBOSE(" add time : " << updateTime.count() << "ns / " << deltaTime.count() << "ns");
if (usedSizeBeforeEnd >= nbSampleToRemove) {
usedSizeBeforeEnd -= nbSampleToRemove;
m_size -= nbSampleToRemove;
m_read = static_cast<uint8_t*>(m_read) + nbSampleToRemove * m_sizeChunk;
} else {
nbSampleToRemove -= usedSizeBeforeEnd;
m_size -= nbSampleToRemove;
m_read = &m_data[0] + nbSampleToRemove*m_sizeChunk;
}
m_timeRead += updateTime;
//m_timeRead += deltaTime;
} else {
m_timeRead = std11::chrono::system_clock::time_point();
}
}
size_t river::CircularBuffer::getFreeSize() const {
return m_capacity - m_size;
}
void river::CircularBuffer::clear() {
RIVER_DEBUG("buffer clear()");
// set pointer to the start
m_read = &m_data[0];
m_write = &m_data[0];
// Clean the number of element in the buffer
m_size = 0;
// Clean all element inside :
memset(&m_data[0], 0, m_sizeChunk * m_capacity);
}

View File

@@ -1,139 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __RIVER_CIRCULAR_BUFFER_H__
#define __RIVER_CIRCULAR_BUFFER_H__
#include <etk/types.h>
#include <vector>
#include <etk/chrono.h>
namespace river {
/**
* For these functions we have 4 solutions :
* - Free Buffer
* ----------------------------------------------------------
* m_data | | |
* ----------------------------------------------------------
* m_write
* m_read
* - Full Buffer
* ----------------------------------------------------------
* m_data |****************************|***************************|
* ----------------------------------------------------------
* m_write
* m_read
* - Buffer in used
* ----------------------------------------------------------
* m_data | |********************| |
* ----------------------------------------------------------
* m_read m_write
* - Buffer out used
* ----------------------------------------------------------
* m_data |****************| |******************|
* ----------------------------------------------------------
* m_read m_write
*/
class CircularBuffer {
private:
std::vector<uint8_t> m_data; //!< data pointer
void* m_write; //!< write pointer
void* m_read; //!< read pointer
std11::chrono::system_clock::time_point m_timeRead; //!< current read time
uint32_t m_frequency;
// TODO : Remove the m_size ==> this is a bad element to be mutex-less
size_t m_size; //!< number of chunk availlable in this buffer
size_t m_capacity; //!< number of chunk available in this Buffer
size_t m_sizeChunk; //!< Size of one chunk (in byte)
public:
CircularBuffer();
~CircularBuffer();
/**
* @brief copy contructor.
* @param[in] _obj Circular buffer object
*/
CircularBuffer(const river::CircularBuffer& _obj);
/**
* @brief copy operator.
* @param[in] _obj Circular buffer object
*/
CircularBuffer& operator=(const river::CircularBuffer& _obj);
/**
* @brief set the capacity of the circular buffer.
* @param[in] _capacity Number of chunk in the buffer.
* @param[in] _chunkSize Size of one chunk.
* @param[in] _frequency Frequency of the buffer
*/
void setCapacity(size_t _capacity, size_t _chunkSize, uint32_t _frequency);
/**
* @brief set the capacity of the circular buffer.
* @param[in] _capacity time in millisecond stored in the buffer.
* @param[in] _chunkSize Size of one chunk.
* @param[in] _frequency Frequency of the buffer
*/
void setCapacity(std11::chrono::milliseconds _capacity, size_t _chunkSize, uint32_t _frequency);
/**
* @brief get free size of the buffer.
* @return Number of free chunk.
*/
size_t getFreeSize() const;
/**
* @brief Get number of chunk in the buffer.
* @return number of chunk.
*/
size_t getSize() const {
return m_size;
}
/**
* @brief Get number of chunk that can be set in the buffer.
* @return number of chunk.
*/
size_t getCapacity() const {
return m_capacity;
}
/**
* @brief Write chunk in the buffer.
* @param[in] _data Pointer on the data.
* @param[in] _nbChunk number of chunk to copy.
* @param[in] _time Time to start write data (if before end ==> not replace data, write only if after end)
* @return Number of chunk copied.
*/
size_t write(const void* _data, size_t _nbChunk, const std11::chrono::system_clock::time_point& _time);
/**
* @brief Read Chunk from the buffer to the pointer data.
* @param[out] _data Pointer on the data.
* @param[in] _nbChunk number of chunk to copy.
* @param[in] _time Time to start read data (if before start ==> add 0 at start, if after, remove unread data)
* @return Number of chunk copied.
*/
size_t read(void* _data, size_t _nbChunk, const std11::chrono::system_clock::time_point& _time);
//! @previous
size_t read(void* _data, size_t _nbChunk);
void setReadPosition(const std11::chrono::system_clock::time_point& _time);
std11::chrono::system_clock::time_point getReadTimeStamp() {
return m_timeRead;
}
/**
* @brief Clear the buffer.
*/
void clear();
private:
/**
* @brief Get number of free chunks before end of buffer.
* @return Number of chunk.
*/
size_t getFreeSizeBeforEnd() const;
/**
* @brief Get number of used chunks before end of buffer.
* @return Number of chunk.
*/
size_t getUsedSizeBeforEnd() const;
};
}
#endif

View File

@@ -1,424 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include "debug.h"
#include "Interface.h"
#include "io/Node.h"
#include <drain/EndPointCallback.h>
#include <drain/EndPointWrite.h>
#include <drain/EndPointRead.h>
#include <drain/Volume.h>
#undef __class__
#define __class__ "Interface"
river::Interface::Interface(void) :
m_node(),
m_name(""),
m_volume(0.0f) {
static uint32_t uid = 0;
m_uid = uid++;
}
bool river::Interface::init(const std::string& _name,
float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std11::shared_ptr<river::io::Node>& _node,
const std11::shared_ptr<const ejson::Object>& _config) {
std::vector<audio::channel> map(_map);
m_name = _name;
m_node = _node;
m_volume = 0.0f;
m_config = _config;
m_mode = river::modeInterface_unknow;
std::string type = m_config->getStringValue("io", "error");
if (type == "output") {
m_mode = river::modeInterface_output;
} else if (type == "input") {
m_mode = river::modeInterface_input;
} else if (type == "feedback") {
m_mode = river::modeInterface_feedback;
}
// register interface to be notify from the volume change.
m_node->registerAsRemote(shared_from_this());
if (map.size() == 0) {
RIVER_INFO("Select auto map system ...");
map = m_node->getInterfaceFormat().getMap();
RIVER_INFO(" ==> " << map);
}
// Create convertion interface
if ( m_node->isInput() == true
&& m_mode == river::modeInterface_input) {
m_process.setInputConfig(m_node->getInterfaceFormat());
// Add volume only if the Low level has a volume (otherwise it will be added by the application)
std11::shared_ptr<drain::VolumeElement> tmpVolume = m_node->getVolume();
if (tmpVolume != nullptr) {
// add all time the volume stage :
std11::shared_ptr<drain::Volume> algo = drain::Volume::create();
//algo->setInputFormat(m_node->getInterfaceFormat());
algo->setName("volume");
m_process.pushBack(algo);
RIVER_INFO(" add volume for node");
algo->addVolumeStage(tmpVolume);
}
m_process.setOutputConfig(drain::IOFormatInterface(map, _format, _freq));
} else if ( m_node->isOutput() == true
&& m_mode == river::modeInterface_output) {
m_process.setInputConfig(drain::IOFormatInterface(map, _format, _freq));
// Add volume only if the Low level has a volume (otherwise it will be added by the application)
std11::shared_ptr<drain::VolumeElement> tmpVolume = m_node->getVolume();
if (tmpVolume != nullptr) {
// add all time the volume stage :
std11::shared_ptr<drain::Volume> algo = drain::Volume::create();
//algo->setOutputFormat(m_node->getInterfaceFormat());
algo->setName("volume");
m_process.pushBack(algo);
RIVER_INFO(" add volume for node");
algo->addVolumeStage(tmpVolume);
}
m_process.setOutputConfig(m_node->getInterfaceFormat());
} else if ( m_node->isOutput() == true
&& m_mode == river::modeInterface_feedback) {
m_process.setInputConfig(m_node->getHarwareFormat());
// note : feedback has no volume stage ...
m_process.setOutputConfig(drain::IOFormatInterface(map, _format, _freq));
} else {
RIVER_ERROR("Can not link virtual interface with type : " << m_mode << " to a hardware interface " << (m_node->isInput()==true?"input":"output"));
return false;
}
return true;
}
std11::shared_ptr<river::Interface> river::Interface::create(const std::string& _name,
float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std11::shared_ptr<river::io::Node>& _node,
const std11::shared_ptr<const ejson::Object>& _config) {
std11::shared_ptr<river::Interface> out = std11::shared_ptr<river::Interface>(new river::Interface());
out->init(_name, _freq, _map, _format, _node, _config);
return out;
}
river::Interface::~Interface() {
//stop(true, true);
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
//m_node->interfaceRemove(shared_from_this());
}
/*
bool river::Interface::hasEndPoint() {
}
*/
void river::Interface::setReadwrite() {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
m_process.removeAlgoDynamic();
if (m_process.hasType<drain::EndPoint>() ) {
RIVER_ERROR("Endpoint is already present ==> can not change");
return;
}
if (m_node->isInput() == true) {
m_process.removeIfLast<drain::EndPoint>();
std11::shared_ptr<drain::EndPointRead> algo = drain::EndPointRead::create();
m_process.pushBack(algo);
} else {
m_process.removeIfFirst<drain::EndPoint>();
std11::shared_ptr<drain::EndPointWrite> algo = drain::EndPointWrite::create();
m_process.pushFront(algo);
}
}
void river::Interface::setOutputCallback(drain::playbackFunction _function) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
if (m_mode != river::modeInterface_output) {
RIVER_ERROR("Can not set output endpoint on other than a output IO");
return;
}
m_process.removeAlgoDynamic();
m_process.removeIfFirst<drain::EndPoint>();
std11::shared_ptr<drain::Algo> algo = drain::EndPointCallback::create(_function);
m_process.pushFront(algo);
}
void river::Interface::setInputCallback(drain::recordFunction _function) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
if (m_mode == river::modeInterface_output) {
RIVER_ERROR("Can not set output endpoint on other than a input or feedback IO");
return;
}
m_process.removeAlgoDynamic();
m_process.removeIfLast<drain::EndPoint>();
std11::shared_ptr<drain::Algo> algo = drain::EndPointCallback::create(_function);
m_process.pushBack(algo);
}
void river::Interface::setWriteCallback(drain::playbackFunctionWrite _function) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
if (m_mode != river::modeInterface_output) {
RIVER_ERROR("Can not set output endpoint on other than a output IO");
return;
}
m_process.removeAlgoDynamic();
std11::shared_ptr<drain::EndPointWrite> algo = m_process.get<drain::EndPointWrite>(0);
if (algo == nullptr) {
return;
}
algo->setCallback(_function);
}
void river::Interface::start(const std11::chrono::system_clock::time_point& _time) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
RIVER_DEBUG("start [BEGIN]");
m_process.updateInterAlgo();
m_node->interfaceAdd(shared_from_this());
RIVER_DEBUG("start [ END ]");
}
void river::Interface::stop(bool _fast, bool _abort) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
RIVER_DEBUG("stop [BEGIN]");
m_node->interfaceRemove(shared_from_this());
RIVER_DEBUG("stop [ END]");
}
void river::Interface::abort() {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
RIVER_DEBUG("abort [BEGIN]");
// TODO :...
RIVER_DEBUG("abort [ END ]");
}
bool river::Interface::setParameter(const std::string& _filter, const std::string& _parameter, const std::string& _value) {
RIVER_DEBUG("setParameter [BEGIN] : '" << _filter << "':'" << _parameter << "':'" << _value << "'");
bool out = false;
if ( _filter == "volume"
&& _parameter != "FLOW") {
RIVER_ERROR("Interface is not allowed to modify '" << _parameter << "' Volume just allowed to modify 'FLOW' volume");
return false;
}
std11::shared_ptr<drain::Algo> algo = m_process.get<drain::Algo>(_filter);
if (algo == nullptr) {
RIVER_ERROR("setParameter(" << _filter << ") ==> no filter named like this ...");
return false;
}
out = algo->setParameter(_parameter, _value);
RIVER_DEBUG("setParameter [ END ] : '" << out << "'");
return out;
}
std::string river::Interface::getParameter(const std::string& _filter, const std::string& _parameter) const {
RIVER_DEBUG("getParameter [BEGIN] : '" << _filter << "':'" << _parameter << "'");
std::string out;
std11::shared_ptr<const drain::Algo> algo = m_process.get<const drain::Algo>(_filter);
if (algo == nullptr) {
RIVER_ERROR("setParameter(" << _filter << ") ==> no filter named like this ...");
return "[ERROR]";
}
out = algo->getParameter(_parameter);
RIVER_DEBUG("getParameter [ END ] : '" << out << "'");
return out;
}
std::string river::Interface::getParameterProperty(const std::string& _filter, const std::string& _parameter) const {
RIVER_DEBUG("getParameterProperty [BEGIN] : '" << _filter << "':'" << _parameter << "'");
std::string out;
std11::shared_ptr<const drain::Algo> algo = m_process.get<const drain::Algo>(_filter);
if (algo == nullptr) {
RIVER_ERROR("setParameter(" << _filter << ") ==> no filter named like this ...");
return "[ERROR]";
}
out = algo->getParameterProperty(_parameter);
RIVER_DEBUG("getParameterProperty [ END ] : '" << out << "'");
return out;
}
void river::Interface::write(const void* _value, size_t _nbChunk) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
m_process.updateInterAlgo();
std11::shared_ptr<drain::EndPointWrite> algo = m_process.get<drain::EndPointWrite>(0);
if (algo == nullptr) {
return;
}
algo->write(_value, _nbChunk);
}
#if 0
// TODO : add API aCCess mutex for Read and write...
std::vector<int16_t> river::Interface::read(size_t _nbChunk) {
// TODO :...
std::vector<int16_t> data;
/*
data.resize(_nbChunk*m_map.size(), 0);
m_mutex.lock();
int32_t nbChunkBuffer = m_circularBuffer.size() / m_map.size();
m_mutex.unlock();
while (nbChunkBuffer < _nbChunk) {
usleep(1000);
nbChunkBuffer = m_circularBuffer.size() / m_map.size();
}
m_mutex.lock();
for (size_t iii = 0; iii<data.size(); ++iii) {
data[iii] = m_circularBuffer[iii];
}
m_circularBuffer.erase(m_circularBuffer.begin(), m_circularBuffer.begin()+data.size());
m_mutex.unlock();
*/
return data;
}
#endif
void river::Interface::read(void* _value, size_t _nbChunk) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
m_process.updateInterAlgo();
// TODO :...
}
size_t river::Interface::size() const {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
// TODO :...
return 0;
}
void river::Interface::setBufferSize(size_t _nbChunk) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
m_process.updateInterAlgo();
// TODO :...
}
void river::Interface::setBufferSize(const std11::chrono::microseconds& _time) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
m_process.updateInterAlgo();
// TODO :...
}
void river::Interface::clearInternalBuffer() {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
m_process.updateInterAlgo();
// TODO :...
}
std11::chrono::system_clock::time_point river::Interface::getCurrentTime() const {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
// TODO :...
return std11::chrono::system_clock::time_point();
return std11::chrono::system_clock::now();
}
void river::Interface::addVolumeGroup(const std::string& _name) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
RIVER_DEBUG("addVolumeGroup(" << _name << ")");
std11::shared_ptr<drain::Volume> algo = m_process.get<drain::Volume>("volume");
if (algo == nullptr) {
m_process.removeAlgoDynamic();
// add all time the volume stage :
algo = drain::Volume::create();
algo->setName("volume");
if (m_node->isInput() == true) {
m_process.pushFront(algo);
} else {
m_process.pushBack(algo);
}
}
if (_name == "FLOW") {
// Local volume name
algo->addVolumeStage(std11::make_shared<drain::VolumeElement>(_name));
} else {
// get manager unique instance:
std11::shared_ptr<river::io::Manager> mng = river::io::Manager::getInstance();
algo->addVolumeStage(mng->getVolumeGroup(_name));
}
}
void river::Interface::systemNewInputData(std11::chrono::system_clock::time_point _time, const void* _data, size_t _nbChunk) {
std11::unique_lock<std11::recursive_mutex> lockProcess(m_mutex);
void * tmpData = const_cast<void*>(_data);
m_process.push(_time, tmpData, _nbChunk);
}
void river::Interface::systemNeedOutputData(std11::chrono::system_clock::time_point _time, void* _data, size_t _nbChunk, size_t _chunkSize) {
std11::unique_lock<std11::recursive_mutex> lockProcess(m_mutex);
//RIVER_INFO("time : " << _time);
m_process.pull(_time, _data, _nbChunk, _chunkSize);
}
void river::Interface::systemVolumeChange() {
std11::unique_lock<std11::recursive_mutex> lockProcess(m_mutex);
std11::shared_ptr<drain::Volume> algo = m_process.get<drain::Volume>("volume");
if (algo == nullptr) {
return;
}
algo->volumeChange();
}
static void link(etk::FSNode& _node, const std::string& _first, const std::string& _op, const std::string& _second, bool _isLink=true) {
if (_op == "->") {
if (_isLink) {
_node << " " << _first << " -> " << _second << ";\n";
} else {
_node << " " << _first << " -> " << _second << " [style=dashed];\n";
}
} else if (_op == "<-") {
_node << " " << _first << " -> " <<_second<< " [color=transparent];\n";
if (_isLink) {
_node << " " << _second << " -> " << _first << " [constraint=false];\n";
} else {
_node << " " << _second << " -> " << _first << " [constraint=false, style=dashed];\n";
}
}
}
std::string river::Interface::getDotNodeName() const {
if (m_mode == river::modeInterface_input) {
return "API_" + etk::to_string(m_uid) + "_input";
} else if (m_mode == river::modeInterface_feedback) {
return "API_" + etk::to_string(m_uid) + "_feedback";
} else if (m_mode == river::modeInterface_output) {
return "API_" + etk::to_string(m_uid) + "_output";
}
return "error";
}
void river::Interface::generateDot(etk::FSNode& _node, const std::string& _nameIO, bool _isLink) {
_node << " subgraph clusterInterface_" << m_uid << " {\n";
_node << " color=orange;\n";
_node << " label=\"[" << m_uid << "] Interface : " << m_name << "\";\n";
std::string nameIn;
std::string nameOut;
if ( m_mode == river::modeInterface_input
|| m_mode == river::modeInterface_feedback) {
m_process.generateDot(_node, 3, 10000+m_uid, nameIn, nameOut, false);
} else {
m_process.generateDot(_node, 3, 10000+m_uid, nameIn, nameOut, true);
}
if ( m_mode == river::modeInterface_input
|| m_mode == river::modeInterface_feedback) {
link(_node, _nameIO, "->", nameIn, _isLink);
} else {
link(_node, _nameIO, "<-", nameOut, _isLink);
}
_node << " node [shape=Mdiamond];\n";
if (m_mode == river::modeInterface_input) {
_node << " " << getDotNodeName() << " [ label=\"API\\nINPUT\" ];\n";
link(_node, nameOut, "->", getDotNodeName());
} else if (m_mode == river::modeInterface_feedback) {
_node << " " << getDotNodeName() << " [ label=\"API\\nFEEDBACK\" ];\n";
link(_node, nameOut, "->", getDotNodeName());
} else if (m_mode == river::modeInterface_output) {
_node << " " << getDotNodeName() << " [ label=\"API\\nOUTPUT\" ];\n";
link(_node, nameIn, "<-", getDotNodeName());
}
_node << " }\n \n";
}

View File

@@ -1,218 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __RIVER_INTERFACE_H__
#define __RIVER_INTERFACE_H__
#include <string>
#include <vector>
#include <stdint.h>
#include <etk/mutex.h>
#include <etk/chrono.h>
#include <etk/functional.h>
#include <etk/memory.h>
#include <audio/format.h>
#include <audio/channel.h>
#include <drain/Process.h>
#include <drain/EndPointCallback.h>
#include <drain/EndPointWrite.h>
#include <ejson/ejson.h>
#include <etk/os/FSNode.h>
namespace river {
namespace io {
class Node;
class NodeAirTAudio;
class NodeAEC;
class NodeMuxer;
}
enum modeInterface {
modeInterface_unknow,
modeInterface_input,
modeInterface_output,
modeInterface_feedback,
};
class Interface : public std11::enable_shared_from_this<Interface> {
friend class io::Node;
friend class io::NodeAirTAudio;
friend class io::NodeAEC;
friend class io::NodeMuxer;
friend class Manager;
protected:
uint32_t m_uid; //!< unique ID for interface
protected:
/**
* @brief Constructor
*/
Interface();
bool init(const std::string& _name,
float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std11::shared_ptr<river::io::Node>& _node,
const std11::shared_ptr<const ejson::Object>& _config);
public:
/**
* @brief Destructor
*/
virtual ~Interface();
static std11::shared_ptr<Interface> create(const std::string& _name,
float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std11::shared_ptr<river::io::Node>& _node,
const std11::shared_ptr<const ejson::Object>& _config);
protected:
mutable std11::recursive_mutex m_mutex;
std11::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:
std11::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.
*/
virtual void setReadwrite();
/**
* @brief When we want to implement a Callback Mode:
*/
virtual void setWriteCallback(drain::playbackFunctionWrite _function);
virtual void setOutputCallback(drain::playbackFunction _function);
virtual void setInputCallback(drain::recordFunction _function);
/**
* @brief Add a volume group of the current channel.
* @note If you do not call this function with the group "FLOW" you chan not have a channel volume.
* @note the set volume stage can not be set after the start.
* @param[in] _name Name of the group classicle common group:
* - FLOW for channel volume.
* - MEDIA for multimedia volume control (audio player, player video, web streaming ...).
* - TTS for Test-to-speech volume control.
* - COMMUNICATION for user communication volume control.
* - NOTIFICATION for urgent notification volume control.
* - NOISE for small noise volume control.
*/
virtual void addVolumeGroup(const std::string& _name);
public:
/**
* @brief Start the Audio interface flow.
* @param[in] _time Time to start the flow (0) to start as fast as possible...
* @note _time to play buffer when output interface (if possible)
* @note _time to read buffer when inut interface (if possible)
*/
virtual void start(const std11::chrono::system_clock::time_point& _time = std11::chrono::system_clock::time_point());
/**
* @brief Stop the current flow.
* @param[in] _fast The stream stop as fast as possible (not write all the buffer in speaker) but apply cross fade out.
* @param[in] _abort The stream stop whith no garenty of good audio stop.
*/
virtual void stop(bool _fast=false, bool _abort=false);
/**
* @brief Abort flow (no audio garenty)
*/
virtual void abort();
/**
* @brief Set a parameter in the stream flow
* @param[in] _filter name of the filter (if you added some personels)
* @param[in] _parameter Parameter name.
* @param[in] _value Value to set.
* @return true set done
* @return false An error occured
* @example : setParameter("volume", "FLOW", "-3dB");
* @example : setParameter("LowPassFilter", "cutFrequency", "1000Hz");
*/
virtual bool setParameter(const std::string& _filter, const std::string& _parameter, const std::string& _value);
/**
* @brief Get a parameter value
* @param[in] _filter name of the filter (if you added some personels)
* @param[in] _parameter Parameter name.
* @return The requested value.
* @example : getParameter("volume", "FLOW"); can return something like "-3dB"
* @example : getParameter("LowPassFilter", "cutFrequency"); can return something like "[-120..0]dB"
*/
virtual std::string getParameter(const std::string& _filter, const std::string& _parameter) const;
/**
* @brief Get a parameter value
* @param[in] _filter name of the filter (if you added some personels)
* @param[in] _parameter Parameter name.
* @return The requested value.
* @example : getParameter("volume", "FLOW"); can return something like "[-120..0]dB"
* @example : getParameter("LowPassFilter", "cutFreqiency"); can return something like "]100..10000]Hz"
*/
virtual std::string getParameterProperty(const std::string& _filter, const std::string& _parameter) const;
/**
* @brief write some audio sample in the speakers
* @param[in] _value Data To write on output
* @param[in] _nbChunk Number of audio chunk to write
*/
// TODO : TimeOut ???
virtual void write(const void* _value, size_t _nbChunk);
/**
* @brief read some audio sample from Microphone
* @param[in] _value Data To write on output
* @param[in] _nbChunk Number of audio chunk to write
*/
// TODO : TimeOut ???
virtual void read(void* _value, size_t _nbChunk);
/**
* @brief Get number of chunk in the local buffer
* @return Number of chunk
*/
virtual size_t size() const;
/**
* @brief Set buffer size in chunk number
* @param[in] _nbChunk Number of chunk in the buffer
*/
virtual void setBufferSize(size_t _nbChunk);
/**
* @brief Set buffer size in chunk number
* @param[in] _nbChunk Number of chunk in the buffer
*/
virtual void setBufferSize(const std11::chrono::microseconds& _time);
/**
* @brief Remove internal Buffer
*/
virtual void clearInternalBuffer();
/**
* @brief Write : Get the time of the next sample time to write in the local buffer
* @brief Read : Get the time of the next sample time to read in the local buffer
*/
virtual std11::chrono::system_clock::time_point getCurrentTime() const;
private:
virtual void systemNewInputData(std11::chrono::system_clock::time_point _time, const void* _data, size_t _nbChunk);
virtual void systemNeedOutputData(std11::chrono::system_clock::time_point _time, void* _data, size_t _nbChunk, size_t _chunkSize);
virtual void systemVolumeChange();
float m_volume; //!< Local channel Volume
public:
virtual void generateDot(etk::FSNode& _node, const std::string& _nameIO, bool _isLink=true);
virtual std::string getDotNodeName() const;
};
};
#endif

View File

@@ -1,175 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include "Manager.h"
#include "Interface.h"
#include <stdexcept>
#include "io/Manager.h"
#include "io/Node.h"
#include "debug.h"
#undef __class__
#define __class__ "Manager"
static std::string basicAutoConfig =
"{\n"
" microphone:{\n"
" io:'input',\n"
" map-on:'microphone',\n"
" resampling-type:'speexdsp',\n"
" resampling-option:'quality=10'\n"
" },\n"
" speaker:{\n"
" io:'output',\n"
" map-on:'speaker',\n"
" resampling-type:'speexdsp',\n"
" resampling-option:'quality=10'\n"
" }\n"
"}\n";
std11::shared_ptr<river::Manager> river::Manager::create(const std::string& _applicationUniqueId) {
return std11::shared_ptr<river::Manager>(new river::Manager(_applicationUniqueId));
}
river::Manager::Manager(const std::string& _applicationUniqueId) :
m_applicationUniqueId(_applicationUniqueId),
m_listOpenInterface() {
// TODO : Maybe create a single interface property (and all get the same ...)
if (m_config.load("DATA:virtual.json") == false) {
RIVER_WARNING("you must set a basic configuration file for virtual configuration: DATA:virtual.json (load default interface)");
m_config.parse(basicAutoConfig);
}
}
river::Manager::~Manager() {
// TODO : Stop all interfaces...
}
std::vector<std::pair<std::string,std::string> > river::Manager::getListStreamInput() {
std::vector<std::pair<std::string,std::string> > output;
std::vector<std::string> keys = m_config.getKeys();
for (size_t iii=0; iii<keys.size(); ++iii) {
const std11::shared_ptr<const ejson::Object> tmppp = m_config.getObject(keys[iii]);
if (tmppp != nullptr) {
std::string type = tmppp->getStringValue("io", "error");
if ( type == "input"
|| type == "feedback") {
output.push_back(std::make_pair<std::string,std::string>(std::string(keys[iii]), std::string("---")));
}
}
}
return output;
}
std::vector<std::pair<std::string,std::string> > river::Manager::getListStreamOutput() {
std::vector<std::pair<std::string,std::string> > output;
std::vector<std::string> keys = m_config.getKeys();
for (size_t iii=0; iii<keys.size(); ++iii) {
const std11::shared_ptr<const ejson::Object> tmppp = m_config.getObject(keys[iii]);
if (tmppp != nullptr) {
std::string type = tmppp->getStringValue("io", "error");
if (type == "output") {
output.push_back(std::make_pair<std::string,std::string>(std::string(keys[iii]), std::string("---")));
}
}
}
return output;
}
bool river::Manager::setVolume(const std::string& _volumeName, float _valuedB) {
return river::io::Manager::getInstance()->setVolume(_volumeName, _valuedB);
}
float river::Manager::getVolume(const std::string& _volumeName) const {
return river::io::Manager::getInstance()->getVolume(_volumeName);
}
std::pair<float,float> river::Manager::getVolumeRange(const std::string& _volumeName) const {
return river::io::Manager::getInstance()->getVolumeRange(_volumeName);
}
std11::shared_ptr<river::Interface> river::Manager::createOutput(float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std::string& _streamName,
const std::string& _name) {
// check if the output exist
const std11::shared_ptr<const ejson::Object> tmppp = m_config.getObject(_streamName);
if (tmppp == nullptr) {
RIVER_ERROR("can not open a non existance virtual input: '" << _streamName << "' not present in : " << m_config.getKeys());
return std11::shared_ptr<river::Interface>();
}
// check if it is an Output:
std::string type = tmppp->getStringValue("io", "error");
if (type != "output") {
RIVER_ERROR("can not open in output a virtual interface: '" << _streamName << "' configured has : " << type);
return std11::shared_ptr<river::Interface>();
}
std::string mapOn = tmppp->getStringValue("map-on", "");
if (mapOn == "") {
RIVER_ERROR("can not open in output a virtual interface: '" << _streamName << "' No 'map-on' element in json file ... ");
return std11::shared_ptr<river::Interface>();
}
// get global hardware interface:
std11::shared_ptr<river::io::Manager> manager = river::io::Manager::getInstance();
// get the output or input channel :
std11::shared_ptr<river::io::Node> node = manager->getNode(mapOn);
// create user iterface:
std11::shared_ptr<river::Interface> interface;
interface = river::Interface::create(_name, _freq, _map, _format, node, tmppp);
// store it in a list (needed to apply some parameters).
m_listOpenInterface.push_back(interface);
return interface;
}
std11::shared_ptr<river::Interface> river::Manager::createInput(float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std::string& _streamName,
const std::string& _name) {
// check if the output exist
const std11::shared_ptr<const ejson::Object> tmppp = m_config.getObject(_streamName);
if (tmppp == nullptr) {
RIVER_ERROR("can not open a non existance virtual interface: '" << _streamName << "' not present in : " << m_config.getKeys());
return std11::shared_ptr<river::Interface>();
}
// 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 std11::shared_ptr<river::Interface>();
}
std::string mapOn = tmppp->getStringValue("map-on", "");
if (mapOn == "") {
RIVER_ERROR("can not open in output a virtual interface: '" << _streamName << "' No 'map-on' element in json file ... ");
return std11::shared_ptr<river::Interface>();
}
// get global hardware interface:
std11::shared_ptr<river::io::Manager> manager = river::io::Manager::getInstance();
// get the output or input channel :
std11::shared_ptr<river::io::Node> node = manager->getNode(mapOn);
// create user iterface:
std11::shared_ptr<river::Interface> interface;
interface = river::Interface::create(_name, _freq, _map, _format, node, tmppp);
// store it in a list (needed to apply some parameters).
m_listOpenInterface.push_back(interface);
return interface;
}
void river::Manager::generateDotAll(const std::string& _filename) {
// get global hardware interface:
std11::shared_ptr<river::io::Manager> manager = river::io::Manager::getInstance();
if (manager == nullptr) {
RIVER_ERROR("Can not get the harware manager");
return;
}
manager->generateDot(_filename);
}

View File

@@ -1,110 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __RIVER_MANAGER_H__
#define __RIVER_MANAGER_H__
#include <string>
#include <stdint.h>
#include <etk/memory.h>
#include <river/Interface.h>
#include <audio/format.h>
#include <audio/channel.h>
#include <ejson/ejson.h>
namespace river {
/**
* @brief Audio interface manager : Single interface for every application that want to access on the Audio input/output
*/
class Manager {
private:
ejson::Document m_config; // virtual configuration
const std::string& m_applicationUniqueId; //!< name of the application that open the Audio Interface.
std::vector<std11::weak_ptr<river::Interface> > m_listOpenInterface; //!< List of all open Stream.
protected:
/**
* @brief Constructor
*/
Manager(const std::string& _applicationUniqueId);
public:
static std11::shared_ptr<river::Manager> create(const std::string& _applicationUniqueId);
/**
* @brief Destructor
*/
virtual ~Manager();
public:
/**
* @brief Get all input audio stream description.
* @return a list of all availlables input stream (name + description)
*/
virtual std::vector<std::pair<std::string,std::string> > getListStreamInput();
/**
* @brief Get all output audio stream description.
* @return a list of all availlables output stream (name + description)
*/
virtual std::vector<std::pair<std::string,std::string> > getListStreamOutput();
/**
* @brief Set a volume for a specific group
* @param[in] _volumeName Name of the volume (MASTER, MATER_BT ...)
* @param[in] _value Volume in dB to set.
* @return true set done
* @return false An error occured
* @example : setVolume("MASTER", -3.0f);
*/
virtual bool setVolume(const std::string& _volumeName, float _valuedB);
/**
* @brief Get a volume value
* @param[in] _volumeName Name of the volume (MASTER, MATER_BT ...)
* @return The Volume value in dB.
* @example ret = getVolume("MASTER"); can return something like ret = -3.0f
*/
virtual float getVolume(const std::string& _volumeName) const;
/**
* @brief Get a parameter value
* @param[in] _volumeName Name of the volume (MASTER, MATER_BT ...)
* @return The requested value Range.
* @example ret = getVolumeRange("MASTER"); can return something like ret=(-120.0f,0.0f)
*/
virtual std::pair<float,float> getVolumeRange(const std::string& _volumeName) const;
/**
* @brief Create output Interface
* @param[in] _freq Frequency to open Interface [8,16,22,32,48] kHz
* @param[in] _map ChannelMap of the Output
* @param[in] _format Sample Format to open the stream [int8_t]
* @param[in] _streamName Stream name to open: "" or "default" open current selected output
* @param[in] _name Name of this interface
* @return a pointer on the interface
*/
virtual std11::shared_ptr<Interface> createOutput(float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std::string& _streamName = "",
const std::string& _name = "");
/**
* @brief Create input Interface
* @param[in] _freq Frequency to open Interface [8,16,22,32,48] kHz
* @param[in] _map ChannelMap of the Output
* @param[in] _format Sample Format to open the stream [int8_t]
* @param[in] _streamName Stream name to open: "" or "default" open current selected input
* @param[in] _name Name of this interface
* @return a pointer on the interface
*/
virtual std11::shared_ptr<Interface> createInput(float _freq,
const std::vector<audio::channel>& _map,
audio::format _format,
const std::string& _streamName = "",
const std::string& _name = "");
/**
* @brief Generate the dot file corresponding at all the actif nodes.
* @param[in] _filename Name of the file to write data.
*/
virtual void generateDotAll(const std::string& _filename);
};
};
#endif

View File

@@ -1,36 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __RIVER_IO_GROUP_H__
#define __RIVER_IO_GROUP_H__
#include <string>
#include <vector>
#include <ejson/ejson.h>
#include <etk/os/FSNode.h>
namespace river {
namespace io {
class Node;
class Manager;
class Group : public std11::enable_shared_from_this<Group> {
public:
Group() {}
~Group() {}
private:
std::vector< std11::shared_ptr<Node> > m_list;
public:
void createFrom(const ejson::Document& _obj, const std::string& _name);
std11::shared_ptr<river::io::Node> getNode(const std::string& _name);
void start();
void stop();
void generateDot(etk::FSNode& _node, bool _hardwareNode);
};
}
}
#endif

View File

@@ -1,301 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include "Manager.h"
#include <river/debug.h>
#include "Node.h"
#include "NodeAEC.h"
#include "NodeMuxer.h"
#include "NodeAirTAudio.h"
#include "NodePortAudio.h"
#include <etk/os/FSNode.h>
#include <etk/memory.h>
#include <etk/types.h>
#include <utility>
#undef __class__
#define __class__ "io::Manager"
#ifdef __PORTAUDIO_INFERFACE__
#include <portaudio.h>
#endif
static std::string basicAutoConfig =
"{\n"
" microphone:{\n"
" io:'input',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" },\n"
" frequency:0,\n"
" channel-map:[\n"
" 'front-left', 'front-right'\n"
" ],\n"
" type:'auto',\n"
" nb-chunk:1024\n"
" },\n"
" speaker:{\n"
" io:'output',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" },\n"
" frequency:0,\n"
" channel-map:[\n"
" 'front-left', 'front-right',\n"
" ],\n"
" type:'auto',\n"
" nb-chunk:1024,\n"
" volume-name:'MASTER'\n"
" }\n"
"}\n";
river::io::Manager::Manager() {
if (m_config.load("DATA:hardware.json") == false) {
RIVER_WARNING("you must set a basic configuration file for harware configuration: DATA:hardware.json (load default interface)");
m_config.parse(basicAutoConfig);
}
// 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
};
std11::shared_ptr<river::io::Manager> river::io::Manager::getInstance() {
static std11::shared_ptr<river::io::Manager> manager(new Manager());
return manager;
}
std11::shared_ptr<river::io::Node> river::io::Manager::getNode(const std::string& _name) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
RIVER_WARNING("Get node : " << _name);
// search in the standalone list :
for (size_t iii=0; iii<m_list.size(); ++iii) {
std11::shared_ptr<river::io::Node> tmppp = m_list[iii].lock();
if ( tmppp != nullptr
&& _name == tmppp->getName()) {
RIVER_WARNING(" find it ... in standalone");
return tmppp;
}
}
// search in the group list:
{
for (std::map<std::string, std11::shared_ptr<river::io::Group> >::iterator it(m_listGroup.begin());
it != m_listGroup.end();
++it) {
if (it->second != nullptr) {
std11::shared_ptr<river::io::Node> node = it->second->getNode(_name);
if (node != nullptr) {
RIVER_WARNING(" find it ... in group: " << it->first);
return node;
}
}
}
}
RIVER_WARNING("Create a new one : " << _name);
// check if the node can be open :
const std11::shared_ptr<const ejson::Object> tmpObject = m_config.getObject(_name);
if (tmpObject != nullptr) {
//Check if it is in a group:
std::string groupName = tmpObject->getStringValue("group", "");
// get type : io
std::string ioType = tmpObject->getStringValue("io", "error");
if ( groupName != ""
&& ( ioType == "input"
|| ioType == "output"
|| ioType == "PAinput"
|| ioType == "PAoutput") ) {
std11::shared_ptr<river::io::Group> tmpGroup = getGroup(groupName);
if (tmpGroup == nullptr) {
RIVER_WARNING("Can not get group ... '" << groupName << "'");
return std11::shared_ptr<river::io::Node>();
}
return tmpGroup->getNode(_name);
} else {
if (groupName != "") {
RIVER_WARNING("Group is only availlable for Hardware interface ... '" << _name << "'");
}
// TODO : Create a standalone group for every single element ==> simplify understanding ... but not for virtual interface ...
#ifdef __AIRTAUDIO_INFERFACE__
if ( ioType == "input"
|| ioType == "output") {
std11::shared_ptr<river::io::Node> tmp = river::io::NodeAirTAudio::create(_name, tmpObject);
m_list.push_back(tmp);
return tmp;
}
#endif
#ifdef __PORTAUDIO_INFERFACE__
if ( ioType == "PAinput"
|| ioType == "PAoutput") {
std11::shared_ptr<river::io::Node> tmp = river::io::NodePortAudio::create(_name, tmpObject);
m_list.push_back(tmp);
return tmp;
}
#endif
if (ioType == "aec") {
std11::shared_ptr<river::io::Node> tmp = river::io::NodeAEC::create(_name, tmpObject);
m_list.push_back(tmp);
return tmp;
}
if (ioType == "muxer") {
std11::shared_ptr<river::io::Node> tmp = river::io::NodeMuxer::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 std11::shared_ptr<river::io::Node>();
}
std11::shared_ptr<drain::VolumeElement> river::io::Manager::getVolumeGroup(const std::string& _name) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
if (_name == "") {
RIVER_ERROR("Try to create an audio group with no name ...");
return std11::shared_ptr<drain::VolumeElement>();
}
for (size_t iii=0; iii<m_volumeGroup.size(); ++iii) {
if (m_volumeGroup[iii] == nullptr) {
continue;
}
if (m_volumeGroup[iii]->getName() == _name) {
return m_volumeGroup[iii];
}
}
RIVER_DEBUG("Add a new volume group : '" << _name << "'");
std11::shared_ptr<drain::VolumeElement> tmpVolume = std11::make_shared<drain::VolumeElement>(_name);
m_volumeGroup.push_back(tmpVolume);
return tmpVolume;
}
bool river::io::Manager::setVolume(const std::string& _volumeName, float _valuedB) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
std11::shared_ptr<drain::VolumeElement> volume = getVolumeGroup(_volumeName);
if (volume == nullptr) {
RIVER_ERROR("Can not set volume ... : '" << _volumeName << "'");
return false;
}
if ( _valuedB < -300
|| _valuedB > 300) {
RIVER_ERROR("Can not set volume ... : '" << _volumeName << "' out of range : [-300..300]");
return false;
}
volume->setVolume(_valuedB);
for (size_t iii=0; iii<m_list.size(); ++iii) {
std11::shared_ptr<river::io::Node> val = m_list[iii].lock();
if (val != nullptr) {
val->volumeChange();
}
}
return true;
}
float river::io::Manager::getVolume(const std::string& _volumeName) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
std11::shared_ptr<drain::VolumeElement> volume = getVolumeGroup(_volumeName);
if (volume == nullptr) {
RIVER_ERROR("Can not get volume ... : '" << _volumeName << "'");
return 0.0f;
}
return volume->getVolume();
}
std::pair<float,float> river::io::Manager::getVolumeRange(const std::string& _volumeName) const {
return std::make_pair<float,float>(-300, 300);
}
void river::io::Manager::generateDot(const std::string& _filename) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
etk::FSNode node(_filename);
RIVER_INFO("Generate the DOT files: " << node);
if (node.fileOpenWrite() == false) {
RIVER_ERROR("Can not Write the dot file (fail to open) : " << node);
return;
}
node << "digraph G {" << "\n";
node << " rankdir=\"LR\";\n";
// First Step : Create all HW interface:
{
// standalone
for (size_t iii=0; iii<m_list.size(); ++iii) {
std11::shared_ptr<river::io::Node> val = m_list[iii].lock();
if (val != nullptr) {
if (val->isHarwareNode() == true) {
val->generateDot(node);
}
}
}
for (std::map<std::string, std11::shared_ptr<river::io::Group> >::iterator it(m_listGroup.begin());
it != m_listGroup.end();
++it) {
if (it->second != nullptr) {
it->second->generateDot(node, true);
}
}
}
// All other ...
{
// standalone
for (size_t iii=0; iii<m_list.size(); ++iii) {
std11::shared_ptr<river::io::Node> val = m_list[iii].lock();
if (val != nullptr) {
if (val->isHarwareNode() == false) {
val->generateDot(node);
}
}
}
for (std::map<std::string, std11::shared_ptr<river::io::Group> >::iterator it(m_listGroup.begin());
it != m_listGroup.end();
++it) {
if (it->second != nullptr) {
it->second->generateDot(node, false);
}
}
}
node << "}" << "\n";
node.fileClose();
RIVER_INFO("Generate the DOT files: " << node << " (DONE)");
}
std11::shared_ptr<river::io::Group> river::io::Manager::getGroup(const std::string& _name) {
std11::unique_lock<std11::recursive_mutex> lock(m_mutex);
std11::shared_ptr<river::io::Group> out;
std::map<std::string, std11::shared_ptr<river::io::Group> >::iterator it = m_listGroup.find(_name);
if (it == m_listGroup.end()) {
RIVER_INFO("Create a new group: " << _name << " (START)");
out = std11::make_shared<river::io::Group>();
if (out != nullptr) {
out->createFrom(m_config, _name);
std::pair<std::string, std11::shared_ptr<river::io::Group> > plop(std::string(_name), out);
m_listGroup.insert(plop);
RIVER_INFO("Create a new group: " << _name << " ( END )");
} else {
RIVER_ERROR("Can not create new group: " << _name << " ( END )");
}
} else {
out = it->second;
}
return out;
}

View File

@@ -1,90 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __RIVER_IO_MANAGER_H__
#define __RIVER_IO_MANAGER_H__
#include <string>
#include <vector>
#include <map>
#include <list>
#include <stdint.h>
#include <etk/mutex.h>
#include <etk/chrono.h>
#include <etk/functional.h>
#include <etk/memory.h>
#include <audio/format.h>
#include <audio/channel.h>
#include <ejson/ejson.h>
#include <drain/Volume.h>
#include <river/io/Group.h>
namespace river {
namespace io {
class Node;
class Manager {
private:
mutable std11::recursive_mutex m_mutex;
private:
/**
* @brief Constructor
*/
Manager();
public:
static std11::shared_ptr<Manager> getInstance();
/**
* @brief Destructor
*/
virtual ~Manager();
private:
ejson::Document m_config; // harware configuration
std::vector<std11::shared_ptr<river::io::Node> > m_listKeepAlive; //!< list of all Node that might be keep alive sone time
std::vector<std11::weak_ptr<river::io::Node> > m_list; //!< List of all IO node
public:
std11::shared_ptr<river::io::Node> getNode(const std::string& _name);
private:
std::vector<std11::shared_ptr<drain::VolumeElement> > m_volumeGroup;
public:
std11::shared_ptr<drain::VolumeElement> getVolumeGroup(const std::string& _name);
/**
* @brief Set a volume for a specific group
* @param[in] _volumeName Name of the volume (MASTER, MATER_BT ...)
* @param[in] _value Volume in dB to set.
* @return true set done
* @return false An error occured
* @example : setVolume("MASTER", -3.0f);
*/
virtual bool setVolume(const std::string& _volumeName, float _valuedB);
/**
* @brief Get a volume value
* @param[in] _volumeName Name of the volume (MASTER, MATER_BT ...)
* @return The Volume value in dB.
* @example ret = getVolume("MASTER"); can return something like ret = -3.0f
*/
virtual float getVolume(const std::string& _volumeName);
/**
* @brief Get a parameter value
* @param[in] _volumeName Name of the volume (MASTER, MATER_BT ...)
* @return The requested value Range.
* @example ret = getVolumeRange("MASTER"); can return something like ret=(-120.0f,0.0f)
*/
virtual std::pair<float,float> getVolumeRange(const std::string& _volumeName) const;
/**
* @brief Generate the dot file corresponding at the actif nodes.
* @param[in] _filename Name of the file to write data.
*/
virtual void generateDot(const std::string& _filename);
private:
std::map<std::string, std11::shared_ptr<river::io::Group> > m_listGroup; //!< List of all groups
std11::shared_ptr<river::io::Group> getGroup(const std::string& _name);
};
}
}
#endif

View File

@@ -1,130 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __RIVER_IO_NODE_H__
#define __RIVER_IO_NODE_H__
#include <string>
#include <vector>
#include <list>
#include <stdint.h>
#include <etk/chrono.h>
#include <etk/functional.h>
#include <etk/memory.h>
#include <audio/format.h>
#include <audio/channel.h>
#include "Manager.h"
#include <river/Interface.h>
#include <airtaudio/Interface.h>
#include <drain/IOFormatInterface.h>
#include <drain/Volume.h>
#include <etk/os/FSNode.h>
namespace river {
namespace io {
class Manager;
class Group;
class Node : public std11::enable_shared_from_this<Node> {
friend class river::io::Group;
protected:
uint32_t m_uid; // uniqueNodeID
protected:
/**
* @brief Constructor
*/
Node(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
public:
/**
* @brief Destructor
*/
virtual ~Node();
virtual bool isHarwareNode() {
return false;
};
protected:
mutable std11::mutex m_mutex;
std11::shared_ptr<const ejson::Object> m_config;
protected:
drain::Process m_process;
public:
const drain::IOFormatInterface& getInterfaceFormat() {
if (m_isInput == true) {
return m_process.getOutputConfig();
} else {
return m_process.getInputConfig();
}
}
const drain::IOFormatInterface& getHarwareFormat() {
if (m_isInput == true) {
return m_process.getInputConfig();
} else {
return m_process.getOutputConfig();
}
}
protected:
std11::shared_ptr<drain::VolumeElement> m_volume; //!< if a volume is set it is set here ...
protected:
std::vector<std11::weak_ptr<river::Interface> > m_listAvaillable; //!< List of all interface that exist on this Node
std::vector<std11::shared_ptr<river::Interface> > m_list;
size_t getNumberOfInterface(enum river::modeInterface _interfaceType);
size_t getNumberOfInterfaceAvaillable(enum river::modeInterface _interfaceType);
public:
size_t getNumberOfInterface() {
return m_list.size();
}
public:
void registerAsRemote(const std11::shared_ptr<river::Interface>& _interface);
void interfaceAdd(const std11::shared_ptr<river::Interface>& _interface);
void interfaceRemove(const std11::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() {
return m_isInput;
}
bool isOutput() {
return !m_isInput;
}
protected:
std11::weak_ptr<river::io::Group> m_group;
public:
void setGroup(std11::shared_ptr<river::io::Group> _group) {
m_group = _group;
}
protected:
void startInGroup();
void stopInGroup();
virtual void start() = 0;
virtual void stop() = 0;
public:
const std11::shared_ptr<drain::VolumeElement>& getVolume() {
return m_volume;
}
public:
void volumeChange();
protected:
int32_t newInput(const void* _inputBuffer,
uint32_t _nbChunk,
const std11::chrono::system_clock::time_point& _time);
int32_t newOutput(void* _outputBuffer,
uint32_t _nbChunk,
const std11::chrono::system_clock::time_point& _time);
public:
virtual void generateDot(etk::FSNode& _node);
};
}
}
#endif

View File

@@ -1,63 +0,0 @@
/** @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>
#include <river/CircularBuffer.h>
namespace river {
namespace io {
class Manager;
class NodeAEC : public Node {
protected:
/**
* @brief Constructor
*/
NodeAEC(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
public:
static std11::shared_ptr<NodeAEC> create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
/**
* @brief Destructor
*/
virtual ~NodeAEC();
protected:
virtual void start();
virtual void stop();
std11::shared_ptr<river::Interface> m_interfaceMicrophone;
std11::shared_ptr<river::Interface> m_interfaceFeedBack;
std11::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 void* _data,
const std11::chrono::system_clock::time_point& _time,
size_t _nbChunk,
enum audio::format _format,
uint32_t _frequency,
const std::vector<audio::channel>& _map);
void onDataReceivedFeedBack(const void* _data,
const std11::chrono::system_clock::time_point& _time,
size_t _nbChunk,
enum audio::format _format,
uint32_t _frequency,
const std::vector<audio::channel>& _map);
river::CircularBuffer m_bufferMicrophone;
river::CircularBuffer m_bufferFeedBack;
std11::chrono::nanoseconds m_sampleTime; //!< represent the sample time at the specify frequency.
void process();
void processAEC(void* _dataMic, void* _dataFB, uint32_t _nbChunk, const std11::chrono::system_clock::time_point& _time);
public:
virtual void generateDot(etk::FSNode& _node);
};
}
}
#endif

View File

@@ -1,62 +0,0 @@
/** @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__
#ifdef __AIRTAUDIO_INFERFACE__
#include <river/io/Node.h>
namespace river {
namespace io {
class Manager;
class Group;
class NodeAirTAudio : public Node {
friend class river::io::Group;
protected:
/**
* @brief Constructor
*/
NodeAirTAudio(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
public:
static std11::shared_ptr<NodeAirTAudio> create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
/**
* @brief Destructor
*/
virtual ~NodeAirTAudio();
virtual bool isHarwareNode() {
return true;
};
protected:
airtaudio::Interface m_adac; //!< Real audio interface
airtaudio::DeviceInfo m_info;
unsigned int m_rtaudioFrameSize;
public:
int32_t duplexCallback(const void* _inputBuffer,
const std11::chrono::system_clock::time_point& _timeInput,
void* _outputBuffer,
const std11::chrono::system_clock::time_point& _timeOutput,
uint32_t _nbChunk,
const std::vector<airtaudio::status>& _status);
int32_t recordCallback(const void* _inputBuffer,
const std11::chrono::system_clock::time_point& _timeInput,
uint32_t _nbChunk,
const std::vector<airtaudio::status>& _status);
int32_t playbackCallback(void* _outputBuffer,
const std11::chrono::system_clock::time_point& _timeOutput,
uint32_t _nbChunk,
const std::vector<airtaudio::status>& _status);
protected:
virtual void start();
virtual void stop();
};
}
}
#endif
#endif

View File

@@ -1,68 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __RIVER_IO_NODE_MUXER_H__
#define __RIVER_IO_NODE_MUXER_H__
#include <river/io/Node.h>
#include <river/Interface.h>
#include <river/CircularBuffer.h>
namespace river {
namespace io {
class Manager;
class NodeMuxer : public Node {
protected:
/**
* @brief Constructor
*/
NodeMuxer(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
public:
static std11::shared_ptr<NodeMuxer> create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
/**
* @brief Destructor
*/
virtual ~NodeMuxer();
protected:
virtual void start();
virtual void stop();
std11::shared_ptr<river::Interface> m_interfaceInput1;
std11::shared_ptr<river::Interface> m_interfaceInput2;
std11::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 onDataReceivedInput1(const void* _data,
const std11::chrono::system_clock::time_point& _time,
size_t _nbChunk,
enum audio::format _format,
uint32_t _frequency,
const std::vector<audio::channel>& _map);
void onDataReceivedInput2(const void* _data,
const std11::chrono::system_clock::time_point& _time,
size_t _nbChunk,
enum audio::format _format,
uint32_t _frequency,
const std::vector<audio::channel>& _map);
std::vector<audio::channel> m_mapInput1;
std::vector<audio::channel> m_mapInput2;
river::CircularBuffer m_bufferInput1;
river::CircularBuffer m_bufferInput2;
std11::chrono::nanoseconds m_sampleTime; //!< represent the sample time at the specify frequency.
void process();
void processMuxer(void* _dataMic, void* _dataFB, uint32_t _nbChunk, const std11::chrono::system_clock::time_point& _time);
std::vector<uint8_t> m_data;
public:
virtual void generateDot(etk::FSNode& _node);
private:
void reorder(void* _output, uint32_t _nbChunk, void* _input, const std::vector<audio::channel>& _mapInput);
};
}
}
#endif

View File

@@ -1,52 +0,0 @@
/** @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__
#ifdef __PORTAUDIO_INFERFACE__
#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 std11::shared_ptr<const ejson::Object>& _config);
public:
static std11::shared_ptr<NodePortAudio> create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
/**
* @brief Destructor
*/
virtual ~NodePortAudio();
virtual bool isHarwareNode() {
return true;
};
protected:
PaStream* m_stream;
public:
int32_t duplexCallback(const void* _inputBuffer,
const std11::chrono::system_clock::time_point& _timeInput,
void* _outputBuffer,
const std11::chrono::system_clock::time_point& _timeOutput,
uint32_t _nbChunk,
PaStreamCallbackFlags _status);
protected:
virtual void start();
virtual void stop();
};
}
}
#endif
#endif

View File

@@ -0,0 +1,24 @@
#!/usr/bin/python
import lutin.module as module
import lutin.tools as tools
def get_desc():
return "Read some data"
def create(target):
myModule = module.Module(__file__, 'river-sample-read', 'BINARY')
myModule.add_src_file([
'main.cpp',
])
myModule.add_module_depend(['audio-river', 'etk'])
return myModule

133
sample/read/main.cpp Normal file
View File

@@ -0,0 +1,133 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <audio/river/river.h>
#include <audio/river/Manager.h>
#include <audio/river/Interface.h>
#include <etk/os/FSNode.h>
#include <etk/etk.h>
#include <unistd.h>
static const std::string configurationRiver =
"{\n"
" microphone:{\n"
" io:'input',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" },\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024\n"
" }\n"
"}\n";
void onDataReceived(const void* _data,
const audio::Time& _time,
size_t _nbChunk,
enum audio::format _format,
uint32_t _frequency,
const std::vector<audio::channel>& _map,
etk::FSNode* _outputNode) {
if (_format != audio::format_int16) {
std::cout << "[ERROR] call wrong type ... (need int16_t)" << std::endl;
}
if (_outputNode->fileIsOpen() == false) {
// get the curent power of the signal.
const int16_t* data = static_cast<const int16_t*>(_data);
int64_t value = 0;
for (size_t iii=0; iii<_nbChunk*_map.size(); ++iii) {
value += std::abs(data[iii]);
}
value /= (_nbChunk*_map.size());
std::cout << "Get data ... average=" << int32_t(value) << std::endl;
} else {
// just write data
std::cout << "Get data ... chunks=" << _nbChunk << " time=" << _time << std::endl;
_outputNode->fileWrite(_data, _map.size()*audio::getFormatBytes(_format), _nbChunk);
}
}
int main(int _argc, const char **_argv) {
// the only one init for etk:
etk::init(_argc, _argv);
// local parameter:
std::string configFile;
std::string ioName="microphone";
std::string outputFileName = "";
for (int32_t iii=0; iii<_argc ; ++iii) {
std::string data = _argv[iii];
if ( data == "-h"
|| data == "--help") {
std::cout << "Help : " << std::endl;
std::cout << " --conf=xxx.json Input/output configuration" << std::endl;
std::cout << " --io=xxx name configuration input" << std::endl;
std::cout << " --file=yyy.raw File name to store data" << std::endl;
exit(0);
} else if (etk::start_with(data, "--conf=") == true) {
configFile = std::string(data.begin()+7, data.end());
std::cout << "Select config: " << configFile << std::endl;
} else if (etk::start_with(data, "--io=") == true) {
ioName = std::string(data.begin()+5, data.end());
std::cout << "Select io: " << ioName << std::endl;
} else if (etk::start_with(data, "--file=") == true) {
outputFileName = std::string(data.begin()+7, data.end());
std::cout << "Select output file name: " << outputFileName << std::endl;
}
}
// initialize river interface
if (configFile == "") {
audio::river::initString(configurationRiver);
} else {
audio::river::init(configFile);
}
// Create the River manager for tha application or part of the application.
std11::shared_ptr<audio::river::Manager> manager = audio::river::Manager::create("river_sample_read");
// create interface:
std11::shared_ptr<audio::river::Interface> interface;
//Get the generic input:
interface = manager->createInput(48000,
std::vector<audio::channel>(),
audio::format_int16,
ioName);
if(interface == nullptr) {
std::cout << "nullptr interface" << std::endl;
return -1;
}
etk::FSNode outputNode;
// open output file if needed:
if (outputFileName != "") {
outputNode.setName(outputFileName);
outputNode.fileOpenWrite();
}
// set callback mode ...
interface->setInputCallback(std11::bind(&onDataReceived,
std11::placeholders::_1,
std11::placeholders::_2,
std11::placeholders::_3,
std11::placeholders::_4,
std11::placeholders::_5,
std11::placeholders::_6,
&outputNode));
// start the stream
interface->start();
// wait 10 second ...
sleep(10);
// stop the stream
interface->stop();
// remove interface and manager.
interface.reset();
manager.reset();
// close the output file
if (outputFileName != "") {
outputNode.fileClose();
}
return 0;
}

View File

@@ -0,0 +1,24 @@
#!/usr/bin/python
import lutin.module as module
import lutin.tools as tools
def get_desc():
return "Write some data"
def create(target):
myModule = module.Module(__file__, 'river-sample-write', 'BINARY')
myModule.add_src_file([
'main.cpp',
])
myModule.add_module_depend(['audio-river', 'etk'])
return myModule

99
sample/write/main.cpp Normal file
View File

@@ -0,0 +1,99 @@
/** @file
* @author Edouard DUPIN
* @copyright 2015, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <audio/river/river.h>
#include <audio/river/Manager.h>
#include <audio/river/Interface.h>
#include <etk/etk.h>
#include <unistd.h>
static const std::string configurationRiver =
"{\n"
" speaker:{\n"
" io:'output',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" },\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024,\n"
" volume-name:'MASTER'\n"
" }\n"
"}\n";
void onDataNeeded(void* _data,
const audio::Time& _time,
size_t _nbChunk,
enum audio::format _format,
uint32_t _frequency,
const std::vector<audio::channel>& _map) {
static double phase = 0;
if (_format != audio::format_int16) {
std::cout << "[ERROR] call wrong type ... (need int16_t)" << std::endl;
}
int16_t* data = static_cast<int16_t*>(_data);
double baseCycle = 2.0*M_PI/(double)48000 * (double)550;
for (int32_t iii=0; iii<_nbChunk; iii++) {
for (int32_t jjj=0; jjj<_map.size(); jjj++) {
data[_map.size()*iii+jjj] = cos(phase) * 30000;
}
phase += baseCycle;
if (phase >= 2*M_PI) {
phase -= 2*M_PI;
}
}
}
int main(int _argc, const char **_argv) {
// the only one init for etk:
etk::init(_argc, _argv);
for (int32_t iii=0; iii<_argc ; ++iii) {
std::string data = _argv[iii];
if ( data == "-h"
|| data == "--help") {
std::cout << "Help : " << std::endl;
std::cout << " ./xxx ---" << std::endl;
exit(0);
}
}
// initialize river interface
audio::river::initString(configurationRiver);
// Create the River manager for tha application or part of the application.
std11::shared_ptr<audio::river::Manager> manager = audio::river::Manager::create("river_sample_read");
// create interface:
std11::shared_ptr<audio::river::Interface> interface;
//Get the generic input:
interface = manager->createOutput(48000,
std::vector<audio::channel>(),
audio::format_int16,
"speaker");
if(interface == nullptr) {
std::cout << "nullptr interface" << std::endl;
return -1;
}
// set callback mode ...
interface->setOutputCallback(std11::bind(&onDataNeeded,
std11::placeholders::_1,
std11::placeholders::_2,
std11::placeholders::_3,
std11::placeholders::_4,
std11::placeholders::_5,
std11::placeholders::_6));
// start the stream
interface->start();
// wait 10 second ...
sleep(10);
// stop the stream
interface->stop();
// remove interface and manager.
interface.reset();
manager.reset();
return 0;
}

View File

@@ -0,0 +1,55 @@
cmake_minimum_required(VERSION 2.8.3)
project(audio_river_test)
set(CMAKE_VERBOSE_MAKEFILE ON)
## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
audio_river
rosconsole
)
###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if you package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package()
###########
## Build ##
###########
## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
..
${catkin_INCLUDE_DIRS}
)
## Declare a cpp library
catkin_add_gtest(${PROJECT_NAME}
../debug.cpp
../main.cpp
)
## Specify libraries to link a library or executable target against
target_link_libraries(${PROJECT_NAME}
${catkin_LIBRARIES}
)
#############
## Install ##
#############
## Mark executables and/or libraries for installation
#install(TARGETS ${PROJECT_NAME}
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
#)

13
test/catkin/package.xml Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0"?>
<package>
<name>audio_river_test</name>
<version>0.3.0</version>
<description>Ewol River test</description>
<maintainer email="yui.heero@gmail.com">Edouard DUPIN</maintainer>
<license>Apache-2.0</license>
<build_depend>gtest</build_depend>
<build_depend>audio_river</build_depend>
<buildtool_depend>catkin</buildtool_depend>
<run_depend>audio_river</run_depend>
<run_depend>gtest</run_depend>
</package>

View File

@@ -14,17 +14,9 @@
namespace appl { namespace appl {
int32_t getLogId(); int32_t getLogId();
}; };
// TODO : Review this problem of multiple intanciation of "std::stringbuf sb" #define APPL_BASE(info,data) TK_LOG_BASE(appl::getLogId(),info,data)
#define APPL_BASE(info,data) \
do { \
if (info <= etk::log::getLevel(appl::getLogId())) { \
std::stringbuf sb; \
std::ostream tmpStream(&sb); \
tmpStream << data; \
etk::log::logStream(appl::getLogId(), info, __LINE__, __class__, __func__, tmpStream); \
} \
} while(0)
#define APPL_PRINT(data) APPL_BASE(-1, data)
#define APPL_CRITICAL(data) APPL_BASE(1, data) #define APPL_CRITICAL(data) APPL_BASE(1, data)
#define APPL_ERROR(data) APPL_BASE(2, data) #define APPL_ERROR(data) APPL_BASE(2, data)
#define APPL_WARNING(data) APPL_BASE(3, data) #define APPL_WARNING(data) APPL_BASE(3, data)

View File

@@ -5,9 +5,11 @@
*/ */
#include "debug.h" #include "debug.h"
#include <river/Manager.h> #include <audio/river/river.h>
#include <river/Interface.h> #include <audio/river/Manager.h>
#include <audio/river/Interface.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <etk/etk.h>
#include <etk/os/FSNode.h> #include <etk/os/FSNode.h>
#include <math.h> #include <math.h>
#include <sstream> #include <sstream>
@@ -27,77 +29,20 @@
#undef __class__ #undef __class__
#define __class__ "test" #define __class__ "test"
int main(int _argc, const char** _argv) {
/*
static void threadVolume() {
std11::shared_ptr<river::Manager> manager;
manager = river::Manager::create("testApplication");
std11::shared_ptr<testCallbackVolume> process = std11::make_shared<testCallbackVolume>(manager);
usleep(100000);
process->run();
process.reset();
usleep(500000);
}
TEST(TestALL, testInputCallBackMicClean) {
std11::shared_ptr<river::Manager> manager;
manager = river::Manager::create("testApplication");
std11::thread tmpThread(std11::bind(&threadVolume));
usleep(30000);
APPL_INFO("test input (callback mode)");
std11::shared_ptr<testInCallback> process = std11::make_shared<testInCallback>(manager, "microphone-clean");
process->run();
usleep(100000);
process.reset();
usleep(500000);
tmpThread.join();
}
*/
int main(int argc, char **argv) {
// init Google test : // init Google test :
::testing::InitGoogleTest(&argc, const_cast<char **>(argv)); ::testing::InitGoogleTest(&_argc, const_cast<char **>(_argv));
// the only one init for etk: // the only one init for etk:
etk::log::setLevel(etk::log::logLevelNone); etk::init(_argc, _argv);
for (int32_t iii=0; iii<argc ; ++iii) { for (int32_t iii=0; iii<_argc ; ++iii) {
std::string data = argv[iii]; std::string data = _argv[iii];
if (data == "-l0") { if ( data == "-h"
etk::log::setLevel(etk::log::logLevelNone);
} else if (data == "-l1") {
etk::log::setLevel(etk::log::logLevelCritical);
} else if (data == "-l2") {
etk::log::setLevel(etk::log::logLevelError);
} else if (data == "-l3") {
etk::log::setLevel(etk::log::logLevelWarning);
} else if (data == "-l4") {
etk::log::setLevel(etk::log::logLevelInfo);
} else if (data == "-l5") {
etk::log::setLevel(etk::log::logLevelDebug);
} else if (data == "-l6") {
etk::log::setLevel(etk::log::logLevelVerbose);
} else if ( data == "-h"
|| data == "--help") { || data == "--help") {
APPL_INFO("Help : "); APPL_PRINT("Help : ");
APPL_INFO(" ./xxx [options]"); APPL_PRINT(" ./xxx ---");
APPL_INFO(" -l0: debug None");
APPL_INFO(" -l1: debug Critical");
APPL_INFO(" -l2: debug Error");
APPL_INFO(" -l3: debug Warning");
APPL_INFO(" -l4: debug Info");
APPL_INFO(" -l5: debug Debug");
APPL_INFO(" -l6: debug Verbose");
APPL_INFO(" -h/--help: this help");
exit(0); exit(0);
} }
} }
etk::setArgZero(argv[0]);
etk::initDefaultFolder("exml_test");
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
} }

View File

@@ -12,6 +12,209 @@
namespace river_test_aec { namespace river_test_aec {
class Linker {
private:
std11::shared_ptr<audio::river::Manager> m_manager;
std11::shared_ptr<audio::river::Interface> m_interfaceOut;
std11::shared_ptr<audio::river::Interface> m_interfaceIn;
audio::drain::CircularBuffer m_buffer;
public:
Linker(std11::shared_ptr<audio::river::Manager> _manager, const std::string& _input, const std::string& _output) :
m_manager(_manager) {
//Set stereo output:
std::vector<audio::channel> channelMap;
if (false) { //"speaker" == _output) {
channelMap.push_back(audio::channel_frontCenter);
} else {
channelMap.push_back(audio::channel_frontLeft);
channelMap.push_back(audio::channel_frontRight);
}
m_buffer.setCapacity(std11::chrono::milliseconds(2000), sizeof(int16_t)*channelMap.size(), 48000);
m_interfaceOut = m_manager->createOutput(48000,
channelMap,
audio::format_int16,
_output);
if(m_interfaceOut == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
// set callback mode ...
m_interfaceOut->setOutputCallback(std11::bind(&Linker::onDataNeeded,
this,
std11::placeholders::_1,
std11::placeholders::_2,
std11::placeholders::_3,
std11::placeholders::_4,
std11::placeholders::_5,
std11::placeholders::_6));
m_interfaceOut->addVolumeGroup("FLOW");
if ("speaker" == _output) {
m_interfaceOut->setParameter("volume", "FLOW", "0dB");
}
m_interfaceIn = m_manager->createInput(48000,
channelMap,
audio::format_int16,
_input);
if(m_interfaceIn == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
// set callback mode ...
m_interfaceIn->setInputCallback(std11::bind(&Linker::onDataReceived,
this,
std11::placeholders::_1,
std11::placeholders::_2,
std11::placeholders::_3,
std11::placeholders::_4,
std11::placeholders::_5,
std11::placeholders::_6));
}
void onDataNeeded(void* _data,
const audio::Time& _time,
size_t _nbChunk,
enum audio::format _format,
uint32_t _frequency,
const std::vector<audio::channel>& _map) {
if (_format != audio::format_int16) {
APPL_ERROR("call wrong type ... (need int16_t)");
}
m_buffer.read(_data, _nbChunk);
}
void onDataReceived(const void* _data,
const audio::Time& _time,
size_t _nbChunk,
enum audio::format _format,
uint32_t _frequency,
const std::vector<audio::channel>& _map) {
if (_format != audio::format_int16) {
APPL_ERROR("call wrong type ... (need int16_t)");
}
m_buffer.write(_data, _nbChunk);
}
void start() {
if(m_interfaceIn == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
if(m_interfaceOut == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
m_interfaceOut->start();
m_interfaceIn->start();
}
void stop() {
if(m_interfaceIn == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
if(m_interfaceOut == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
m_manager->generateDotAll("activeProcess.dot");
m_interfaceOut->stop();
m_interfaceIn->stop();
}
};
static const std::string configurationRiver =
"{\n"
" speaker:{\n"
" io:'output',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'hw:0,0',\n"
" timestamp-mode:'trigered',\n"
" },\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024,\n"
" },\n"
" microphone:{\n"
" io:'input',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'hw:0,0',\n"
" timestamp-mode:'trigered',\n"
" },\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024\n"
" },\n"
" speaker-test:{\n"
" io:'output',\n"
" map-on:{\n"
" interface:'alsa',\n"
" name:'hw:2,0',\n"
" timestamp-mode:'trigered',\n"
" },\n"
//" group:'groupSynchro',\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024\n"
" },\n"
" microphone-test:{\n"
" io:'input',\n"
" map-on:{\n"
" interface:'alsa',\n"
" name:'hw:2,0',\n"
" timestamp-mode:'trigered',\n"
" },\n"
//" group:'groupSynchro',\n"
" frequency:0,\n"
" channel-map:['front-center'],\n"
" type:'auto',\n"
" nb-chunk:1024\n"
" },\n"
" # virtual Nodes :\n"
" microphone-clean:{\n"
" io:'aec',\n"
" map-on-microphone:{\n"
" io:'input',\n"
" map-on:'microphone-test'\n"
" },\n"
" map-on-feedback:{\n"
" io:'feedback',\n"
" map-on:'speaker-test',\n"
" },\n"
" frequency:48000,\n"
" channel-map:[\n"
" 'front-left', 'front-right'\n"
//" 'front-center'\n"
" ],\n"
" nb-chunk:1024,\n"
" type:'int16',\n"
" algo:'river-remover',\n"
" algo-mode:'cutter',\n"
" feedback-delay:10000,\n"
" mux-demux-type:'int16'\n"
" }\n"
"}\n";
TEST(TestUser, testAECManually) {
audio::river::initString(configurationRiver);
std11::shared_ptr<audio::river::Manager> manager;
manager = audio::river::Manager::create("testApplication");
std11::shared_ptr<Linker> processLink1 = std11::make_shared<Linker>(manager, "microphone-clean", "speaker");
std11::shared_ptr<Linker> processLink2 = std11::make_shared<Linker>(manager, "microphone", "speaker-test");
processLink1->start();
processLink2->start();
sleep(30);
processLink1->stop();
processLink2->stop();
processLink1.reset();
processLink2.reset();
manager.reset();
audio::river::unInit();
}
}; };
#undef __class__ #undef __class__

View File

@@ -7,7 +7,7 @@
#ifndef __RIVER_TEST_ECHO_DELAY_H__ #ifndef __RIVER_TEST_ECHO_DELAY_H__
#define __RIVER_TEST_ECHO_DELAY_H__ #define __RIVER_TEST_ECHO_DELAY_H__
#include <river/debug.h> #include <audio/river/debug.h>
#undef __class__ #undef __class__
#define __class__ "test_echo_delay" #define __class__ "test_echo_delay"
@@ -15,16 +15,16 @@
namespace river_test_echo_delay { namespace river_test_echo_delay {
class TestClass { class TestClass {
private: private:
std11::shared_ptr<river::Manager> m_manager; std11::shared_ptr<audio::river::Manager> m_manager;
std11::shared_ptr<river::Interface> m_interfaceOut; std11::shared_ptr<audio::river::Interface> m_interfaceOut;
std11::shared_ptr<river::Interface> m_interfaceIn; std11::shared_ptr<audio::river::Interface> m_interfaceIn;
std11::shared_ptr<river::Interface> m_interfaceFB; std11::shared_ptr<audio::river::Interface> m_interfaceFB;
double m_phase; double m_phase;
double m_freq; double m_freq;
int32_t m_nextSampleCount; int32_t m_nextSampleCount;
std11::chrono::milliseconds m_delayBetweenEvent; std11::chrono::milliseconds m_delayBetweenEvent;
std11::chrono::system_clock::time_point m_nextTick; audio::Time m_nextTick;
std11::chrono::system_clock::time_point m_currentTick; audio::Time m_currentTick;
int32_t m_stateFB; int32_t m_stateFB;
int32_t m_stateMic; int32_t m_stateMic;
std::vector<uint64_t> m_delayListMic; std::vector<uint64_t> m_delayListMic;
@@ -33,7 +33,7 @@ namespace river_test_echo_delay {
int16_t m_volumeInputMin; int16_t m_volumeInputMin;
float m_gain; float m_gain;
public: public:
TestClass(std11::shared_ptr<river::Manager> _manager) : TestClass(std11::shared_ptr<audio::river::Manager> _manager) :
m_manager(_manager), m_manager(_manager),
m_phase(0), m_phase(0),
m_freq(400), m_freq(400),
@@ -48,8 +48,11 @@ namespace river_test_echo_delay {
m_interfaceOut = m_manager->createOutput(48000, m_interfaceOut = m_manager->createOutput(48000,
channelMap, channelMap,
audio::format_int16, audio::format_int16,
"speaker", "speaker");
"delayTestOut"); if(m_interfaceOut == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
// set callback mode ... // set callback mode ...
m_interfaceOut->setOutputCallback(std11::bind(&TestClass::onDataNeeded, m_interfaceOut->setOutputCallback(std11::bind(&TestClass::onDataNeeded,
this, this,
@@ -65,8 +68,11 @@ namespace river_test_echo_delay {
m_interfaceIn = m_manager->createInput(48000, m_interfaceIn = m_manager->createInput(48000,
channelMap, channelMap,
audio::format_int16, audio::format_int16,
"microphone", "microphone");
"delayTestIn"); if(m_interfaceIn == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
// set callback mode ... // set callback mode ...
m_interfaceIn->setInputCallback(std11::bind(&TestClass::onDataReceived, m_interfaceIn->setInputCallback(std11::bind(&TestClass::onDataReceived,
this, this,
@@ -77,11 +83,14 @@ namespace river_test_echo_delay {
std11::placeholders::_5, std11::placeholders::_5,
std11::placeholders::_6)); std11::placeholders::_6));
m_interfaceFB = m_manager->createInput(48000, m_interfaceFB = m_manager->createFeedback(48000,
channelMap, channelMap,
audio::format_int16, audio::format_int16,
"feedback", "speaker");
"delayTestFB"); if(m_interfaceFB == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
// set callback mode ... // set callback mode ...
m_interfaceFB->setInputCallback(std11::bind(&TestClass::onDataReceivedFeedBack, m_interfaceFB->setInputCallback(std11::bind(&TestClass::onDataReceivedFeedBack,
this, this,
@@ -95,7 +104,7 @@ namespace river_test_echo_delay {
m_manager->generateDotAll("activeProcess.dot"); m_manager->generateDotAll("activeProcess.dot");
} }
void onDataNeeded(void* _data, void onDataNeeded(void* _data,
const std11::chrono::system_clock::time_point& _time, const audio::Time& _time,
size_t _nbChunk, size_t _nbChunk,
enum audio::format _format, enum audio::format _format,
uint32_t _frequency, uint32_t _frequency,
@@ -113,7 +122,7 @@ namespace river_test_echo_delay {
} }
} }
} else { } else {
if (_time == std11::chrono::system_clock::time_point()) { if (_time == audio::Time()) {
for (int32_t iii=0; iii<_nbChunk; iii++) { for (int32_t iii=0; iii<_nbChunk; iii++) {
for (int32_t jjj=0; jjj<_map.size(); jjj++) { for (int32_t jjj=0; jjj<_map.size(); jjj++) {
data[_map.size()*iii+jjj] = 0; data[_map.size()*iii+jjj] = 0;
@@ -121,7 +130,7 @@ namespace river_test_echo_delay {
} }
return; return;
} }
if (m_nextTick == std11::chrono::system_clock::time_point()) { if (m_nextTick == audio::Time()) {
m_nextTick = _time + m_delayBetweenEvent; m_nextTick = _time + m_delayBetweenEvent;
m_nextSampleCount = m_delayBetweenEvent.count()*int64_t(_frequency)/1000; m_nextSampleCount = m_delayBetweenEvent.count()*int64_t(_frequency)/1000;
m_phase = -1; m_phase = -1;
@@ -166,19 +175,19 @@ namespace river_test_echo_delay {
} }
} }
} }
std11::chrono::system_clock::time_point getInterpolateTime(std11::chrono::system_clock::time_point _time, int32_t _pos, int16_t _val1, int16_t _val2, uint32_t _frequency) { audio::Time getInterpolateTime(audio::Time _time, int32_t _pos, int16_t _val1, int16_t _val2, uint32_t _frequency) {
if (_val1 == 0) { if (_val1 == 0) {
return _time + std11::chrono::nanoseconds(int64_t(_pos)*1000000000LL/int64_t(_frequency)); return _time + audio::Duration(0, int64_t(_pos)*1000000000LL/int64_t(_frequency));
} else if (_val2 == 0) { } else if (_val2 == 0) {
return _time + std11::chrono::nanoseconds(int64_t(_pos+1)*1000000000LL/int64_t(_frequency)); return _time + audio::Duration(0, int64_t(_pos+1)*1000000000LL/int64_t(_frequency));
} }
double xxx = double(-_val1) / double(_val2 - _val1); double xxx = double(-_val1) / double(_val2 - _val1);
APPL_VERBOSE("deltaPos:" << xxx); APPL_VERBOSE("deltaPos:" << xxx);
return _time + std11::chrono::nanoseconds(int64_t((double(_pos)+xxx)*1000000000.0)/int64_t(_frequency)); return _time + audio::Duration(0, int64_t((double(_pos)+xxx)*1000000000.0)/int64_t(_frequency));
} }
void onDataReceivedFeedBack(const void* _data, void onDataReceivedFeedBack(const void* _data,
const std11::chrono::system_clock::time_point& _time, const audio::Time& _time,
size_t _nbChunk, size_t _nbChunk,
enum audio::format _format, enum audio::format _format,
uint32_t _frequency, uint32_t _frequency,
@@ -208,7 +217,7 @@ namespace river_test_echo_delay {
if (data[iii*_map.size() + jjj] <= 0) { if (data[iii*_map.size() + jjj] <= 0) {
// detect inversion of signe ... // detect inversion of signe ...
m_stateFB = 3; m_stateFB = 3;
std11::chrono::system_clock::time_point time = getInterpolateTime(_time, iii-1, data[(iii-1)*_map.size() + jjj], data[iii*_map.size() + jjj], _frequency); audio::Time time = getInterpolateTime(_time, iii-1, data[(iii-1)*_map.size() + jjj], data[iii*_map.size() + jjj], _frequency);
APPL_VERBOSE("FB: 1 position -1: " << iii-1 << " " << data[(iii-1)*_map.size() + jjj]); APPL_VERBOSE("FB: 1 position -1: " << iii-1 << " " << data[(iii-1)*_map.size() + jjj]);
APPL_VERBOSE("FB: 1 position 0: " << iii << " " << data[iii*_map.size() + jjj]); APPL_VERBOSE("FB: 1 position 0: " << iii << " " << data[iii*_map.size() + jjj]);
@@ -219,7 +228,7 @@ namespace river_test_echo_delay {
if (data[iii*_map.size() + jjj] >= 0) { if (data[iii*_map.size() + jjj] >= 0) {
// detect inversion of signe ... // detect inversion of signe ...
m_stateFB = 3; m_stateFB = 3;
std11::chrono::system_clock::time_point time = getInterpolateTime(_time, iii-1, data[(iii-1)*_map.size() + jjj], data[iii*_map.size() + jjj], _frequency); audio::Time time = getInterpolateTime(_time, iii-1, data[(iii-1)*_map.size() + jjj], data[iii*_map.size() + jjj], _frequency);
APPL_VERBOSE("FB: 2 position -1: " << iii-1 << " " << data[(iii-1)*_map.size() + jjj]); APPL_VERBOSE("FB: 2 position -1: " << iii-1 << " " << data[(iii-1)*_map.size() + jjj]);
APPL_VERBOSE("FB: 2 position 0: " << iii << " " << data[iii*_map.size() + jjj]); APPL_VERBOSE("FB: 2 position 0: " << iii << " " << data[iii*_map.size() + jjj]);
APPL_WARNING("FB: 2 time detected: " << time << " delay = " << float((time-m_currentTick).count())/1000.0f << "µs"); APPL_WARNING("FB: 2 time detected: " << time << " delay = " << float((time-m_currentTick).count())/1000.0f << "µs");
@@ -233,7 +242,7 @@ namespace river_test_echo_delay {
} }
} }
void onDataReceived(const void* _data, void onDataReceived(const void* _data,
const std11::chrono::system_clock::time_point& _time, const audio::Time& _time,
size_t _nbChunk, size_t _nbChunk,
enum audio::format _format, enum audio::format _format,
uint32_t _frequency, uint32_t _frequency,
@@ -310,10 +319,10 @@ namespace river_test_echo_delay {
if (data[iii*_map.size() + jjj] <= 0) { if (data[iii*_map.size() + jjj] <= 0) {
// detect inversion of signe ... // detect inversion of signe ...
m_stateMic = 3; m_stateMic = 3;
std11::chrono::system_clock::time_point time = getInterpolateTime(_time, iii-1, data[(iii-1)*_map.size() + jjj], data[iii*_map.size() + jjj], _frequency); audio::Time time = getInterpolateTime(_time, iii-1, data[(iii-1)*_map.size() + jjj], data[iii*_map.size() + jjj], _frequency);
APPL_VERBOSE("MIC: 1 position -1: " << iii-1 << " " << data[(iii-1)*_map.size() + jjj]); APPL_VERBOSE("MIC: 1 position -1: " << iii-1 << " " << data[(iii-1)*_map.size() + jjj]);
APPL_VERBOSE("MIC: 1 position 0: " << iii << " " << data[iii*_map.size() + jjj]); APPL_VERBOSE("MIC: 1 position 0: " << iii << " " << data[iii*_map.size() + jjj]);
std11::chrono::nanoseconds delay = time-m_currentTick; audio::Duration delay = time-m_currentTick;
int32_t sampleDalay = (delay.count()*_frequency)/1000000000LL; int32_t sampleDalay = (delay.count()*_frequency)/1000000000LL;
APPL_WARNING("MIC: 1 time detected: " << time << " delay = " << float(delay.count())/1000.0f << "µs samples=" << sampleDalay); APPL_WARNING("MIC: 1 time detected: " << time << " delay = " << float(delay.count())/1000.0f << "µs samples=" << sampleDalay);
m_delayListMic.push_back(delay.count()); m_delayListMic.push_back(delay.count());
@@ -323,10 +332,10 @@ namespace river_test_echo_delay {
if (data[iii*_map.size() + jjj] >= 0) { if (data[iii*_map.size() + jjj] >= 0) {
// detect inversion of signe ... // detect inversion of signe ...
m_stateMic = 3; m_stateMic = 3;
std11::chrono::system_clock::time_point time = getInterpolateTime(_time, iii-1, data[(iii-1)*_map.size() + jjj], data[iii*_map.size() + jjj], _frequency); audio::Time time = getInterpolateTime(_time, iii-1, data[(iii-1)*_map.size() + jjj], data[iii*_map.size() + jjj], _frequency);
APPL_VERBOSE("MIC: 2 position -1: " << iii-1 << " " << data[(iii-1)*_map.size() + jjj]); APPL_VERBOSE("MIC: 2 position -1: " << iii-1 << " " << data[(iii-1)*_map.size() + jjj]);
APPL_VERBOSE("MIC: 2 position 0: " << iii << " " << data[iii*_map.size() + jjj]); APPL_VERBOSE("MIC: 2 position 0: " << iii << " " << data[iii*_map.size() + jjj]);
std11::chrono::nanoseconds delay = time-m_currentTick; audio::Duration delay = time-m_currentTick;
int32_t sampleDalay = (delay.count()*_frequency)/1000000000LL; int32_t sampleDalay = (delay.count()*_frequency)/1000000000LL;
APPL_WARNING("MIC: 2 time detected: " << time << " delay = " << float(delay.count())/1000.0f << "µs samples=" << sampleDalay); APPL_WARNING("MIC: 2 time detected: " << time << " delay = " << float(delay.count())/1000.0f << "µs samples=" << sampleDalay);
m_delayListMic.push_back(delay.count()); m_delayListMic.push_back(delay.count());
@@ -340,6 +349,18 @@ namespace river_test_echo_delay {
} }
} }
void run() { void run() {
if(m_interfaceIn == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
if(m_interfaceOut == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
if(m_interfaceFB == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
m_interfaceOut->start(); m_interfaceOut->start();
m_interfaceIn->start(); m_interfaceIn->start();
//m_interfaceFB->start(); //m_interfaceFB->start();
@@ -363,13 +384,47 @@ namespace river_test_echo_delay {
} }
}; };
static const std::string configurationRiver =
"{\n"
" speaker:{\n"
" io:'output',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
//" name:'hw:0,0',\n"
" timestamp-mode:'trigered',\n"
" },\n"
//" group:'groupSynchro',\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024,\n"
" },\n"
" microphone:{\n"
" io:'input',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
//" name:'hw:0,0',\n"
" timestamp-mode:'trigered',\n"
" },\n"
//" group:'groupSynchro',\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024\n"
" }\n"
"}\n";
TEST(TestTime, testDelay) { TEST(TestTime, testDelay) {
std11::shared_ptr<river::Manager> manager; audio::river::initString(configurationRiver);
manager = river::Manager::create("testApplication"); std11::shared_ptr<audio::river::Manager> manager;
manager = audio::river::Manager::create("testApplication");
std11::shared_ptr<TestClass> process = std11::make_shared<TestClass>(manager); std11::shared_ptr<TestClass> process = std11::make_shared<TestClass>(manager);
process->run(); process->run();
process.reset(); process.reset();
usleep(500000); usleep(500000);
audio::river::unInit();
} }
}; };

View File

@@ -11,17 +11,32 @@
#define __class__ "test_format" #define __class__ "test_format"
namespace river_test_format { namespace river_test_format {
static const std::string configurationRiver =
"{\n"
" speaker:{\n"
" io:'output',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" },\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024,\n"
" volume-name:'MASTER'\n"
" }\n"
"}\n";
class testOutCallbackType { class testOutCallbackType {
private: private:
std11::shared_ptr<river::Manager> m_manager; std11::shared_ptr<audio::river::Manager> m_manager;
std11::shared_ptr<river::Interface> m_interface; std11::shared_ptr<audio::river::Interface> m_interface;
double m_phase; double m_phase;
float m_freq; float m_freq;
int32_t m_nbChannels; int32_t m_nbChannels;
float m_generateFreq; float m_generateFreq;
public: public:
testOutCallbackType(const std11::shared_ptr<river::Manager>& _manager, testOutCallbackType(const std11::shared_ptr<audio::river::Manager>& _manager,
float _freq=48000.0f, float _freq=48000.0f,
int32_t _nbChannels=2, int32_t _nbChannels=2,
audio::format _format=audio::format_int16) : audio::format _format=audio::format_int16) :
@@ -51,6 +66,10 @@ namespace river_test_format {
_format, _format,
"speaker", "speaker",
"WriteModeCallbackType"); "WriteModeCallbackType");
if(m_interface == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
// set callback mode ... // set callback mode ...
m_interface->setOutputCallback(std11::bind(&testOutCallbackType::onDataNeeded, m_interface->setOutputCallback(std11::bind(&testOutCallbackType::onDataNeeded,
this, this,
@@ -62,7 +81,7 @@ namespace river_test_format {
std11::placeholders::_6)); std11::placeholders::_6));
} }
void onDataNeeded(void* _data, void onDataNeeded(void* _data,
const std11::chrono::system_clock::time_point& _time, const audio::Time& _time,
size_t _nbChunk, size_t _nbChunk,
enum audio::format _format, enum audio::format _format,
uint32_t _frequency, uint32_t _frequency,
@@ -116,27 +135,29 @@ namespace river_test_format {
} }
} }
void run() { void run() {
if (m_interface != nullptr) { if(m_interface == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
m_interface->start(); m_interface->start();
// wait 2 second ... // wait 2 second ...
usleep(1000000); usleep(1000000);
m_interface->stop(); m_interface->stop();
usleep(100000); usleep(100000);
} else {
APPL_ERROR("Can not create interface !!!");
}
} }
}; };
class testResampling : public ::testing::TestWithParam<float> {}; class testResampling : public ::testing::TestWithParam<float> {};
TEST_P(testResampling, base) { TEST_P(testResampling, base) {
std11::shared_ptr<river::Manager> manager; audio::river::initString(configurationRiver);
manager = river::Manager::create("testApplication"); std11::shared_ptr<audio::river::Manager> manager;
manager = audio::river::Manager::create("testApplication");
std11::shared_ptr<testOutCallbackType> process = std11::make_shared<testOutCallbackType>(manager, GetParam(), 2, audio::format_int16); std11::shared_ptr<testOutCallbackType> process = std11::make_shared<testOutCallbackType>(manager, GetParam(), 2, audio::format_int16);
process->run(); process->run();
process.reset(); process.reset();
usleep(500000); usleep(500000);
audio::river::unInit();
} }
INSTANTIATE_TEST_CASE_P(InstantiationName, INSTANTIATE_TEST_CASE_P(InstantiationName,
@@ -146,12 +167,14 @@ namespace river_test_format {
class testFormat : public ::testing::TestWithParam<audio::format> {}; class testFormat : public ::testing::TestWithParam<audio::format> {};
TEST_P(testFormat, base) { TEST_P(testFormat, base) {
std11::shared_ptr<river::Manager> manager; audio::river::initString(configurationRiver);
manager = river::Manager::create("testApplication"); std11::shared_ptr<audio::river::Manager> manager;
manager = audio::river::Manager::create("testApplication");
std11::shared_ptr<testOutCallbackType> process = std11::make_shared<testOutCallbackType>(manager, 48000, 2, GetParam()); std11::shared_ptr<testOutCallbackType> process = std11::make_shared<testOutCallbackType>(manager, 48000, 2, GetParam());
process->run(); process->run();
process.reset(); process.reset();
usleep(500000); usleep(500000);
audio::river::unInit();
} }
INSTANTIATE_TEST_CASE_P(InstantiationName, INSTANTIATE_TEST_CASE_P(InstantiationName,
testFormat, testFormat,
@@ -160,12 +183,14 @@ namespace river_test_format {
class testChannels : public ::testing::TestWithParam<int32_t> {}; class testChannels : public ::testing::TestWithParam<int32_t> {};
TEST_P(testChannels, base) { TEST_P(testChannels, base) {
std11::shared_ptr<river::Manager> manager; audio::river::initString(configurationRiver);
manager = river::Manager::create("testApplication"); std11::shared_ptr<audio::river::Manager> manager;
manager = audio::river::Manager::create("testApplication");
std11::shared_ptr<testOutCallbackType> process = std11::make_shared<testOutCallbackType>(manager, 48000, GetParam(), audio::format_int16); std11::shared_ptr<testOutCallbackType> process = std11::make_shared<testOutCallbackType>(manager, 48000, GetParam(), audio::format_int16);
process->run(); process->run();
process.reset(); process.reset();
usleep(500000); usleep(500000);
audio::river::unInit();
} }
INSTANTIATE_TEST_CASE_P(InstantiationName, INSTANTIATE_TEST_CASE_P(InstantiationName,
testChannels, testChannels,
@@ -173,8 +198,9 @@ namespace river_test_format {
TEST(TestALL, testChannelsFormatResampling) { TEST(TestALL, testChannelsFormatResampling) {
std11::shared_ptr<river::Manager> manager; audio::river::initString(configurationRiver);
manager = river::Manager::create("testApplication"); std11::shared_ptr<audio::river::Manager> manager;
manager = audio::river::Manager::create("testApplication");
APPL_INFO("test convert flaot to output (callback mode)"); APPL_INFO("test convert flaot to output (callback mode)");
std::vector<float> listFreq; std::vector<float> listFreq;
listFreq.push_back(4000); listFreq.push_back(4000);
@@ -209,6 +235,7 @@ namespace river_test_format {
} }
} }
} }
audio::river::unInit();
} }

View File

@@ -7,7 +7,7 @@
#ifndef __RIVER_TEST_MUXER_H__ #ifndef __RIVER_TEST_MUXER_H__
#define __RIVER_TEST_MUXER_H__ #define __RIVER_TEST_MUXER_H__
#include <river/debug.h> #include <audio/river/debug.h>
#undef __class__ #undef __class__
#define __class__ "test_muxer" #define __class__ "test_muxer"
@@ -15,12 +15,12 @@
namespace river_test_muxer { namespace river_test_muxer {
class TestClass { class TestClass {
private: private:
std11::shared_ptr<river::Manager> m_manager; std11::shared_ptr<audio::river::Manager> m_manager;
std11::shared_ptr<river::Interface> m_interfaceIn; std11::shared_ptr<audio::river::Interface> m_interfaceIn;
std11::shared_ptr<river::Interface> m_interfaceOut; std11::shared_ptr<audio::river::Interface> m_interfaceOut;
double m_phase; double m_phase;
public: public:
TestClass(std11::shared_ptr<river::Manager> _manager) : TestClass(std11::shared_ptr<audio::river::Manager> _manager) :
m_manager(_manager), m_manager(_manager),
m_phase(0) { m_phase(0) {
std::vector<audio::channel> channelMap; std::vector<audio::channel> channelMap;
@@ -29,8 +29,11 @@ namespace river_test_muxer {
m_interfaceOut = m_manager->createOutput(48000, m_interfaceOut = m_manager->createOutput(48000,
channelMap, channelMap,
audio::format_int16, audio::format_int16,
"speaker", "speaker");
"MuxerTestOut"); if(m_interfaceOut == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
// set callback mode ... // set callback mode ...
m_interfaceOut->setOutputCallback(std11::bind(&TestClass::onDataNeeded, m_interfaceOut->setOutputCallback(std11::bind(&TestClass::onDataNeeded,
this, this,
@@ -47,8 +50,11 @@ namespace river_test_muxer {
m_interfaceIn = m_manager->createInput(48000, m_interfaceIn = m_manager->createInput(48000,
std::vector<audio::channel>(), std::vector<audio::channel>(),
audio::format_int16, audio::format_int16,
"microphone-muxed", "microphone-muxed");
"microphone-muxed-local-name"); if(m_interfaceIn == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
// set callback mode ... // set callback mode ...
m_interfaceIn->setInputCallback(std11::bind(&TestClass::onDataReceived, m_interfaceIn->setInputCallback(std11::bind(&TestClass::onDataReceived,
this, this,
@@ -62,7 +68,7 @@ namespace river_test_muxer {
} }
void onDataNeeded(void* _data, void onDataNeeded(void* _data,
const std11::chrono::system_clock::time_point& _time, const audio::Time& _time,
size_t _nbChunk, size_t _nbChunk,
enum audio::format _format, enum audio::format _format,
uint32_t _frequency, uint32_t _frequency,
@@ -80,7 +86,7 @@ namespace river_test_muxer {
} }
} }
void onDataReceived(const void* _data, void onDataReceived(const void* _data,
const std11::chrono::system_clock::time_point& _time, const audio::Time& _time,
size_t _nbChunk, size_t _nbChunk,
enum audio::format _format, enum audio::format _format,
uint32_t _frequency, uint32_t _frequency,
@@ -92,6 +98,14 @@ namespace river_test_muxer {
APPL_ERROR("Receive data ... " << _nbChunk << " map=" << _map); APPL_ERROR("Receive data ... " << _nbChunk << " map=" << _map);
} }
void run() { void run() {
if(m_interfaceIn == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
if(m_interfaceOut == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
m_interfaceOut->start(); m_interfaceOut->start();
m_interfaceIn->start(); m_interfaceIn->start();
usleep(10000000); usleep(10000000);
@@ -100,13 +114,67 @@ namespace river_test_muxer {
} }
}; };
static const std::string configurationRiver =
"{\n"
" speaker:{\n"
" io:'output',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" timestamp-mode:'trigered',\n"
" },\n"
" group:'groupSynchro',\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024,\n"
" },\n"
" microphone:{\n"
" io:'input',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" timestamp-mode:'trigered',\n"
" },\n"
" group:'groupSynchro',\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024\n"
" },\n"
" microphone-muxed:{\n"
" io:'muxer',\n"
" # connect in input mode\n"
" map-on-input-1:{\n"
" # generic virtual definition\n"
" io:'input',\n"
" map-on:'microphone',\n"
" },\n"
" # connect in feedback mode\n"
" map-on-input-2:{\n"
" io:'feedback',\n"
" map-on:'speaker',\n"
" },\n"
" input-2-remap:['rear-left', 'rear-right'],\n"
" #classical format configuration:\n"
" frequency:48000,\n"
" channel-map:[\n"
" 'front-left', 'front-right', 'rear-left', 'rear-right'\n"
" ],\n"
" type:'int16',\n"
" mux-demux-type:'int16',\n"
" }\n"
"}\n";
TEST(TestMuxer, testMuxing) { TEST(TestMuxer, testMuxing) {
std11::shared_ptr<river::Manager> manager; audio::river::initString(configurationRiver);
manager = river::Manager::create("testApplication"); std11::shared_ptr<audio::river::Manager> manager;
manager = audio::river::Manager::create("testApplication");
std11::shared_ptr<TestClass> process = std11::make_shared<TestClass>(manager); std11::shared_ptr<TestClass> process = std11::make_shared<TestClass>(manager);
process->run(); process->run();
process.reset(); process.reset();
usleep(500000); usleep(500000);
audio::river::unInit();
} }
}; };

View File

@@ -13,12 +13,12 @@
namespace river_test_playback_callback { namespace river_test_playback_callback {
class testOutCallback { class testOutCallback {
private: public:
std11::shared_ptr<river::Manager> m_manager; std11::shared_ptr<audio::river::Manager> m_manager;
std11::shared_ptr<river::Interface> m_interface; std11::shared_ptr<audio::river::Interface> m_interface;
double m_phase; double m_phase;
public: public:
testOutCallback(std11::shared_ptr<river::Manager> _manager, const std::string& _io="speaker") : testOutCallback(std11::shared_ptr<audio::river::Manager> _manager, const std::string& _io="speaker") :
m_manager(_manager), m_manager(_manager),
m_phase(0) { m_phase(0) {
//Set stereo output: //Set stereo output:
@@ -28,8 +28,11 @@ namespace river_test_playback_callback {
m_interface = m_manager->createOutput(48000, m_interface = m_manager->createOutput(48000,
channelMap, channelMap,
audio::format_int16, audio::format_int16,
_io, _io);
"WriteModeCallback"); if(m_interface == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
// set callback mode ... // set callback mode ...
m_interface->setOutputCallback(std11::bind(&testOutCallback::onDataNeeded, m_interface->setOutputCallback(std11::bind(&testOutCallback::onDataNeeded,
this, this,
@@ -41,7 +44,7 @@ namespace river_test_playback_callback {
std11::placeholders::_6)); std11::placeholders::_6));
} }
void onDataNeeded(void* _data, void onDataNeeded(void* _data,
const std11::chrono::system_clock::time_point& _time, const audio::Time& _time,
size_t _nbChunk, size_t _nbChunk,
enum audio::format _format, enum audio::format _format,
uint32_t _frequency, uint32_t _frequency,
@@ -62,6 +65,10 @@ namespace river_test_playback_callback {
} }
} }
void run() { void run() {
if(m_interface == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
m_interface->start(); m_interface->start();
// wait 2 second ... // wait 2 second ...
usleep(2000000); usleep(2000000);
@@ -69,37 +76,60 @@ namespace river_test_playback_callback {
} }
}; };
static const std::string configurationRiver =
"{\n"
" speaker:{\n"
" io:'output',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" },\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024,\n"
" volume-name:'MASTER'\n"
" }\n"
"}\n";
TEST(TestALL, testOutputCallBack) { TEST(TestALL, testOutputCallBack) {
std11::shared_ptr<river::Manager> manager; audio::river::initString(configurationRiver);
manager = river::Manager::create("testApplication"); std11::shared_ptr<audio::river::Manager> manager;
manager = audio::river::Manager::create("testApplication");
APPL_INFO("test output (callback mode)"); APPL_INFO("test output (callback mode)");
std11::shared_ptr<testOutCallback> process = std11::make_shared<testOutCallback>(manager, "speaker"); std11::shared_ptr<testOutCallback> process = std11::make_shared<testOutCallback>(manager, "speaker");
ASSERT_NE(process, nullptr);
process->run(); process->run();
process.reset(); process.reset();
usleep(500000); usleep(500000);
audio::river::unInit();
} }
TEST(TestALL, testOutputCallBackPulse) { TEST(TestALL, testOutputCallBackPulse) {
std11::shared_ptr<river::Manager> manager; audio::river::initString(configurationRiver);
manager = river::Manager::create("testApplication"); std11::shared_ptr<audio::river::Manager> manager;
manager = audio::river::Manager::create("testApplication");
APPL_INFO("test output (callback mode)"); APPL_INFO("test output (callback mode)");
std11::shared_ptr<testOutCallback> process = std11::make_shared<testOutCallback>(manager, "speaker-pulse"); std11::shared_ptr<testOutCallback> process = std11::make_shared<testOutCallback>(manager, "speaker-pulse");
process->run(); process->run();
process.reset(); process.reset();
usleep(500000); usleep(500000);
audio::river::unInit();
} }
TEST(TestALL, testOutputCallBackJack) { TEST(TestALL, testOutputCallBackJack) {
std11::shared_ptr<river::Manager> manager; audio::river::initString(configurationRiver);
manager = river::Manager::create("testApplication"); std11::shared_ptr<audio::river::Manager> manager;
manager = audio::river::Manager::create("testApplication");
APPL_INFO("test output (callback mode)"); APPL_INFO("test output (callback mode)");
std11::shared_ptr<testOutCallback> process = std11::make_shared<testOutCallback>(manager, "speaker-jack"); std11::shared_ptr<testOutCallback> process = std11::make_shared<testOutCallback>(manager, "speaker-jack");
process->run(); process->run();
process.reset(); process.reset();
usleep(500000); usleep(500000);
audio::river::unInit();
} }

View File

@@ -11,13 +11,29 @@
#define __class__ "test_playback_write" #define __class__ "test_playback_write"
namespace river_test_playback_write { namespace river_test_playback_write {
static const std::string configurationRiver =
"{\n"
" speaker:{\n"
" io:'output',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" },\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024,\n"
" volume-name:'MASTER'\n"
" }\n"
"}\n";
class testOutWrite { class testOutWrite {
private:
std::vector<audio::channel> m_channelMap;
std11::shared_ptr<river::Manager> m_manager;
std11::shared_ptr<river::Interface> m_interface;
public: public:
testOutWrite(std11::shared_ptr<river::Manager> _manager) : std::vector<audio::channel> m_channelMap;
std11::shared_ptr<audio::river::Manager> m_manager;
std11::shared_ptr<audio::river::Interface> m_interface;
public:
testOutWrite(std11::shared_ptr<audio::river::Manager> _manager) :
m_manager(_manager) { m_manager(_manager) {
//Set stereo output: //Set stereo output:
m_channelMap.push_back(audio::channel_frontLeft); m_channelMap.push_back(audio::channel_frontLeft);
@@ -25,11 +41,18 @@ namespace river_test_playback_write {
m_interface = m_manager->createOutput(48000, m_interface = m_manager->createOutput(48000,
m_channelMap, m_channelMap,
audio::format_int16, audio::format_int16,
"speaker", "speaker");
"WriteMode"); if(m_interface == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
m_interface->setReadwrite(); m_interface->setReadwrite();
} }
void run() { void run() {
if(m_interface == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
double phase=0; double phase=0;
std::vector<int16_t> data; std::vector<int16_t> data;
data.resize(1024*m_channelMap.size()); data.resize(1024*m_channelMap.size());
@@ -67,23 +90,25 @@ namespace river_test_playback_write {
}; };
TEST(TestALL, testOutputWrite) { TEST(TestALL, testOutputWrite) {
std11::shared_ptr<river::Manager> manager; audio::river::initString(configurationRiver);
manager = river::Manager::create("testApplication"); std11::shared_ptr<audio::river::Manager> manager;
manager = audio::river::Manager::create("testApplication");
APPL_INFO("test output (write mode)"); APPL_INFO("test output (write mode)");
std11::shared_ptr<testOutWrite> process = std11::make_shared<testOutWrite>(manager); std11::shared_ptr<testOutWrite> process = std11::make_shared<testOutWrite>(manager);
process->run(); process->run();
process.reset(); process.reset();
usleep(500000); usleep(500000);
audio::river::unInit();
} }
class testOutWriteCallback { class testOutWriteCallback {
private: public:
std11::shared_ptr<river::Manager> m_manager; std11::shared_ptr<audio::river::Manager> m_manager;
std11::shared_ptr<river::Interface> m_interface; std11::shared_ptr<audio::river::Interface> m_interface;
double m_phase; double m_phase;
public: public:
testOutWriteCallback(std11::shared_ptr<river::Manager> _manager) : testOutWriteCallback(std11::shared_ptr<audio::river::Manager> _manager) :
m_manager(_manager), m_manager(_manager),
m_phase(0) { m_phase(0) {
std::vector<audio::channel> channelMap; std::vector<audio::channel> channelMap;
@@ -93,8 +118,11 @@ namespace river_test_playback_write {
m_interface = m_manager->createOutput(48000, m_interface = m_manager->createOutput(48000,
channelMap, channelMap,
audio::format_int16, audio::format_int16,
"speaker", "speaker");
"WriteMode+Callback"); if(m_interface == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
m_interface->setReadwrite(); m_interface->setReadwrite();
m_interface->setWriteCallback(std11::bind(&testOutWriteCallback::onDataNeeded, m_interface->setWriteCallback(std11::bind(&testOutWriteCallback::onDataNeeded,
this, this,
@@ -104,7 +132,7 @@ namespace river_test_playback_write {
std11::placeholders::_4, std11::placeholders::_4,
std11::placeholders::_5)); std11::placeholders::_5));
} }
void onDataNeeded(const std11::chrono::system_clock::time_point& _time, void onDataNeeded(const audio::Time& _time,
size_t _nbChunk, size_t _nbChunk,
enum audio::format _format, enum audio::format _format,
uint32_t _frequency, uint32_t _frequency,
@@ -128,6 +156,10 @@ namespace river_test_playback_write {
m_interface->write(&data[0], data.size()/_map.size()); m_interface->write(&data[0], data.size()/_map.size());
} }
void run() { void run() {
if(m_interface == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
m_interface->start(); m_interface->start();
usleep(1000000); usleep(1000000);
m_interface->stop(); m_interface->stop();
@@ -135,14 +167,16 @@ namespace river_test_playback_write {
}; };
TEST(TestALL, testOutputWriteWithCallback) { TEST(TestALL, testOutputWriteWithCallback) {
std11::shared_ptr<river::Manager> manager; audio::river::initString(configurationRiver);
manager = river::Manager::create("testApplication"); std11::shared_ptr<audio::river::Manager> manager;
manager = audio::river::Manager::create("testApplication");
APPL_INFO("test output (write with callback event mode)"); APPL_INFO("test output (write with callback event mode)");
std11::shared_ptr<testOutWriteCallback> process = std11::make_shared<testOutWriteCallback>(manager); std11::shared_ptr<testOutWriteCallback> process = std11::make_shared<testOutWriteCallback>(manager);
process->run(); process->run();
process.reset(); process.reset();
usleep(500000); usleep(500000);
audio::river::unInit();
} }
}; };

View File

@@ -7,26 +7,44 @@
#ifndef __RIVER_TEST_RECORD_CALLBACK_H__ #ifndef __RIVER_TEST_RECORD_CALLBACK_H__
#define __RIVER_TEST_RECORD_CALLBACK_H__ #define __RIVER_TEST_RECORD_CALLBACK_H__
#include <river/debug.h> #include <audio/river/debug.h>
#undef __class__ #undef __class__
#define __class__ "test_record_callback" #define __class__ "test_record_callback"
namespace river_test_record_callback { namespace river_test_record_callback {
static const std::string configurationRiver =
"{\n"
" microphone:{\n"
" io:'input',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" },\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024\n"
" }\n"
"}\n";
class testInCallback { class testInCallback {
private:
std11::shared_ptr<river::Manager> m_manager;
std11::shared_ptr<river::Interface> m_interface;
public: public:
testInCallback(std11::shared_ptr<river::Manager> _manager, const std::string& _input="microphone") : std11::shared_ptr<audio::river::Manager> m_manager;
std11::shared_ptr<audio::river::Interface> m_interface;
public:
testInCallback(std11::shared_ptr<audio::river::Manager> _manager, const std::string& _input="microphone") :
m_manager(_manager) { m_manager(_manager) {
//Set stereo output: //Set stereo output:
std::vector<audio::channel> channelMap; std::vector<audio::channel> channelMap;
m_interface = m_manager->createInput(48000, m_interface = m_manager->createInput(48000,
channelMap, channelMap,
audio::format_int16, audio::format_int16,
_input, _input);
"WriteModeCallback"); if(m_interface == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
// set callback mode ... // set callback mode ...
m_interface->setInputCallback(std11::bind(&testInCallback::onDataReceived, m_interface->setInputCallback(std11::bind(&testInCallback::onDataReceived,
this, this,
@@ -38,7 +56,7 @@ namespace river_test_record_callback {
std11::placeholders::_6)); std11::placeholders::_6));
} }
void onDataReceived(const void* _data, void onDataReceived(const void* _data,
const std11::chrono::system_clock::time_point& _time, const audio::Time& _time,
size_t _nbChunk, size_t _nbChunk,
enum audio::format _format, enum audio::format _format,
uint32_t _frequency, uint32_t _frequency,
@@ -56,23 +74,27 @@ namespace river_test_record_callback {
APPL_INFO("Get data ... average=" << int32_t(value)); APPL_INFO("Get data ... average=" << int32_t(value));
} }
void run() { void run() {
if(m_interface == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
m_interface->start(); m_interface->start();
// wait 2 second ... // wait 2 second ...
usleep(20000000); usleep(20000000);
m_interface->stop(); m_interface->stop();
} }
}; };
TEST(TestALL, testInputCallBack) { TEST(TestALL, testInputCallBack) {
std11::shared_ptr<river::Manager> manager; audio::river::initString(configurationRiver);
manager = river::Manager::create("testApplication"); std11::shared_ptr<audio::river::Manager> manager;
manager = audio::river::Manager::create("testApplication");
APPL_INFO("test input (callback mode)"); APPL_INFO("test input (callback mode)");
std11::shared_ptr<testInCallback> process = std11::make_shared<testInCallback>(manager); std11::shared_ptr<testInCallback> process = std11::make_shared<testInCallback>(manager);
process->run(); process->run();
process.reset(); process.reset();
usleep(500000); usleep(500000);
audio::river::unInit();
} }
}; };

View File

@@ -11,14 +11,29 @@
#define __class__ "test_volume" #define __class__ "test_volume"
namespace river_test_volume { namespace river_test_volume {
static const std::string configurationRiver =
"{\n"
" speaker:{\n"
" io:'output',\n"
" map-on:{\n"
" interface:'auto',\n"
" name:'default',\n"
" },\n"
" frequency:0,\n"
" channel-map:['front-left', 'front-right'],\n"
" type:'auto',\n"
" nb-chunk:1024,\n"
" volume-name:'MASTER'\n"
" }\n"
"}\n";
class testCallbackVolume { class testCallbackVolume {
private: private:
std11::shared_ptr<river::Manager> m_manager; std11::shared_ptr<audio::river::Manager> m_manager;
std11::shared_ptr<river::Interface> m_interface; std11::shared_ptr<audio::river::Interface> m_interface;
double m_phase; double m_phase;
public: public:
testCallbackVolume(std11::shared_ptr<river::Manager> _manager) : testCallbackVolume(std11::shared_ptr<audio::river::Manager> _manager) :
m_manager(_manager), m_manager(_manager),
m_phase(0) { m_phase(0) {
//Set stereo output: //Set stereo output:
@@ -28,8 +43,11 @@ namespace river_test_volume {
m_interface = m_manager->createOutput(48000, m_interface = m_manager->createOutput(48000,
channelMap, channelMap,
audio::format_int16, audio::format_int16,
"speaker", "speaker");
"WriteModeCallback"); if(m_interface == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
// set callback mode ... // set callback mode ...
m_interface->setOutputCallback(std11::bind(&testCallbackVolume::onDataNeeded, m_interface->setOutputCallback(std11::bind(&testCallbackVolume::onDataNeeded,
this, this,
@@ -43,7 +61,7 @@ namespace river_test_volume {
m_interface->addVolumeGroup("FLOW"); m_interface->addVolumeGroup("FLOW");
} }
void onDataNeeded(void* _data, void onDataNeeded(void* _data,
const std11::chrono::system_clock::time_point& _time, const audio::Time& _time,
size_t _nbChunk, size_t _nbChunk,
enum audio::format _format, enum audio::format _format,
uint32_t _frequency, uint32_t _frequency,
@@ -61,6 +79,10 @@ namespace river_test_volume {
} }
} }
void run() { void run() {
if(m_interface == nullptr) {
APPL_ERROR("nullptr interface");
return;
}
m_interface->start(); m_interface->start();
usleep(1000000); usleep(1000000);
m_interface->setParameter("volume", "FLOW", "-3dB"); m_interface->setParameter("volume", "FLOW", "-3dB");
@@ -101,12 +123,14 @@ namespace river_test_volume {
}; };
TEST(TestALL, testVolume) { TEST(TestALL, testVolume) {
std11::shared_ptr<river::Manager> manager; audio::river::initString(configurationRiver);
manager = river::Manager::create("testApplication"); std11::shared_ptr<audio::river::Manager> manager;
manager = audio::river::Manager::create("testApplication");
std11::shared_ptr<testCallbackVolume> process = std11::make_shared<testCallbackVolume>(manager); std11::shared_ptr<testCallbackVolume> process = std11::make_shared<testCallbackVolume>(manager);
process->run(); process->run();
process.reset(); process.reset();
usleep(500000); usleep(500000);
audio::river::unInit();
} }
}; };