[RENAME] change airtaudio => river
This commit is contained in:
321
river/Interface.cpp
Normal file
321
river/Interface.cpp
Normal file
@@ -0,0 +1,321 @@
|
||||
/** @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(nullptr),
|
||||
m_freq(8000),
|
||||
m_map(),
|
||||
m_format(audio::format_int16),
|
||||
m_name(""),
|
||||
m_volume(0.0f) {
|
||||
|
||||
}
|
||||
|
||||
bool river::Interface::init(const std::string& _name,
|
||||
float _freq,
|
||||
const std::vector<audio::channel>& _map,
|
||||
audio::format _format,
|
||||
const std::shared_ptr<river::io::Node>& _node) {
|
||||
m_name = _name;
|
||||
m_node = _node;
|
||||
m_freq = _freq;
|
||||
m_map = _map;
|
||||
m_format = _format;
|
||||
m_process = std::make_shared<drain::Process>();
|
||||
m_volume = 0.0f;
|
||||
// register interface to be notify from the volume change.
|
||||
m_node->registerAsRemote(shared_from_this());
|
||||
// Create convertion interface
|
||||
if (m_node->isInput() == true) {
|
||||
// add all time the volume stage :
|
||||
std::shared_ptr<drain::Volume> algo = drain::Volume::create();
|
||||
algo->setInputFormat(m_node->getInterfaceFormat());
|
||||
algo->setName("volume");
|
||||
m_process->pushBack(algo);
|
||||
AIRTIO_INFO("add basic volume stage (1)");
|
||||
std::shared_ptr<drain::VolumeElement> tmpVolume = m_node->getVolume();
|
||||
if (tmpVolume != nullptr) {
|
||||
AIRTIO_INFO(" add volume for node");
|
||||
algo->addVolumeStage(tmpVolume);
|
||||
}
|
||||
} else {
|
||||
// add all time the volume stage :
|
||||
std::shared_ptr<drain::Volume> algo = drain::Volume::create();
|
||||
algo->setOutputFormat(m_node->getInterfaceFormat());
|
||||
algo->setName("volume");
|
||||
m_process->pushBack(algo);
|
||||
AIRTIO_INFO("add basic volume stage (2)");
|
||||
std::shared_ptr<drain::VolumeElement> tmpVolume = m_node->getVolume();
|
||||
if (tmpVolume != nullptr) {
|
||||
AIRTIO_INFO(" add volume for node");
|
||||
algo->addVolumeStage(tmpVolume);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<river::Interface> river::Interface::create(const std::string& _name,
|
||||
float _freq,
|
||||
const std::vector<audio::channel>& _map,
|
||||
audio::format _format,
|
||||
const std::shared_ptr<river::io::Node>& _node) {
|
||||
std::shared_ptr<river::Interface> out = std::shared_ptr<river::Interface>(new river::Interface());
|
||||
out->init(_name, _freq, _map, _format, _node);
|
||||
return out;
|
||||
}
|
||||
|
||||
river::Interface::~Interface() {
|
||||
//stop(true, true);
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
//m_node->interfaceRemove(shared_from_this());
|
||||
m_process.reset();
|
||||
}
|
||||
/*
|
||||
bool river::Interface::hasEndPoint() {
|
||||
|
||||
}
|
||||
*/
|
||||
void river::Interface::setReadwrite() {
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
m_process->removeAlgoDynamic();
|
||||
if (m_process->hasType<drain::EndPoint>() ) {
|
||||
AIRTIO_ERROR("Endpoint is already present ==> can not change");
|
||||
return;
|
||||
}
|
||||
if (m_node->isInput() == true) {
|
||||
m_process->removeIfLast<drain::EndPoint>();
|
||||
std::shared_ptr<drain::EndPointRead> algo = drain::EndPointRead::create();
|
||||
///algo->setInputFormat(drain::IOFormatInterface(m_map, m_format, m_freq));
|
||||
algo->setOutputFormat(drain::IOFormatInterface(m_map, m_format, m_freq));
|
||||
m_process->pushBack(algo);
|
||||
} else {
|
||||
m_process->removeIfFirst<drain::EndPoint>();
|
||||
std::shared_ptr<drain::EndPointWrite> algo = drain::EndPointWrite::create();
|
||||
algo->setInputFormat(drain::IOFormatInterface(m_map, m_format, m_freq));
|
||||
//algo->setOutputFormat(drain::IOFormatInterface(m_map, m_format, m_freq));
|
||||
m_process->pushFront(algo);
|
||||
}
|
||||
}
|
||||
|
||||
void river::Interface::setOutputCallback(size_t _chunkSize, drain::needDataFunction _function) {
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
m_process->removeAlgoDynamic();
|
||||
m_process->removeIfFirst<drain::EndPoint>();
|
||||
std::shared_ptr<drain::Algo> algo = drain::EndPointCallback::create(_function);
|
||||
AIRTIO_INFO("set property: " << m_map << " " << m_format << " " << m_freq);
|
||||
algo->setInputFormat(drain::IOFormatInterface(m_map, m_format, m_freq));
|
||||
//algo->setOutputFormat(drain::IOFormatInterface(m_map, m_format, m_freq));
|
||||
m_process->pushFront(algo);
|
||||
}
|
||||
|
||||
void river::Interface::setInputCallback(size_t _chunkSize, drain::haveNewDataFunction _function) {
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
m_process->removeAlgoDynamic();
|
||||
m_process->removeIfLast<drain::EndPoint>();
|
||||
std::shared_ptr<drain::Algo> algo = drain::EndPointCallback::create(_function);
|
||||
//algo->setInputFormat(drain::IOFormatInterface(m_map, m_format, m_freq));
|
||||
algo->setOutputFormat(drain::IOFormatInterface(m_map, m_format, m_freq));
|
||||
m_process->pushBack(algo);
|
||||
}
|
||||
|
||||
void river::Interface::setWriteCallback(drain::needDataFunctionWrite _function) {
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
m_process->removeAlgoDynamic();
|
||||
std::shared_ptr<drain::EndPointWrite> algo = m_process->get<drain::EndPointWrite>(0);
|
||||
if (algo == nullptr) {
|
||||
return;
|
||||
}
|
||||
algo->setCallback(_function);
|
||||
}
|
||||
|
||||
void river::Interface::start(const std::chrono::system_clock::time_point& _time) {
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
AIRTIO_DEBUG("start [BEGIN]");
|
||||
m_process->updateInterAlgo();
|
||||
m_node->interfaceAdd(shared_from_this());
|
||||
AIRTIO_DEBUG("start [ END ]");
|
||||
}
|
||||
|
||||
void river::Interface::stop(bool _fast, bool _abort) {
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
AIRTIO_DEBUG("stop [BEGIN]");
|
||||
m_node->interfaceRemove(shared_from_this());
|
||||
AIRTIO_DEBUG("stop [ END]");
|
||||
}
|
||||
|
||||
void river::Interface::abort() {
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
AIRTIO_DEBUG("abort [BEGIN]");
|
||||
// TODO :...
|
||||
AIRTIO_DEBUG("abort [ END ]");
|
||||
}
|
||||
|
||||
bool river::Interface::setParameter(const std::string& _filter, const std::string& _parameter, const std::string& _value) {
|
||||
AIRTIO_DEBUG("setParameter [BEGIN] : '" << _filter << "':'" << _parameter << "':'" << _value << "'");
|
||||
bool out = false;
|
||||
if ( _filter == "volume"
|
||||
&& _parameter != "FLOW") {
|
||||
AIRTIO_ERROR("Interface is not allowed to modify '" << _parameter << "' Volume just allowed to modify 'FLOW' volume");
|
||||
return false;
|
||||
}
|
||||
std::shared_ptr<drain::Algo> algo = m_process->get<drain::Algo>(_filter);
|
||||
if (algo == nullptr) {
|
||||
AIRTIO_ERROR("setParameter(" << _filter << ") ==> no filter named like this ...");
|
||||
return false;
|
||||
}
|
||||
out = algo->setParameter(_parameter, _value);
|
||||
AIRTIO_DEBUG("setParameter [ END ] : '" << out << "'");
|
||||
return out;
|
||||
}
|
||||
std::string river::Interface::getParameter(const std::string& _filter, const std::string& _parameter) const {
|
||||
AIRTIO_DEBUG("getParameter [BEGIN] : '" << _filter << "':'" << _parameter << "'");
|
||||
std::string out;
|
||||
std::shared_ptr<drain::Algo> algo = m_process->get<drain::Algo>(_filter);
|
||||
if (algo == nullptr) {
|
||||
AIRTIO_ERROR("setParameter(" << _filter << ") ==> no filter named like this ...");
|
||||
return "[ERROR]";
|
||||
}
|
||||
out = algo->getParameter(_parameter);
|
||||
AIRTIO_DEBUG("getParameter [ END ] : '" << out << "'");
|
||||
return out;
|
||||
}
|
||||
std::string river::Interface::getParameterProperty(const std::string& _filter, const std::string& _parameter) const {
|
||||
AIRTIO_DEBUG("getParameterProperty [BEGIN] : '" << _filter << "':'" << _parameter << "'");
|
||||
std::string out;
|
||||
std::shared_ptr<drain::Algo> algo = m_process->get<drain::Algo>(_filter);
|
||||
if (algo == nullptr) {
|
||||
AIRTIO_ERROR("setParameter(" << _filter << ") ==> no filter named like this ...");
|
||||
return "[ERROR]";
|
||||
}
|
||||
out = algo->getParameterProperty(_parameter);
|
||||
AIRTIO_DEBUG("getParameterProperty [ END ] : '" << out << "'");
|
||||
return out;
|
||||
}
|
||||
|
||||
void river::Interface::write(const void* _value, size_t _nbChunk) {
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
m_process->updateInterAlgo();
|
||||
std::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) {
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
m_process->updateInterAlgo();
|
||||
// TODO :...
|
||||
|
||||
}
|
||||
|
||||
size_t river::Interface::size() const {
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
// TODO :...
|
||||
return 0;
|
||||
}
|
||||
|
||||
void river::Interface::setBufferSize(size_t _nbChunk) {
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
m_process->updateInterAlgo();
|
||||
// TODO :...
|
||||
|
||||
}
|
||||
|
||||
void river::Interface::setBufferSize(const std::chrono::duration<int64_t, std::micro>& _time) {
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
m_process->updateInterAlgo();
|
||||
// TODO :...
|
||||
|
||||
}
|
||||
|
||||
void river::Interface::clearInternalBuffer() {
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
m_process->updateInterAlgo();
|
||||
// TODO :...
|
||||
|
||||
}
|
||||
|
||||
std::chrono::system_clock::time_point river::Interface::getCurrentTime() const {
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
// TODO :...
|
||||
return std::chrono::system_clock::time_point();
|
||||
return std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
void river::Interface::addVolumeGroup(const std::string& _name) {
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
AIRTIO_DEBUG("addVolumeGroup(" << _name << ")");
|
||||
std::shared_ptr<drain::Volume> algo = m_process->get<drain::Volume>("volume");
|
||||
if (algo == nullptr) {
|
||||
AIRTIO_ERROR("addVolumeGroup(" << _name << ") ==> no volume stage ... can not add it ...");
|
||||
return;
|
||||
}
|
||||
if (_name == "FLOW") {
|
||||
// Local volume name
|
||||
algo->addVolumeStage(std::make_shared<drain::VolumeElement>(_name));
|
||||
} else {
|
||||
// get manager unique instance:
|
||||
std::shared_ptr<river::io::Manager> mng = river::io::Manager::getInstance();
|
||||
algo->addVolumeStage(mng->getVolumeGroup(_name));
|
||||
}
|
||||
}
|
||||
|
||||
void river::Interface::systemNewInputData(std::chrono::system_clock::time_point _time, void* _data, size_t _nbChunk) {
|
||||
std::unique_lock<std::recursive_mutex> lockProcess(m_mutex);
|
||||
m_process->push(_time, _data, _nbChunk);
|
||||
}
|
||||
|
||||
void river::Interface::systemNeedOutputData(std::chrono::system_clock::time_point _time, void* _data, size_t _nbChunk, size_t _chunkSize) {
|
||||
std::unique_lock<std::recursive_mutex> lockProcess(m_mutex);
|
||||
m_process->pull(_time, _data, _nbChunk, _chunkSize);
|
||||
}
|
||||
|
||||
void river::Interface::systemVolumeChange() {
|
||||
std::unique_lock<std::recursive_mutex> lockProcess(m_mutex);
|
||||
std::shared_ptr<drain::Volume> algo = m_process->get<drain::Volume>("volume");
|
||||
if (algo == nullptr) {
|
||||
return;
|
||||
}
|
||||
algo->volumeChange();
|
||||
}
|
182
river/Interface.h
Normal file
182
river/Interface.h
Normal file
@@ -0,0 +1,182 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __AIRTIO_INTERFACE_H__
|
||||
#define __AIRTIO_INTERFACE_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <audio/format.h>
|
||||
#include <audio/channel.h>
|
||||
#include <drain/Process.h>
|
||||
#include <drain/EndPointCallback.h>
|
||||
#include <drain/EndPointWrite.h>
|
||||
#include <memory>
|
||||
|
||||
namespace river {
|
||||
namespace io {
|
||||
class Node;
|
||||
}
|
||||
class Interface : public std::enable_shared_from_this<Interface> {
|
||||
friend class io::Node;
|
||||
friend class Manager;
|
||||
protected:
|
||||
mutable std::recursive_mutex m_mutex;
|
||||
protected:
|
||||
std::shared_ptr<river::io::Node> m_node;
|
||||
float m_freq;
|
||||
std::vector<audio::channel> m_map;
|
||||
audio::format m_format;
|
||||
std::shared_ptr<drain::Process> m_process;
|
||||
protected:
|
||||
std::string m_name;
|
||||
public:
|
||||
virtual std::string getName() {
|
||||
return m_name;
|
||||
};
|
||||
protected:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
Interface();
|
||||
bool init(const std::string& _name,
|
||||
float _freq,
|
||||
const std::vector<audio::channel>& _map,
|
||||
audio::format _format,
|
||||
const std::shared_ptr<river::io::Node>& _node);
|
||||
public:
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~Interface();
|
||||
static std::shared_ptr<Interface> create(const std::string& _name,
|
||||
float _freq,
|
||||
const std::vector<audio::channel>& _map,
|
||||
audio::format _format,
|
||||
const std::shared_ptr<river::io::Node>& _node);
|
||||
/**
|
||||
* @brief set the read/write mode enable.
|
||||
*/
|
||||
virtual void setReadwrite();
|
||||
/**
|
||||
* @brief When we want to implement a Callback Mode:
|
||||
*/
|
||||
virtual void setWriteCallback(drain::needDataFunctionWrite _function);
|
||||
virtual void setOutputCallback(size_t _chunkSize, drain::needDataFunction _function);
|
||||
virtual void setInputCallback(size_t _chunkSize, drain::haveNewDataFunction _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 std::chrono::system_clock::time_point& _time = std::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 std::chrono::duration<int64_t, std::micro>& _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 std::chrono::system_clock::time_point getCurrentTime() const;
|
||||
private:
|
||||
virtual void systemNewInputData(std::chrono::system_clock::time_point _time, void* _data, size_t _nbChunk);
|
||||
virtual void systemNeedOutputData(std::chrono::system_clock::time_point _time, void* _data, size_t _nbChunk, size_t _chunkSize);
|
||||
virtual void systemVolumeChange();
|
||||
float m_volume; //!< Local channel Volume
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
91
river/Manager.cpp
Normal file
91
river/Manager.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
/** @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 <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "io/Manager.h"
|
||||
#include "io/Node.h"
|
||||
#include "debug.h"
|
||||
|
||||
#undef __class__
|
||||
#define __class__ "Manager"
|
||||
|
||||
std::shared_ptr<river::Manager> river::Manager::create(const std::string& _applicationUniqueId) {
|
||||
return std::shared_ptr<river::Manager>(new river::Manager(_applicationUniqueId));
|
||||
}
|
||||
|
||||
river::Manager::Manager(const std::string& _applicationUniqueId) :
|
||||
m_applicationUniqueId(_applicationUniqueId),
|
||||
m_listOpenInterface() {
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
//output.push_back(std::make_pair<std::string,std::string>("default", "48000 Hz, 16 bits, 2 channels: Default input "));
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string,std::string> > river::Manager::getListStreamOutput() {
|
||||
std::vector<std::pair<std::string,std::string> > output;
|
||||
//output.push_back(std::make_pair<std::string,std::string>("default", "48000 Hz, 16 bits, 2 channels: Default output "));
|
||||
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);
|
||||
}
|
||||
|
||||
std::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) {
|
||||
// get global hardware interface:
|
||||
std::shared_ptr<river::io::Manager> manager = river::io::Manager::getInstance();
|
||||
// get the output or input channel :
|
||||
std::shared_ptr<river::io::Node> node = manager->getNode(_streamName);//, false);
|
||||
// create user iterface:
|
||||
std::shared_ptr<river::Interface> interface;
|
||||
interface = river::Interface::create(_name, _freq, _map, _format, node);
|
||||
// store it in a list (needed to apply some parameters).
|
||||
m_listOpenInterface.push_back(interface);
|
||||
return interface;
|
||||
}
|
||||
|
||||
std::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) {
|
||||
// get global hardware interface:
|
||||
std::shared_ptr<river::io::Manager> manager = river::io::Manager::getInstance();
|
||||
// get the output or input channel :
|
||||
std::shared_ptr<river::io::Node> node = manager->getNode(_streamName);//, true);
|
||||
// create user iterface:
|
||||
std::shared_ptr<river::Interface> interface;
|
||||
interface = river::Interface::create(_name, _freq, _map, _format, node);
|
||||
// store it in a list (needed to apply some parameters).
|
||||
m_listOpenInterface.push_back(interface);
|
||||
return interface;
|
||||
}
|
103
river/Manager.h
Normal file
103
river/Manager.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __AIRTIO_MANAGER_H__
|
||||
#define __AIRTIO_MANAGER_H__
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
#include <memory>
|
||||
#include <river/Interface.h>
|
||||
#include <audio/format.h>
|
||||
#include <audio/channel.h>
|
||||
|
||||
namespace river {
|
||||
/**
|
||||
* @brief Audio interface manager : Single interface for every application that want to access on the Audio input/output
|
||||
*/
|
||||
class Manager {
|
||||
private:
|
||||
const std::string& m_applicationUniqueId; //!< name of the application that open the Audio Interface.
|
||||
std::vector<std::weak_ptr<river::Interface> > m_listOpenInterface; //!< List of all open Stream.
|
||||
protected:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
Manager(const std::string& _applicationUniqueId);
|
||||
public:
|
||||
static std::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 std::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 std::shared_ptr<Interface> createInput(float _freq,
|
||||
const std::vector<audio::channel>& _map,
|
||||
audio::format _format,
|
||||
const std::string& _streamName = "",
|
||||
const std::string& _name = "");
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
13
river/debug.cpp
Normal file
13
river/debug.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <river/debug.h>
|
||||
|
||||
|
||||
int32_t river::getLogId() {
|
||||
static int32_t g_val = etk::log::registerInstance("river");
|
||||
return g_val;
|
||||
}
|
51
river/debug.h
Normal file
51
river/debug.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AIRTIO_DEBUG_H__
|
||||
#define __AIRTIO_DEBUG_H__
|
||||
|
||||
#include <etk/log.h>
|
||||
|
||||
namespace river {
|
||||
int32_t getLogId();
|
||||
};
|
||||
// TODO : Review this problem of multiple intanciation of "std::stringbuf sb"
|
||||
#define AIRTIO_BASE(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 AIRTIO_CRITICAL(data) AIRTIO_BASE(1, data)
|
||||
#define AIRTIO_ERROR(data) AIRTIO_BASE(2, data)
|
||||
#define AIRTIO_WARNING(data) AIRTIO_BASE(3, data)
|
||||
#ifdef DEBUG
|
||||
#define AIRTIO_INFO(data) AIRTIO_BASE(4, data)
|
||||
#define AIRTIO_DEBUG(data) AIRTIO_BASE(5, data)
|
||||
#define AIRTIO_VERBOSE(data) AIRTIO_BASE(6, data)
|
||||
#define AIRTIO_TODO(data) AIRTIO_BASE(4, "TODO : " << data)
|
||||
#else
|
||||
#define AIRTIO_INFO(data) do { } while(false)
|
||||
#define AIRTIO_DEBUG(data) do { } while(false)
|
||||
#define AIRTIO_VERBOSE(data) do { } while(false)
|
||||
#define AIRTIO_TODO(data) do { } while(false)
|
||||
#endif
|
||||
|
||||
#define AIRTIO_ASSERT(cond,data) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
AIRTIO_CRITICAL(data); \
|
||||
assert(!#cond); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
20
river/debugRemove.h
Normal file
20
river/debugRemove.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifdef __AIRTIO_DEBUG_H__
|
||||
#undef __AIRTIO_DEBUG_H__
|
||||
|
||||
#undef AIRTIO_BASE
|
||||
#undef AIRTIO_CRITICAL
|
||||
#undef AIRTIO_ERROR
|
||||
#undef AIRTIO_WARNING
|
||||
#undef AIRTIO_INFO
|
||||
#undef AIRTIO_DEBUG
|
||||
#undef AIRTIO_VERBOSE
|
||||
#undef AIRTIO_TODO
|
||||
#undef AIRTIO_ASSERT
|
||||
#endif
|
||||
|
97
river/io/Manager.cpp
Normal file
97
river/io/Manager.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include "Manager.h"
|
||||
#include <memory>
|
||||
#include <river/debug.h>
|
||||
#include "Node.h"
|
||||
|
||||
#undef __class__
|
||||
#define __class__ "io::Manager"
|
||||
|
||||
river::io::Manager::Manager() {
|
||||
if (m_config.load("DATA:hardware.json") == false) {
|
||||
AIRTIO_ERROR("you must set a basic configuration file for harware configuration: DATA:hardware.json");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
std::shared_ptr<river::io::Manager> river::io::Manager::getInstance() {
|
||||
static std::shared_ptr<river::io::Manager> manager(new Manager());
|
||||
return manager;
|
||||
}
|
||||
|
||||
std::shared_ptr<river::io::Node> river::io::Manager::getNode(const std::string& _name) {
|
||||
for (size_t iii=0; iii< m_list.size(); ++iii) {
|
||||
std::shared_ptr<river::io::Node> tmppp = m_list[iii].lock();
|
||||
if ( tmppp != nullptr
|
||||
&& _name == tmppp->getName()) {
|
||||
return tmppp;
|
||||
}
|
||||
}
|
||||
// check if the node can be open :
|
||||
const std::shared_ptr<const ejson::Object> tmpObject = m_config.getObject(_name);
|
||||
if (tmpObject != nullptr) {
|
||||
std::shared_ptr<river::io::Node> tmp = river::io::Node::create(_name, tmpObject);
|
||||
m_list.push_back(tmp);
|
||||
return tmp;
|
||||
}
|
||||
AIRTIO_ERROR("Can not create the interface : '" << _name << "' the node is not DEFINED in the configuration file availlable : " << m_config.getKeys());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<drain::VolumeElement> river::io::Manager::getVolumeGroup(const std::string& _name) {
|
||||
if (_name == "") {
|
||||
AIRTIO_ERROR("Try to create an audio group with no name ...");
|
||||
return nullptr;
|
||||
}
|
||||
for (auto &it : m_volumeGroup) {
|
||||
if (it == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (it->getName() == _name) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
AIRTIO_DEBUG("Add a new volume group : '" << _name << "'");
|
||||
std::shared_ptr<drain::VolumeElement> tmpVolume = std::make_shared<drain::VolumeElement>(_name);
|
||||
m_volumeGroup.push_back(tmpVolume);
|
||||
return tmpVolume;
|
||||
}
|
||||
|
||||
bool river::io::Manager::setVolume(const std::string& _volumeName, float _valuedB) {
|
||||
std::shared_ptr<drain::VolumeElement> volume = getVolumeGroup(_volumeName);
|
||||
if (volume == nullptr) {
|
||||
AIRTIO_ERROR("Can not set volume ... : '" << _volumeName << "'");
|
||||
return false;
|
||||
}
|
||||
if ( _valuedB < -300
|
||||
|| _valuedB > 300) {
|
||||
AIRTIO_ERROR("Can not set volume ... : '" << _volumeName << "' out of range : [-300..300]");
|
||||
return false;
|
||||
}
|
||||
volume->setVolume(_valuedB);
|
||||
for (auto &it2 : m_list) {
|
||||
std::shared_ptr<river::io::Node> val = it2.lock();
|
||||
if (val != nullptr) {
|
||||
val->volumeChange();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
float river::io::Manager::getVolume(const std::string& _volumeName) {
|
||||
std::shared_ptr<drain::VolumeElement> volume = getVolumeGroup(_volumeName);
|
||||
if (volume == nullptr) {
|
||||
AIRTIO_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);
|
||||
}
|
76
river/io/Manager.h
Normal file
76
river/io/Manager.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __AIRTIO_IO_MANAGER_H__
|
||||
#define __AIRTIO_IO_MANAGER_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <stdint.h>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <audio/format.h>
|
||||
#include <audio/channel.h>
|
||||
#include <ejson/ejson.h>
|
||||
#include <memory>
|
||||
#include <drain/Volume.h>
|
||||
|
||||
namespace river {
|
||||
namespace io {
|
||||
class Node;
|
||||
class Manager {
|
||||
private:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
Manager();
|
||||
public:
|
||||
static std::shared_ptr<Manager> getInstance();
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~Manager() {};
|
||||
private:
|
||||
ejson::Document m_config; // harware configuration
|
||||
std::vector<std::shared_ptr<river::io::Node> > m_listKeepAlive; //!< list of all Node that might be keep alive sone time
|
||||
std::vector<std::weak_ptr<river::io::Node> > m_list; //!< List of all IO node
|
||||
public:
|
||||
std::shared_ptr<river::io::Node> getNode(const std::string& _name);
|
||||
private:
|
||||
std::vector<std::shared_ptr<drain::VolumeElement>> m_volumeGroup;
|
||||
public:
|
||||
std::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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
366
river/io/Node.cpp
Normal file
366
river/io/Node.cpp
Normal file
@@ -0,0 +1,366 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include "Node.h"
|
||||
#include <river/debug.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#undef __class__
|
||||
#define __class__ "io::Node"
|
||||
|
||||
#ifndef INT16_MAX
|
||||
#define INT16_MAX 0x7fff
|
||||
#endif
|
||||
#ifndef INT16_MIN
|
||||
#define INT16_MIN (-INT16_MAX - 1)
|
||||
#endif
|
||||
#ifndef INT32_MAX
|
||||
#define INT32_MAX 0x7fffffffL
|
||||
#endif
|
||||
#ifndef INT32_MIN
|
||||
#define INT32_MIN (-INT32_MAX - 1L)
|
||||
#endif
|
||||
|
||||
int32_t river::io::Node::rtAudioCallback(void* _outputBuffer,
|
||||
void* _inputBuffer,
|
||||
unsigned int _nBufferFrames,
|
||||
double _streamTime,
|
||||
airtaudio::streamStatus _status) {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
std::chrono::system_clock::time_point ttime = std::chrono::system_clock::time_point();//std::chrono::system_clock::now();
|
||||
|
||||
if (_outputBuffer != nullptr) {
|
||||
AIRTIO_VERBOSE("data Output");
|
||||
std::vector<int32_t> output;
|
||||
output.resize(_nBufferFrames*m_interfaceFormat.getMap().size(), 0);
|
||||
const int32_t* outputTmp = nullptr;
|
||||
std::vector<uint8_t> outputTmp2;
|
||||
outputTmp2.resize(sizeof(int32_t)*m_interfaceFormat.getMap().size()*_nBufferFrames, 0);
|
||||
for (auto &it : m_list) {
|
||||
if (it != nullptr) {
|
||||
// clear datas ...
|
||||
memset(&outputTmp2[0], 0, sizeof(int32_t)*m_interfaceFormat.getMap().size()*_nBufferFrames);
|
||||
AIRTIO_VERBOSE(" IO : " /* << std::distance(m_list.begin(), it)*/ << "/" << m_list.size() << " name="<< it->getName());
|
||||
it->systemNeedOutputData(ttime, &outputTmp2[0], _nBufferFrames, sizeof(int32_t)*m_interfaceFormat.getMap().size());
|
||||
outputTmp = reinterpret_cast<const int32_t*>(&outputTmp2[0]);
|
||||
//it->systemNeedOutputData(ttime, _outputBuffer, _nBufferFrames, sizeof(int16_t)*m_map.size());
|
||||
// Add data to the output tmp buffer :
|
||||
for (size_t kkk=0; kkk<output.size(); ++kkk) {
|
||||
output[kkk] += outputTmp[kkk];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
int16_t* outputBuffer = static_cast<int16_t*>(_outputBuffer);
|
||||
for (size_t kkk=0; kkk<output.size(); ++kkk) {
|
||||
*outputBuffer++ = static_cast<int16_t>(std::min(std::max(INT16_MIN, output[kkk]), INT16_MAX));
|
||||
}
|
||||
}
|
||||
if (_inputBuffer != nullptr) {
|
||||
AIRTIO_INFO("data Input");
|
||||
int16_t* inputBuffer = static_cast<int16_t *>(_inputBuffer);
|
||||
for (size_t iii=0; iii< m_list.size(); ++iii) {
|
||||
if (m_list[iii] != nullptr) {
|
||||
AIRTIO_INFO(" IO : " << iii+1 << "/" << m_list.size() << " name="<< m_list[iii]->getName());
|
||||
m_list[iii]->systemNewInputData(ttime, inputBuffer, _nBufferFrames);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<river::io::Node> river::io::Node::create(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config) {
|
||||
return std::shared_ptr<river::io::Node>(new river::io::Node(_name, _config));
|
||||
}
|
||||
|
||||
river::io::Node::Node(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config) :
|
||||
m_config(_config),
|
||||
m_name(_name),
|
||||
m_isInput(false) {
|
||||
AIRTIO_INFO("-----------------------------------------------------------------");
|
||||
AIRTIO_INFO("-- CREATE NODE --");
|
||||
AIRTIO_INFO("-----------------------------------------------------------------");
|
||||
/**
|
||||
io:"input", # input or output
|
||||
map-on:{ # select hardware interface and name
|
||||
interface:"alsa", # interface : "alsa", "pulse", "core", ...
|
||||
name:"default", # name of the interface
|
||||
},
|
||||
frequency:48000, # frequency to open device
|
||||
channel-map:[ # mapping of the harware device (to change map if needed)
|
||||
"front-left", "front-right",
|
||||
"read-left", "rear-right",
|
||||
],
|
||||
type:"int16", # format to open device (int8, int16, int16-on-ont32, int24, int32, float)
|
||||
nb-chunk:1024 # number of chunk to open device (create the latency anf the frequency to call user)
|
||||
*/
|
||||
m_isInput = m_config->getStringValue("io") == "input";
|
||||
enum airtaudio::api::type typeInterface = airtaudio::api::LINUX_ALSA;
|
||||
std::string streamName = "default";
|
||||
const std::shared_ptr<const ejson::Object> tmpObject = m_config->getObject("map-on");
|
||||
if (tmpObject == nullptr) {
|
||||
AIRTIO_WARNING("missing node : 'map-on' ==> auto map : 'alsa:default'");
|
||||
} else {
|
||||
std::string value = tmpObject->getStringValue("interface", "default");
|
||||
if (value == "alsa") {
|
||||
typeInterface = airtaudio::api::LINUX_ALSA;
|
||||
} else if (value == "pulse") {
|
||||
typeInterface = airtaudio::api::LINUX_PULSE;
|
||||
} else if (value == "b") {
|
||||
typeInterface = airtaudio::api::LINUX_OSS;
|
||||
} else if (value == "jack") {
|
||||
typeInterface = airtaudio::api::UNIX_JACK;
|
||||
} else if (value == "mac-core") {
|
||||
typeInterface = airtaudio::api::MACOSX_CORE;
|
||||
} else if (value == "ios-core") {
|
||||
typeInterface = airtaudio::api::IOS_CORE;
|
||||
} else if (value == "asio") {
|
||||
typeInterface = airtaudio::api::WINDOWS_ASIO;
|
||||
} else if (value == "ds") {
|
||||
typeInterface = airtaudio::api::WINDOWS_DS;
|
||||
} else if (value == "dummy") {
|
||||
typeInterface = airtaudio::api::RTAUDIO_DUMMY;
|
||||
} else if (value == "java") {
|
||||
typeInterface = airtaudio::api::ANDROID_JAVA;
|
||||
} else if (value == "user-1") {
|
||||
typeInterface = airtaudio::api::USER_INTERFACE_1;
|
||||
} else if (value == "user-2") {
|
||||
typeInterface = airtaudio::api::USER_INTERFACE_2;
|
||||
} else if (value == "user-3") {
|
||||
typeInterface = airtaudio::api::USER_INTERFACE_3;
|
||||
} else if (value == "user-4") {
|
||||
typeInterface = airtaudio::api::USER_INTERFACE_4;
|
||||
} else {
|
||||
AIRTIO_WARNING("Unknow interface : '" << value << "'");
|
||||
}
|
||||
streamName = tmpObject->getStringValue("name", "default");
|
||||
}
|
||||
int32_t frequency = m_config->getNumberValue("frequency", 48000);
|
||||
std::string type = m_config->getStringValue("type", "int16");
|
||||
int32_t nbChunk = m_config->getNumberValue("nb-chunk", 1024);
|
||||
std::string volumeName = m_config->getStringValue("volume-name", "");
|
||||
if (volumeName != "") {
|
||||
AIRTIO_INFO("add node volume stage : '" << volumeName << "'");
|
||||
// use global manager for volume ...
|
||||
m_volume = river::io::Manager::getInstance()->getVolumeGroup(volumeName);
|
||||
}
|
||||
|
||||
enum audio::format formatType = audio::format_int16;
|
||||
if (type == "int16") {
|
||||
formatType = audio::format_int16;
|
||||
} else {
|
||||
AIRTIO_WARNING("not managed type : '" << type << "'");
|
||||
}
|
||||
// TODO : MAP ...
|
||||
|
||||
// intanciate specific API ...
|
||||
m_adac.instanciate(typeInterface);
|
||||
// TODO : Check return ...
|
||||
|
||||
if (streamName == "") {
|
||||
streamName = "default";
|
||||
}
|
||||
std::vector<audio::channel> map;
|
||||
// set default channel property :
|
||||
map.push_back(audio::channel_frontLeft);
|
||||
map.push_back(audio::channel_frontRight);
|
||||
|
||||
m_hardwareFormat.set(map, formatType, frequency);
|
||||
// TODO : Better view of interface type float -> float, int16 -> int16/int32, ...
|
||||
if (m_isInput == true) {
|
||||
// for input we just transfert audio with no transformation
|
||||
m_interfaceFormat.set(map, audio::format_int16, frequency);
|
||||
} else {
|
||||
// for output we will do a mix ...
|
||||
m_interfaceFormat.set(map, audio::format_int16_on_int32, frequency);
|
||||
}
|
||||
|
||||
// search device ID :
|
||||
AIRTIO_INFO("Open :");
|
||||
AIRTIO_INFO(" m_streamName=" << streamName);
|
||||
AIRTIO_INFO(" m_freq=" << m_hardwareFormat.getFrequency());
|
||||
AIRTIO_INFO(" m_map=" << m_hardwareFormat.getMap());
|
||||
AIRTIO_INFO(" m_format=" << m_hardwareFormat.getFormat());
|
||||
AIRTIO_INFO(" m_isInput=" << m_isInput);
|
||||
int32_t deviceId = 0;
|
||||
AIRTIO_INFO("Device list:");
|
||||
for (int32_t iii=0; iii<m_adac.getDeviceCount(); ++iii) {
|
||||
m_info = m_adac.getDeviceInfo(iii);
|
||||
AIRTIO_INFO(" " << iii << " name :" << m_info.name);
|
||||
if (m_info.name == streamName) {
|
||||
AIRTIO_INFO(" Select ...");
|
||||
deviceId = iii;
|
||||
}
|
||||
}
|
||||
// Open specific ID :
|
||||
m_info = m_adac.getDeviceInfo(deviceId);
|
||||
// display property :
|
||||
{
|
||||
AIRTIO_INFO("Device " << deviceId << " property :");
|
||||
AIRTIO_INFO(" probe=" << m_info.probed);
|
||||
AIRTIO_INFO(" name=" << m_info.name);
|
||||
AIRTIO_INFO(" outputChannels=" << m_info.outputChannels);
|
||||
AIRTIO_INFO(" inputChannels=" << m_info.inputChannels);
|
||||
AIRTIO_INFO(" duplexChannels=" << m_info.duplexChannels);
|
||||
AIRTIO_INFO(" isDefaultOutput=" << m_info.isDefaultOutput);
|
||||
AIRTIO_INFO(" isDefaultInput=" << m_info.isDefaultInput);
|
||||
//std::string rrate;
|
||||
std::stringstream rrate;
|
||||
for (int32_t jjj=0; jjj<m_info.sampleRates.size(); ++jjj) {
|
||||
rrate << m_info.sampleRates[jjj] << ";";
|
||||
}
|
||||
AIRTIO_INFO(" rates=" << rrate.str());
|
||||
switch(m_info.nativeFormats) {
|
||||
case airtaudio::SINT8:
|
||||
AIRTIO_INFO(" native Format: 8-bit signed integer");
|
||||
break;
|
||||
case airtaudio::SINT16:
|
||||
AIRTIO_INFO(" native Format: 16-bit signed integer");
|
||||
break;
|
||||
case airtaudio::SINT24:
|
||||
AIRTIO_INFO(" native Format: 24-bit signed integer");
|
||||
break;
|
||||
case airtaudio::SINT32:
|
||||
AIRTIO_INFO(" native Format: 32-bit signed integer");
|
||||
break;
|
||||
case airtaudio::FLOAT32:
|
||||
AIRTIO_INFO(" native Format: Normalized between plus/minus 1.0");
|
||||
break;
|
||||
case airtaudio::FLOAT64:
|
||||
AIRTIO_INFO(" native Format: Normalized between plus/minus 1.0");
|
||||
break;
|
||||
default:
|
||||
AIRTIO_INFO(" native Format: Unknow");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// open Audio device:
|
||||
airtaudio::StreamParameters params;
|
||||
params.deviceId = deviceId;
|
||||
if (m_isInput == true) {
|
||||
m_info.inputChannels = 2;
|
||||
params.nChannels = 2;
|
||||
} else {
|
||||
m_info.outputChannels = 2;
|
||||
params.nChannels = 2;
|
||||
}
|
||||
|
||||
m_rtaudioFrameSize = nbChunk;
|
||||
AIRTIO_INFO("Open output stream nbChannels=" << params.nChannels);
|
||||
enum airtaudio::errorType err = airtaudio::errorNone;
|
||||
if (m_isInput == true) {
|
||||
err = m_adac.openStream(nullptr, ¶ms,
|
||||
airtaudio::SINT16, m_hardwareFormat.getFrequency(), &m_rtaudioFrameSize,
|
||||
std::bind(&river::io::Node::rtAudioCallback,
|
||||
this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2,
|
||||
std::placeholders::_3,
|
||||
std::placeholders::_4,
|
||||
std::placeholders::_5)
|
||||
);
|
||||
} else {
|
||||
err = m_adac.openStream(¶ms, nullptr,
|
||||
airtaudio::SINT16, m_hardwareFormat.getFrequency(), &m_rtaudioFrameSize,
|
||||
std::bind(&river::io::Node::rtAudioCallback,
|
||||
this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2,
|
||||
std::placeholders::_3,
|
||||
std::placeholders::_4,
|
||||
std::placeholders::_5)
|
||||
);
|
||||
}
|
||||
if (err != airtaudio::errorNone) {
|
||||
AIRTIO_ERROR("Create stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not create stream " << err);
|
||||
}
|
||||
}
|
||||
|
||||
river::io::Node::~Node() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
AIRTIO_INFO("-----------------------------------------------------------------");
|
||||
AIRTIO_INFO("-- DESTROY NODE --");
|
||||
AIRTIO_INFO("-----------------------------------------------------------------");
|
||||
AIRTIO_INFO("close input stream");
|
||||
if (m_adac.isStreamOpen() ) {
|
||||
m_adac.closeStream();
|
||||
}
|
||||
};
|
||||
|
||||
void river::io::Node::start() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
AIRTIO_INFO("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
|
||||
enum airtaudio::errorType err = m_adac.startStream();
|
||||
if (err != airtaudio::errorNone) {
|
||||
AIRTIO_ERROR("Start stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not start stream ... " << err);
|
||||
}
|
||||
}
|
||||
|
||||
void river::io::Node::stop() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
AIRTIO_INFO("Stop stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
|
||||
enum airtaudio::errorType err = m_adac.stopStream();
|
||||
if (err != airtaudio::errorNone) {
|
||||
AIRTIO_ERROR("Stop stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not stop stream ... " << err);
|
||||
}
|
||||
}
|
||||
|
||||
void river::io::Node::registerAsRemote(const std::shared_ptr<river::Interface>& _interface) {
|
||||
auto it = m_listAvaillable.begin();
|
||||
while (it != m_listAvaillable.end()) {
|
||||
if (it->expired() == true) {
|
||||
it = m_listAvaillable.erase(it);
|
||||
}
|
||||
++it;
|
||||
}
|
||||
m_listAvaillable.push_back(_interface);
|
||||
}
|
||||
|
||||
void river::io::Node::interfaceAdd(const std::shared_ptr<river::Interface>& _interface) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
for (size_t iii=0; iii< m_list.size(); ++iii) {
|
||||
if (_interface == m_list[iii]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
AIRTIO_INFO("ADD interface for stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
|
||||
m_list.push_back(_interface);
|
||||
}
|
||||
if (m_list.size() == 1) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
void river::io::Node::interfaceRemove(const std::shared_ptr<river::Interface>& _interface) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
for (size_t iii=0; iii< m_list.size(); ++iii) {
|
||||
if (_interface == m_list[iii]) {
|
||||
m_list.erase(m_list.begin()+iii);
|
||||
AIRTIO_INFO("RM interface for stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_list.size() == 0) {
|
||||
stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void river::io::Node::volumeChange() {
|
||||
for (auto &it : m_listAvaillable) {
|
||||
std::shared_ptr<river::Interface> node = it.lock();
|
||||
if (node != nullptr) {
|
||||
node->systemVolumeChange();
|
||||
}
|
||||
}
|
||||
}
|
98
river/io/Node.h
Normal file
98
river/io/Node.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __AIRTIO_IO_NODE_H__
|
||||
#define __AIRTIO_IO_NODE_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <stdint.h>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <audio/format.h>
|
||||
#include <audio/channel.h>
|
||||
#include "Manager.h"
|
||||
#include <memory>
|
||||
#include <river/Interface.h>
|
||||
#include <airtaudio/Interface.h>
|
||||
#include <drain/IOFormatInterface.h>
|
||||
#include <drain/Volume.h>
|
||||
|
||||
namespace river {
|
||||
namespace io {
|
||||
class Manager;
|
||||
class Node {
|
||||
private:
|
||||
mutable std::mutex m_mutex;
|
||||
std::shared_ptr<const ejson::Object> m_config;
|
||||
std::shared_ptr<drain::VolumeElement> m_volume; //!< if a volume is set it is set here ...
|
||||
private:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
Node(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config);
|
||||
public:
|
||||
static std::shared_ptr<Node> create(const std::string& _name, const std::shared_ptr<const ejson::Object>& _config);
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~Node();
|
||||
private:
|
||||
std::vector<std::weak_ptr<river::Interface> > m_listAvaillable; //!< List of all interface that exist on this Node
|
||||
std::vector<std::shared_ptr<river::Interface> > m_list;
|
||||
public:
|
||||
void registerAsRemote(const std::shared_ptr<river::Interface>& _interface);
|
||||
void interfaceAdd(const std::shared_ptr<river::Interface>& _interface);
|
||||
void interfaceRemove(const std::shared_ptr<river::Interface>& _interface);
|
||||
private:
|
||||
airtaudio::Interface m_adac; //!< Real audio interface
|
||||
airtaudio::DeviceInfo m_info;
|
||||
unsigned int m_rtaudioFrameSize;
|
||||
public:
|
||||
int32_t rtAudioCallback(void* _outputBuffer,
|
||||
void * _inputBuffer,
|
||||
unsigned int _nBufferFrames,
|
||||
double _streamTime,
|
||||
airtaudio::streamStatus _status);
|
||||
private:
|
||||
std::string m_name; //!< Harware.json configuration name
|
||||
public:
|
||||
const std::string& getName() {
|
||||
return m_name;
|
||||
}
|
||||
private:
|
||||
drain::IOFormatInterface m_interfaceFormat;
|
||||
public:
|
||||
const drain::IOFormatInterface& getInterfaceFormat() {
|
||||
return m_interfaceFormat;
|
||||
}
|
||||
private:
|
||||
drain::IOFormatInterface m_hardwareFormat;
|
||||
private:
|
||||
bool m_isInput;
|
||||
public:
|
||||
bool isInput() {
|
||||
return m_isInput;
|
||||
}
|
||||
bool isOutput() {
|
||||
return !m_isInput;
|
||||
}
|
||||
private:
|
||||
void start();
|
||||
void stop();
|
||||
public:
|
||||
const std::shared_ptr<drain::VolumeElement>& getVolume() {
|
||||
return m_volume;
|
||||
}
|
||||
public:
|
||||
void volumeChange();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user