[DEV] start work on File node
This commit is contained in:
parent
c329af6863
commit
58ceff9371
272
audio/river/io/NodeFile.cpp
Normal file
272
audio/river/io/NodeFile.cpp
Normal file
@ -0,0 +1,272 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifdef AUDIO_RIVER_BUILD_FILE
|
||||
|
||||
#include <audio/river/io/NodeFile.h>
|
||||
#include <audio/river/debug.h>
|
||||
#include <etk/memory.h>
|
||||
|
||||
#undef __class__
|
||||
#define __class__ "io::NodeFile"
|
||||
|
||||
int32_t audio::river::io::NodeFile::recordCallback(const void* _inputBuffer,
|
||||
const audio::Time& _timeInput,
|
||||
uint32_t _nbChunk,
|
||||
const std::vector<audio::orchestra::status>& _status) {
|
||||
std11::unique_lock<std11::mutex> lock(m_mutex);
|
||||
// TODO : Manage status ...
|
||||
RIVER_VERBOSE("data Input size request :" << _nbChunk << " [BEGIN] status=" << _status << " nbIO=" << m_list.size());
|
||||
newInput(_inputBuffer, _nbChunk, _timeInput);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t audio::river::io::NodeFile::playbackCallback(void* _outputBuffer,
|
||||
const audio::Time& _timeOutput,
|
||||
uint32_t _nbChunk,
|
||||
const std::vector<audio::orchestra::status>& _status) {
|
||||
std11::unique_lock<std11::mutex> lock(m_mutex);
|
||||
// TODO : Manage status ...
|
||||
RIVER_VERBOSE("data Output size request :" << _nbChunk << " [BEGIN] status=" << _status << " nbIO=" << m_list.size());
|
||||
newOutput(_outputBuffer, _nbChunk, _timeOutput);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std11::shared_ptr<audio::river::io::NodeFile> audio::river::io::NodeFile::create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) {
|
||||
return std11::shared_ptr<audio::river::io::NodeFile>(new audio::river::io::NodeFile(_name, _config));
|
||||
}
|
||||
|
||||
audio::river::io::NodeFile::NodeFile(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config) :
|
||||
Node(_name, _config) {
|
||||
audio::drain::IOFormatInterface interfaceFormat = getInterfaceFormat();
|
||||
audio::drain::IOFormatInterface hardwareFormat = getHarwareFormat();
|
||||
/**
|
||||
map-on:{ # select hardware interface and name
|
||||
interface:"alsa", # interface : "alsa", "pulse", "core", ...
|
||||
name:"default", # name of the interface
|
||||
},
|
||||
nb-chunk:1024 # number of chunk to open device (create the latency anf the frequency to call user)
|
||||
*/
|
||||
std::string typeInterface = audio::orchestra::type_undefined;
|
||||
std::string streamName = "default";
|
||||
const std11::shared_ptr<const ejson::Object> tmpObject = m_config->getObject("map-on");
|
||||
if (tmpObject == nullptr) {
|
||||
RIVER_WARNING("missing node : 'map-on' ==> auto map : 'auto:default'");
|
||||
} else {
|
||||
typeInterface = tmpObject->getStringValue("interface", audio::orchestra::type_undefined);
|
||||
if (typeInterface == "auto") {
|
||||
typeInterface = audio::orchestra::type_undefined;
|
||||
}
|
||||
streamName = tmpObject->getStringValue("name", "default");
|
||||
}
|
||||
int32_t nbChunk = m_config->getNumberValue("nb-chunk", 1024);
|
||||
|
||||
// intanciate specific API ...
|
||||
m_interface.instanciate(typeInterface);
|
||||
m_interface.setName(_name);
|
||||
// TODO : Check return ...
|
||||
std::string type = m_config->getStringValue("type", "int16");
|
||||
if (streamName == "") {
|
||||
streamName = "default";
|
||||
}
|
||||
|
||||
// search device ID :
|
||||
RIVER_INFO("Open :");
|
||||
RIVER_INFO(" m_streamName=" << streamName);
|
||||
RIVER_INFO(" m_freq=" << hardwareFormat.getFrequency());
|
||||
RIVER_INFO(" m_map=" << hardwareFormat.getMap());
|
||||
RIVER_INFO(" m_format=" << hardwareFormat.getFormat());
|
||||
RIVER_INFO(" m_isInput=" << m_isInput);
|
||||
int32_t deviceId = -1;
|
||||
/*
|
||||
// TODO : Remove this from here (create an extern interface ...)
|
||||
RIVER_INFO("Device list:");
|
||||
for (int32_t iii=0; iii<m_interface.getDeviceCount(); ++iii) {
|
||||
m_info = m_interface.getDeviceInfo(iii);
|
||||
RIVER_INFO(" " << iii << " name :" << m_info.name);
|
||||
m_info.display(2);
|
||||
}
|
||||
*/
|
||||
// special case for default IO:
|
||||
if (streamName == "default") {
|
||||
if (m_isInput == true) {
|
||||
deviceId = m_interface.getDefaultInputDevice();
|
||||
} else {
|
||||
deviceId = m_interface.getDefaultOutputDevice();
|
||||
}
|
||||
} else {
|
||||
for (int32_t iii=0; iii<m_interface.getDeviceCount(); ++iii) {
|
||||
m_info = m_interface.getDeviceInfo(iii);
|
||||
if (m_info.name == streamName) {
|
||||
RIVER_INFO(" Select ... id =" << iii);
|
||||
deviceId = iii;
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO : Check if the devace with the specific name exist ...
|
||||
/*
|
||||
if (deviceId == -1) {
|
||||
RIVER_ERROR("Can not find the " << streamName << " audio interface ... (use O default ...)");
|
||||
deviceId = 0;
|
||||
}
|
||||
*/
|
||||
|
||||
// Open specific ID :
|
||||
if (deviceId == -1) {
|
||||
m_info = m_interface.getDeviceInfo(streamName);
|
||||
} else {
|
||||
m_info = m_interface.getDeviceInfo(deviceId);
|
||||
}
|
||||
// display property :
|
||||
{
|
||||
RIVER_INFO("Device " << deviceId << " - '" << streamName << "' property :");
|
||||
m_info.display();
|
||||
|
||||
if (etk::isIn(hardwareFormat.getFormat(), m_info.nativeFormats) == false) {
|
||||
if (type == "auto") {
|
||||
if (etk::isIn(audio::format_int16, m_info.nativeFormats) == true) {
|
||||
hardwareFormat.setFormat(audio::format_int16);
|
||||
RIVER_INFO("auto set format: " << hardwareFormat.getFormat());
|
||||
} else if (etk::isIn(audio::format_float, m_info.nativeFormats) == true) {
|
||||
hardwareFormat.setFormat(audio::format_float);
|
||||
RIVER_INFO("auto set format: " << hardwareFormat.getFormat());
|
||||
} else if (etk::isIn(audio::format_int16_on_int32, m_info.nativeFormats) == true) {
|
||||
hardwareFormat.setFormat(audio::format_int16_on_int32);
|
||||
RIVER_INFO("auto set format: " << hardwareFormat.getFormat());
|
||||
} else if (etk::isIn(audio::format_int24, m_info.nativeFormats) == true) {
|
||||
hardwareFormat.setFormat(audio::format_int24);
|
||||
RIVER_INFO("auto set format: " << hardwareFormat.getFormat());
|
||||
} else if (m_info.nativeFormats.size() != 0) {
|
||||
hardwareFormat.setFormat(m_info.nativeFormats[0]);
|
||||
RIVER_INFO("auto set format: " << hardwareFormat.getFormat());
|
||||
} else {
|
||||
RIVER_CRITICAL("auto set format no element in the configuration: " << m_info.nativeFormats);
|
||||
}
|
||||
} else {
|
||||
RIVER_CRITICAL("Can not manage input transforamtion: " << hardwareFormat.getFormat() << " not in " << m_info.nativeFormats);
|
||||
}
|
||||
}
|
||||
if (etk::isIn(hardwareFormat.getFrequency(), m_info.sampleRates) == false) {
|
||||
if (etk::isIn(48000, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(48000);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (etk::isIn(44100, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(44100);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (etk::isIn(32000, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(32000);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (etk::isIn(16000, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(16000);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (etk::isIn(8000, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(8000);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (etk::isIn(96000, m_info.sampleRates) == true) {
|
||||
hardwareFormat.setFrequency(96000);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency());
|
||||
} else if (m_info.sampleRates.size() != 0) {
|
||||
hardwareFormat.setFrequency(m_info.sampleRates[0]);
|
||||
RIVER_INFO("auto set frequency: " << hardwareFormat.getFrequency() << "(first element in list) in " << m_info.sampleRates);
|
||||
} else {
|
||||
RIVER_CRITICAL("Can not manage input transforamtion:" << hardwareFormat.getFrequency() << " not in " << m_info.sampleRates);
|
||||
}
|
||||
interfaceFormat.setFrequency(hardwareFormat.getFrequency());
|
||||
}
|
||||
}
|
||||
|
||||
// open Audio device:
|
||||
audio::orchestra::StreamParameters params;
|
||||
params.deviceId = deviceId;
|
||||
params.deviceName = streamName;
|
||||
params.nChannels = hardwareFormat.getMap().size();
|
||||
if (m_info.channels.size() < params.nChannels) {
|
||||
RIVER_CRITICAL("Can not open hardware device with more channel (" << params.nChannels << ") that is autorized by hardware (" << m_info.channels.size() << ").");
|
||||
}
|
||||
audio::orchestra::StreamOptions option;
|
||||
etk::from_string(option.mode, tmpObject->getStringValue("timestamp-mode", "soft"));
|
||||
|
||||
RIVER_DEBUG("interfaceFormat=" << interfaceFormat);
|
||||
RIVER_DEBUG("hardwareFormat=" << hardwareFormat);
|
||||
|
||||
m_rtaudioFrameSize = nbChunk;
|
||||
RIVER_INFO("Open output stream nbChannels=" << params.nChannels);
|
||||
enum audio::orchestra::error err = audio::orchestra::error_none;
|
||||
if (m_isInput == true) {
|
||||
m_process.setInputConfig(hardwareFormat);
|
||||
m_process.setOutputConfig(interfaceFormat);
|
||||
err = m_interface.openStream(nullptr, ¶ms,
|
||||
hardwareFormat.getFormat(), hardwareFormat.getFrequency(), &m_rtaudioFrameSize,
|
||||
std11::bind(&audio::river::io::NodeFile::recordCallback,
|
||||
this,
|
||||
std11::placeholders::_1,
|
||||
std11::placeholders::_2,
|
||||
std11::placeholders::_5,
|
||||
std11::placeholders::_6),
|
||||
option
|
||||
);
|
||||
} else {
|
||||
m_process.setInputConfig(interfaceFormat);
|
||||
m_process.setOutputConfig(hardwareFormat);
|
||||
err = m_interface.openStream(¶ms, nullptr,
|
||||
hardwareFormat.getFormat(), hardwareFormat.getFrequency(), &m_rtaudioFrameSize,
|
||||
std11::bind(&audio::river::io::NodeFile::playbackCallback,
|
||||
this,
|
||||
std11::placeholders::_3,
|
||||
std11::placeholders::_4,
|
||||
std11::placeholders::_5,
|
||||
std11::placeholders::_6),
|
||||
option
|
||||
);
|
||||
}
|
||||
if (err != audio::orchestra::error_none) {
|
||||
RIVER_ERROR("Create stream : '" << m_name << "' mode=" << (m_isInput?"input":"output") << " can not create stream " << err);
|
||||
}
|
||||
m_process.updateInterAlgo();
|
||||
}
|
||||
|
||||
audio::river::io::NodeFile::~NodeFile() {
|
||||
std11::unique_lock<std11::mutex> lock(m_mutex);
|
||||
RIVER_INFO("close input stream");
|
||||
if (m_interface.isStreamOpen() ) {
|
||||
m_interface.closeStream();
|
||||
}
|
||||
};
|
||||
|
||||
void audio::river::io::NodeFile::threadCallback() {
|
||||
etk::thread::setName("RIVER file-IO");
|
||||
// open the file
|
||||
|
||||
while (m_alive == true) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void audio::river::io::NodeFile::start() {
|
||||
std11::unique_lock<std11::mutex> lock(m_mutex);
|
||||
if (m_thread != nullptr) {
|
||||
RIVER_ERROR("Start stream : '" << m_name << "' mode=" << (m_isInput?"read":"write") << " ==> already started ..." );
|
||||
return;
|
||||
}
|
||||
m_alive = true;
|
||||
RIVER_INFO("Start stream : '" << m_name << "' mode=" << (m_isInput?"read":"write") );
|
||||
m_thread = std::make_shared<std::thread>(&audio::river::io::NodeFile::threadCallback2, this);
|
||||
m_time = audio::Time::now();
|
||||
}
|
||||
|
||||
void audio::river::io::NodeFile::stop() {
|
||||
std11::unique_lock<std11::mutex> lock(m_mutex);
|
||||
m_alive = false;
|
||||
RIVER_INFO("Stop stream : '" << m_name << "' mode=" << (m_isInput?"read":"write") );
|
||||
// TODO : Need join ...
|
||||
m_thread->join();
|
||||
m_thread.reset();
|
||||
}
|
||||
|
||||
#endif
|
59
audio/river/io/NodeFile.h
Normal file
59
audio/river/io/NodeFile.h
Normal file
@ -0,0 +1,59 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2015, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __AUDIO_RIVER_IO_NODE_FILE_H__
|
||||
#define __AUDIO_RIVER_IO_NODE_FILE_H__
|
||||
|
||||
#ifdef AUDIO_RIVER_BUILD_FILE
|
||||
|
||||
#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 NodeFile : public audio::river::io::Node {
|
||||
friend class audio::river::io::Group;
|
||||
protected:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
NodeFile(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
|
||||
public:
|
||||
static std11::shared_ptr<NodeFile> create(const std::string& _name, const std11::shared_ptr<const ejson::Object>& _config);
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~NodeFile();
|
||||
virtual bool isHarwareNode() {
|
||||
return true;
|
||||
};
|
||||
protected:
|
||||
audio::Time m_time; //!< time of the flow
|
||||
etk::FSNode m_file; //!< File interface
|
||||
bool m_restartAtEnd; //!< The read is done in loop
|
||||
uint32_t m_sampleRate; //!< Sample Rate of the Raw file
|
||||
audio::format m_format; //!< Format of the file
|
||||
std::vector<audio::channel> m_map; //!< Map of the file
|
||||
std::shared_ptr<std::thread> m_thread; //!< playing thread of the flow
|
||||
std::atomic<bool> m_alive; //!< thread is active
|
||||
protected:
|
||||
virtual void start();
|
||||
virtual void stop();
|
||||
virtual void threadCallback():
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -4,8 +4,8 @@
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __AUDIO_RIVER_IO_NODE_AIRTAUDIO_H__
|
||||
#define __AUDIO_RIVER_IO_NODE_AIRTAUDIO_H__
|
||||
#ifndef __AUDIO_RIVER_IO_NODE_ORCHESTRA_H__
|
||||
#define __AUDIO_RIVER_IO_NODE_ORCHESTRA_H__
|
||||
|
||||
#ifdef AUDIO_RIVER_BUILD_ORCHESTRA
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user