2015-01-25 22:17:06 +01:00
|
|
|
/** @file
|
|
|
|
* @author Edouard DUPIN
|
|
|
|
* @copyright 2015, Edouard DUPIN, all right reserved
|
|
|
|
* @license APACHE v2.0 (see license file)
|
|
|
|
*/
|
|
|
|
|
2015-01-26 22:04:29 +01:00
|
|
|
#include "Node.h"
|
|
|
|
#include <airtio/debug.h>
|
2015-01-25 22:17:06 +01:00
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
|
2015-01-27 21:01:52 +01:00
|
|
|
#undef __class__
|
|
|
|
#define __class__ "io::Node"
|
|
|
|
|
2015-01-25 22:17:06 +01:00
|
|
|
#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
|
|
|
|
|
2015-01-27 21:01:52 +01:00
|
|
|
int32_t airtio::io::Node::rtAudioCallback(void* _outputBuffer,
|
|
|
|
void* _inputBuffer,
|
|
|
|
unsigned int _nBufferFrames,
|
|
|
|
double _streamTime,
|
|
|
|
airtaudio::streamStatus _status) {
|
2015-01-26 22:04:29 +01:00
|
|
|
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();
|
2015-01-25 22:17:06 +01:00
|
|
|
|
|
|
|
if (_outputBuffer != nullptr) {
|
2015-01-26 22:04:29 +01:00
|
|
|
AIRTIO_VERBOSE("data Output");
|
2015-01-25 22:17:06 +01:00
|
|
|
std::vector<int32_t> output;
|
|
|
|
output.resize(_nBufferFrames*m_map.size(), 0);
|
2015-01-27 21:01:52 +01:00
|
|
|
int16_t* outputTmp = nullptr;
|
|
|
|
void* outputTmp2 = nullptr;
|
|
|
|
size_t tmpSize = 0;
|
|
|
|
for (auto &it : m_list) {
|
|
|
|
if (it != nullptr) {
|
|
|
|
AIRTIO_VERBOSE(" IO : " /* << std::distance(m_list.begin(), it)*/ << "/" << m_list.size() << " name="<< it->getName());
|
|
|
|
tmpSize = _nBufferFrames;
|
|
|
|
it->systemNeedOutputData(ttime, outputTmp2, tmpSize, sizeof(int16_t)*m_map.size());
|
|
|
|
outputTmp = static_cast<int16_t*>(outputTmp2);
|
|
|
|
//it->systemNeedOutputData(ttime, _outputBuffer, _nBufferFrames, sizeof(int16_t)*m_map.size());
|
2015-01-25 22:17:06 +01:00
|
|
|
// Add data to the output tmp buffer :
|
|
|
|
for (size_t kkk=0; kkk<output.size(); ++kkk) {
|
|
|
|
output[kkk] += static_cast<int32_t>(outputTmp[kkk]);
|
|
|
|
//*_outputBuffer++ = static_cast<int16_t>(outputTmp[kkk]);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-01-27 21:01:52 +01:00
|
|
|
int16_t* outputBuffer = static_cast<int16_t*>(_outputBuffer);
|
2015-01-25 22:17:06 +01:00
|
|
|
for (size_t kkk=0; kkk<output.size(); ++kkk) {
|
2015-01-27 21:01:52 +01:00
|
|
|
*outputBuffer++ = static_cast<int16_t>(std::min(std::max(INT16_MIN, output[kkk]), INT16_MAX));
|
2015-01-25 22:17:06 +01:00
|
|
|
//*_outputBuffer++ = static_cast<int16_t>(output[kkk]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_inputBuffer != nullptr) {
|
2015-01-26 22:04:29 +01:00
|
|
|
AIRTIO_INFO("data Input");
|
2015-01-27 21:01:52 +01:00
|
|
|
int16_t* inputBuffer = static_cast<int16_t *>(_inputBuffer);
|
2015-01-25 22:17:06 +01:00
|
|
|
for (size_t iii=0; iii< m_list.size(); ++iii) {
|
|
|
|
if (m_list[iii] != nullptr) {
|
2015-01-26 22:04:29 +01:00
|
|
|
AIRTIO_INFO(" IO : " << iii+1 << "/" << m_list.size() << " name="<< m_list[iii]->getName());
|
2015-01-27 21:01:52 +01:00
|
|
|
m_list[iii]->systemNewInputData(ttime, inputBuffer, _nBufferFrames);
|
2015-01-25 22:17:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::shared_ptr<airtio::io::Node> airtio::io::Node::create(const std::string& _streamName, bool _isInput) {
|
|
|
|
return std::shared_ptr<airtio::io::Node>(new airtio::io::Node(_streamName, _isInput));
|
|
|
|
}
|
|
|
|
|
|
|
|
airtio::io::Node::Node(const std::string& _streamName, bool _isInput) :
|
|
|
|
m_streamName(_streamName),
|
|
|
|
m_frequency(48000),
|
|
|
|
m_format(airtalgo::format_int16),
|
|
|
|
m_isInput(_isInput) {
|
2015-01-26 22:04:29 +01:00
|
|
|
AIRTIO_INFO("-----------------------------------------------------------------");
|
|
|
|
AIRTIO_INFO("-- CREATE NODE --");
|
|
|
|
AIRTIO_INFO("-----------------------------------------------------------------");
|
|
|
|
// intanciate specific API ...
|
|
|
|
m_adac.instanciate();
|
2015-01-25 22:17:06 +01:00
|
|
|
|
|
|
|
if (m_streamName == "") {
|
|
|
|
m_streamName = "default";
|
|
|
|
}
|
|
|
|
// set default channel property :
|
|
|
|
m_map.push_back(airtalgo::channel_frontLeft);
|
|
|
|
m_map.push_back(airtalgo::channel_frontRight);
|
|
|
|
|
|
|
|
// search device ID :
|
2015-01-26 22:04:29 +01:00
|
|
|
AIRTIO_INFO("Open :");
|
|
|
|
AIRTIO_INFO(" m_streamName=" << m_streamName);
|
|
|
|
AIRTIO_INFO(" m_freq=" << m_frequency);
|
|
|
|
AIRTIO_INFO(" m_map=" << m_map);
|
|
|
|
AIRTIO_INFO(" m_format=" << m_format);
|
|
|
|
AIRTIO_INFO(" m_isInput=" << m_isInput);
|
2015-01-25 22:17:06 +01:00
|
|
|
int32_t deviceId = 0;
|
2015-01-26 22:04:29 +01:00
|
|
|
AIRTIO_INFO("Device list:");
|
2015-01-25 22:17:06 +01:00
|
|
|
for (int32_t iii=0; iii<m_adac.getDeviceCount(); ++iii) {
|
|
|
|
m_info = m_adac.getDeviceInfo(iii);
|
2015-01-26 22:04:29 +01:00
|
|
|
AIRTIO_INFO(" " << iii << " name :" << m_info.name);
|
2015-01-25 22:17:06 +01:00
|
|
|
if (m_info.name == m_streamName) {
|
2015-01-26 22:04:29 +01:00
|
|
|
AIRTIO_INFO(" Select ...");
|
2015-01-25 22:17:06 +01:00
|
|
|
deviceId = iii;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Open specific ID :
|
|
|
|
m_info = m_adac.getDeviceInfo(deviceId);
|
|
|
|
// display property :
|
|
|
|
{
|
2015-01-26 22:04:29 +01:00
|
|
|
AIRTIO_INFO("Device " << deviceId << " property :");
|
|
|
|
AIRTIO_INFO(" probe=" << (m_info.probed==true?"true":"false"));
|
|
|
|
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==true?"true":"false"));
|
|
|
|
AIRTIO_INFO(" isDefaultInput=" << (m_info.isDefaultInput==true?"true":"false"));
|
2015-01-25 22:17:06 +01:00
|
|
|
//std::string rrate;
|
|
|
|
std::stringstream rrate;
|
|
|
|
for (int32_t jjj=0; jjj<m_info.sampleRates.size(); ++jjj) {
|
|
|
|
rrate << m_info.sampleRates[jjj] << ";";
|
|
|
|
}
|
2015-01-26 22:04:29 +01:00
|
|
|
AIRTIO_INFO(" rates=" << rrate.str());
|
2015-01-25 22:17:06 +01:00
|
|
|
switch(m_info.nativeFormats) {
|
2015-01-26 22:04:29 +01:00
|
|
|
case airtaudio::SINT8:
|
|
|
|
AIRTIO_INFO(" native Format: 8-bit signed integer");
|
2015-01-25 22:17:06 +01:00
|
|
|
break;
|
2015-01-26 22:04:29 +01:00
|
|
|
case airtaudio::SINT16:
|
|
|
|
AIRTIO_INFO(" native Format: 16-bit signed integer");
|
2015-01-25 22:17:06 +01:00
|
|
|
break;
|
2015-01-26 22:04:29 +01:00
|
|
|
case airtaudio::SINT24:
|
|
|
|
AIRTIO_INFO(" native Format: 24-bit signed integer");
|
2015-01-25 22:17:06 +01:00
|
|
|
break;
|
2015-01-26 22:04:29 +01:00
|
|
|
case airtaudio::SINT32:
|
|
|
|
AIRTIO_INFO(" native Format: 32-bit signed integer");
|
2015-01-25 22:17:06 +01:00
|
|
|
break;
|
2015-01-26 22:04:29 +01:00
|
|
|
case airtaudio::FLOAT32:
|
|
|
|
AIRTIO_INFO(" native Format: Normalized between plus/minus 1.0");
|
2015-01-25 22:17:06 +01:00
|
|
|
break;
|
2015-01-26 22:04:29 +01:00
|
|
|
case airtaudio::FLOAT64:
|
|
|
|
AIRTIO_INFO(" native Format: Normalized between plus/minus 1.0");
|
2015-01-25 22:17:06 +01:00
|
|
|
break;
|
|
|
|
default:
|
2015-01-26 22:04:29 +01:00
|
|
|
AIRTIO_INFO(" native Format: Unknow");
|
2015-01-25 22:17:06 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// open Audio device:
|
|
|
|
unsigned int nbChunk= 1024;
|
2015-01-26 22:04:29 +01:00
|
|
|
airtaudio::StreamParameters params;
|
2015-01-25 22:17:06 +01:00
|
|
|
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;
|
2015-01-26 22:04:29 +01:00
|
|
|
AIRTIO_INFO("Open output stream nbChannels=" << params.nChannels);
|
|
|
|
enum airtaudio::errorType err = airtaudio::errorNone;
|
|
|
|
if (m_isInput == true) {
|
2015-01-27 21:01:52 +01:00
|
|
|
err = m_adac.openStream(nullptr, ¶ms,
|
|
|
|
airtaudio::SINT16, m_frequency, &m_rtaudioFrameSize,
|
|
|
|
std::bind(&airtio::io::Node::rtAudioCallback,
|
|
|
|
this,
|
|
|
|
std::placeholders::_1,
|
|
|
|
std::placeholders::_2,
|
|
|
|
std::placeholders::_3,
|
|
|
|
std::placeholders::_4,
|
|
|
|
std::placeholders::_5)
|
|
|
|
);
|
2015-01-26 22:04:29 +01:00
|
|
|
} else {
|
2015-01-27 21:01:52 +01:00
|
|
|
err = m_adac.openStream(¶ms, nullptr,
|
|
|
|
airtaudio::SINT16, m_frequency, &m_rtaudioFrameSize,
|
|
|
|
std::bind(&airtio::io::Node::rtAudioCallback,
|
|
|
|
this,
|
|
|
|
std::placeholders::_1,
|
|
|
|
std::placeholders::_2,
|
|
|
|
std::placeholders::_3,
|
|
|
|
std::placeholders::_4,
|
|
|
|
std::placeholders::_5)
|
|
|
|
);
|
2015-01-26 22:04:29 +01:00
|
|
|
}
|
|
|
|
if (err != airtaudio::errorNone) {
|
|
|
|
AIRTIO_ERROR("Create stream : '" << m_streamName << "' mode=" << (m_isInput?"input":"output") << " can not create strem " << err);
|
2015-01-25 22:17:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
airtio::io::Node::~Node() {
|
2015-01-26 22:04:29 +01:00
|
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
|
|
|
AIRTIO_INFO("-----------------------------------------------------------------");
|
2015-01-28 23:01:12 +01:00
|
|
|
AIRTIO_INFO("-- DESTROY NODE --");
|
2015-01-26 22:04:29 +01:00
|
|
|
AIRTIO_INFO("-----------------------------------------------------------------");
|
|
|
|
AIRTIO_INFO("close input stream");
|
2015-01-25 22:17:06 +01:00
|
|
|
if (m_adac.isStreamOpen() ) {
|
|
|
|
m_adac.closeStream();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void airtio::io::Node::start() {
|
2015-01-26 22:04:29 +01:00
|
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
|
|
|
AIRTIO_INFO("Start stream : '" << m_streamName << "' mode=" << (m_isInput?"input":"output") );
|
|
|
|
enum airtaudio::errorType err = m_adac.startStream();
|
|
|
|
if (err != airtaudio::errorNone) {
|
|
|
|
AIRTIO_ERROR("Start stream : '" << m_streamName << "' mode=" << (m_isInput?"input":"output") << " can not start stream ... " << err);
|
2015-01-25 22:17:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void airtio::io::Node::stop() {
|
2015-01-26 22:04:29 +01:00
|
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
|
|
|
AIRTIO_INFO("Stop stream : '" << m_streamName << "' mode=" << (m_isInput?"input":"output") );
|
|
|
|
enum airtaudio::errorType err = m_adac.stopStream();
|
|
|
|
if (err != airtaudio::errorNone) {
|
|
|
|
AIRTIO_ERROR("Stop stream : '" << m_streamName << "' mode=" << (m_isInput?"input":"output") << " can not stop stream ... " << err);
|
2015-01-25 22:17:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void airtio::io::Node::interfaceAdd(const std::shared_ptr<airtio::Interface>& _interface) {
|
2015-01-28 23:01:12 +01:00
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
|
|
|
for (size_t iii=0; iii< m_list.size(); ++iii) {
|
|
|
|
if (_interface == m_list[iii]) {
|
|
|
|
return;
|
|
|
|
}
|
2015-01-25 22:17:06 +01:00
|
|
|
}
|
2015-01-28 23:01:12 +01:00
|
|
|
AIRTIO_INFO("ADD interface for stream : '" << m_streamName << "' mode=" << (m_isInput?"input":"output") );
|
|
|
|
m_list.push_back(_interface);
|
2015-01-25 22:17:06 +01:00
|
|
|
}
|
2015-01-26 22:04:29 +01:00
|
|
|
if (m_list.size() == 1) {
|
|
|
|
start();
|
|
|
|
}
|
2015-01-25 22:17:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void airtio::io::Node::interfaceRemove(const std::shared_ptr<airtio::Interface>& _interface) {
|
2015-01-28 23:01:12 +01:00
|
|
|
{
|
|
|
|
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_streamName << "' mode=" << (m_isInput?"input":"output") );
|
|
|
|
break;
|
|
|
|
}
|
2015-01-25 22:17:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m_list.size() == 0) {
|
|
|
|
stop();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|