[DEV] rework and correct timestamp of Alsa (pase 1)

This commit is contained in:
Edouard DUPIN 2015-02-17 21:08:15 +01:00
parent b72d6f31df
commit 24fef86124
16 changed files with 225 additions and 259 deletions

View File

@ -43,6 +43,7 @@ const std::vector<uint32_t>& airtaudio::genericSampleRate() {
airtaudio::Api::Api() : airtaudio::Api::Api() :
m_callback(nullptr),
m_deviceBuffer(nullptr) { m_deviceBuffer(nullptr) {
m_device[0] = 11111; m_device[0] = 11111;
m_device[1] = 11111; m_device[1] = 11111;
@ -138,7 +139,7 @@ enum airtaudio::error airtaudio::Api::openStream(airtaudio::StreamParameters *oP
return airtaudio::error_systemError; return airtaudio::error_systemError;
} }
} }
m_callbackInfo.callback = callback; m_callback = callback;
if (options != nullptr) { if (options != nullptr) {
options->numberOfBuffers = m_nBuffers; options->numberOfBuffers = m_nBuffers;
} }
@ -175,7 +176,9 @@ bool airtaudio::Api::probeDeviceOpen(uint32_t /*device*/,
} }
void airtaudio::Api::tickStreamTime() { void airtaudio::Api::tickStreamTime() {
m_duration += std::chrono::duration<int64_t, std::micro>((m_bufferSize * 1000000) / m_sampleRate); //ATA_WARNING("tick : size=" << m_bufferSize << " rate=" << m_sampleRate << " time=" << std::chrono::nanoseconds((int64_t(m_bufferSize) * int64_t(1000000000)) / int64_t(m_sampleRate)).count());
//ATA_WARNING(" one element=" << std::chrono::nanoseconds((int64_t(1000000000)) / int64_t(m_sampleRate)).count());
m_duration += std::chrono::nanoseconds((int64_t(m_bufferSize) * int64_t(1000000000)) / int64_t(m_sampleRate));
} }
long airtaudio::Api::getStreamLatency() { long airtaudio::Api::getStreamLatency() {
@ -224,10 +227,9 @@ void airtaudio::Api::clearStreamInfo() {
m_nBuffers = 0; m_nBuffers = 0;
m_userFormat = audio::format_unknow; m_userFormat = audio::format_unknow;
m_startTime = std::chrono::system_clock::time_point(); m_startTime = std::chrono::system_clock::time_point();
m_duration = std::chrono::duration<int64_t, std::micro>(0); m_duration = std::chrono::nanoseconds(0);
m_deviceBuffer = nullptr; m_deviceBuffer = nullptr;
m_callbackInfo.callback = 0; m_callback = nullptr;
m_callbackInfo.isRunning = false;
for (int32_t iii=0; iii<2; ++iii) { for (int32_t iii=0; iii<2; ++iii) {
m_device[iii] = 11111; m_device[iii] = 11111;
m_doConvertBuffer[iii] = false; m_doConvertBuffer[iii] = false;

View File

@ -18,6 +18,22 @@
namespace airtaudio { namespace airtaudio {
const std::vector<uint32_t>& genericSampleRate(); const std::vector<uint32_t>& genericSampleRate();
/**
* @brief airtaudio callback function prototype.
* @param _inputBuffer For input (or duplex) streams, this buffer will hold _nbChunk of input audio chunk (nullptr if no data).
* @param _timeInput Timestamp of the first buffer sample (recording time).
* @param _outputBuffer For output (or duplex) streams, the client should write _nbChunk of audio chunk into this buffer (nullptr if no data).
* @param _timeOutput Timestamp of the first buffer sample (playing time).
* @param _nbChunk The number of chunk of input or output chunk in the buffer (same size).
* @param _status List of error that occured in the laps of time.
*/
typedef std::function<int32_t (const void* _inputBuffer,
const std::chrono::system_clock::time_point& _timeInput,
void* _outputBuffer,
const std::chrono::system_clock::time_point& _timeOutput,
uint32_t _nbChunk,
const std::vector<airtaudio::status>& _status)> AirTAudioCallback;
// A protected structure used for buffer conversion. // A protected structure used for buffer conversion.
class ConvertInfo { class ConvertInfo {
public: public:
@ -31,9 +47,14 @@ namespace airtaudio {
}; };
class Api { class Api {
protected:
std::string m_name;
public: public:
Api(); Api();
virtual ~Api(); virtual ~Api();
void setName(const std::string& _name) {
m_name = _name;
}
virtual airtaudio::type getCurrentApi() = 0; virtual airtaudio::type getCurrentApi() = 0;
virtual uint32_t getDeviceCount() = 0; virtual uint32_t getDeviceCount() = 0;
virtual airtaudio::DeviceInfo getDeviceInfo(uint32_t _device) = 0; virtual airtaudio::DeviceInfo getDeviceInfo(uint32_t _device) = 0;
@ -43,7 +64,7 @@ namespace airtaudio {
airtaudio::StreamParameters* _inputParameters, airtaudio::StreamParameters* _inputParameters,
audio::format _format, audio::format _format,
uint32_t _sampleRate, uint32_t _sampleRate,
uint32_t* _bufferFrames, uint32_t* _nbChunk,
airtaudio::AirTAudioCallback _callback, airtaudio::AirTAudioCallback _callback,
airtaudio::StreamOptions* _options); airtaudio::StreamOptions* _options);
virtual enum airtaudio::error closeStream(); virtual enum airtaudio::error closeStream();
@ -62,6 +83,7 @@ namespace airtaudio {
protected: protected:
mutable std::mutex m_mutex; mutable std::mutex m_mutex;
airtaudio::AirTAudioCallback m_callback;
uint32_t m_device[2]; // Playback and record, respectively. uint32_t m_device[2]; // Playback and record, respectively.
enum airtaudio::mode m_mode; // airtaudio::mode_output, airtaudio::mode_input, or airtaudio::mode_duplex. enum airtaudio::mode m_mode; // airtaudio::mode_output, airtaudio::mode_input, or airtaudio::mode_duplex.
enum airtaudio::state m_state; // STOPPED, RUNNING, or CLOSED enum airtaudio::state m_state; // STOPPED, RUNNING, or CLOSED
@ -70,22 +92,20 @@ namespace airtaudio {
bool m_doConvertBuffer[2]; // Playback and record, respectively. bool m_doConvertBuffer[2]; // Playback and record, respectively.
bool m_deviceInterleaved[2]; // Playback and record, respectively. bool m_deviceInterleaved[2]; // Playback and record, respectively.
bool m_doByteSwap[2]; // Playback and record, respectively. bool m_doByteSwap[2]; // Playback and record, respectively.
uint32_t m_sampleRate; uint32_t m_sampleRate; // TODO : Rename frequency
uint32_t m_bufferSize; uint32_t m_bufferSize;
uint32_t m_nBuffers; uint32_t m_nBuffers;
uint32_t m_nUserChannels[2]; // Playback and record, respectively. uint32_t m_nUserChannels[2]; // Playback and record, respectively. // TODO : set only one config (open inout with the same number of channels (limitation)
uint32_t m_nDeviceChannels[2]; // Playback and record channels, respectively. uint32_t m_nDeviceChannels[2]; // Playback and record channels, respectively.
uint32_t m_channelOffset[2]; // Playback and record, respectively. uint32_t m_channelOffset[2]; // Playback and record, respectively.
uint64_t m_latency[2]; // Playback and record, respectively. uint64_t m_latency[2]; // Playback and record, respectively.
enum audio::format m_userFormat; // TODO : Remove this ==> use can only open in the Harware format ... enum audio::format m_userFormat; // TODO : Remove this ==> use can only open in the Harware format ...
enum audio::format m_deviceFormat[2]; // Playback and record, respectively. enum audio::format m_deviceFormat[2]; // Playback and record, respectively.
// TODO : Remove this ...
airtaudio::CallbackInfo m_callbackInfo;
airtaudio::ConvertInfo m_convertInfo[2]; airtaudio::ConvertInfo m_convertInfo[2];
//std::chrono::system_clock::time_point //std::chrono::system_clock::time_point
std::chrono::time_point<std::chrono::system_clock> m_startTime; //!< start time of the stream (restart at every stop, pause ...) std::chrono::system_clock::time_point m_startTime; //!< start time of the stream (restart at every stop, pause ...)
std::chrono::duration<int64_t, std::micro> m_duration; //!< duration from wich the stream is started std::chrono::nanoseconds m_duration; //!< duration from wich the stream is started
/** /**
* @brief api-specific method that attempts to open a device * @brief api-specific method that attempts to open a device

View File

@ -1,41 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
* @fork from RTAudio
*/
#ifndef __AIRTAUDIO_CALLBACK_INFO_H__
#define __AIRTAUDIO_CALLBACK_INFO_H__
#include <thread>
#include <functional>
#include <airtaudio/base.h>
namespace airtaudio {
// This global structure type is used to pass callback information
// between the private RtAudio stream structure and global callback
// handling functions.
class CallbackInfo {
public:
std::thread* thread;
airtaudio::AirTAudioCallback callback;
void* apiInfo; // void pointer for API specific callback information
bool isRunning;
bool doRealtime;
int32_t priority;
// Default constructor.
CallbackInfo() :
callback(nullptr),
apiInfo(nullptr),
isRunning(false),
doRealtime(false) {
}
};
};
#endif

View File

@ -40,11 +40,12 @@ namespace airtaudio {
protected: protected:
airtaudio::Api *m_rtapi; airtaudio::Api *m_rtapi;
public: public:
/** void setName(const std::string& _name) {
* @brief A static function to determine the current airtaudio version. if (m_rtapi == nullptr) {
*/
static std::string getVersion() { return;
return "4.0.12"; }
m_rtapi->setName(_name);
} }
/** /**
* @brief A static function to determine the available compiled audio APIs. * @brief A static function to determine the available compiled audio APIs.

View File

@ -30,14 +30,17 @@ namespace airtaudio {
bool xrun[2]; bool xrun[2];
std::condition_variable runnable_cv; std::condition_variable runnable_cv;
bool runnable; bool runnable;
std::unique_ptr<std::thread> thread;
bool threadRunning;
AlsaPrivate() : AlsaPrivate() :
synchronized(false), synchronized(false),
runnable(false) { runnable(false),
threadRunning(false) {
handles[0] = nullptr; handles[0] = nullptr;
handles[1] = nullptr; handles[1] = nullptr;
xrun[0] = false; xrun[0] = false;
xrun[1] = false; xrun[1] = false;
// TODO : Wait thread ...
} }
}; };
}; };
@ -692,10 +695,11 @@ foundDevice:
} else { } else {
m_mode = _mode; m_mode = _mode;
// Setup callback thread. // Setup callback thread.
m_callbackInfo.isRunning = true; m_private->threadRunning = true;
m_callbackInfo.thread = new std::thread(&airtaudio::api::Alsa::alsaCallbackEvent, this); std::unique_ptr<std::thread> tmpThread(new std::thread(&airtaudio::api::Alsa::alsaCallbackEvent, this));
if (m_callbackInfo.thread == nullptr) { m_private->thread = std::move(tmpThread);
m_callbackInfo.isRunning = false; if (m_private->thread == nullptr) {
m_private->threadRunning = false;
ATA_ERROR("creating callback thread!"); ATA_ERROR("creating callback thread!");
goto error; goto error;
} }
@ -729,15 +733,15 @@ enum airtaudio::error airtaudio::api::Alsa::closeStream() {
ATA_ERROR("no open stream to close!"); ATA_ERROR("no open stream to close!");
return airtaudio::error_warning; return airtaudio::error_warning;
} }
m_callbackInfo.isRunning = false; m_private->threadRunning = false;
m_mutex.lock(); m_mutex.lock();
if (m_state == airtaudio::state_stopped) { if (m_state == airtaudio::state_stopped) {
m_private->runnable = true; m_private->runnable = true;
m_private->runnable_cv.notify_one(); m_private->runnable_cv.notify_one();
} }
m_mutex.unlock(); m_mutex.unlock();
if (m_callbackInfo.thread != nullptr) { if (m_private->thread != nullptr) {
m_callbackInfo.thread->join(); m_private->thread->join();
} }
if (m_state == airtaudio::state_running) { if (m_state == airtaudio::state_running) {
m_state = airtaudio::state_stopped; m_state = airtaudio::state_stopped;
@ -909,18 +913,37 @@ unlock:
void airtaudio::api::Alsa::alsaCallbackEvent(void *_userData) { void airtaudio::api::Alsa::alsaCallbackEvent(void *_userData) {
etk::log::setThreadName("Alsa IO");
airtaudio::api::Alsa* myClass = reinterpret_cast<airtaudio::api::Alsa*>(_userData); airtaudio::api::Alsa* myClass = reinterpret_cast<airtaudio::api::Alsa*>(_userData);
myClass->callbackEvent(); myClass->callbackEvent();
} }
void airtaudio::api::Alsa::callbackEvent() { void airtaudio::api::Alsa::callbackEvent() {
while (m_callbackInfo.isRunning == true) { etk::log::setThreadName("Alsa IO-" + m_name);
while (m_private->threadRunning == true) {
callbackEventOneCycle(); callbackEventOneCycle();
} }
} }
namespace std {
static std::ostream& operator <<(std::ostream& _os, const std::chrono::system_clock::time_point& _obj) {
std::chrono::nanoseconds ns = std::chrono::duration_cast<std::chrono::nanoseconds>(_obj.time_since_epoch());
int64_t totalSecond = ns.count()/1000000000;
int64_t millisecond = (ns.count()%1000000000)/1000000;
int64_t microsecond = (ns.count()%1000000)/1000;
int64_t nanosecond = ns.count()%1000;
//_os << totalSecond << "s " << millisecond << "ms " << microsecond << "µs " << nanosecond << "ns";
int32_t second = totalSecond % 60;
int32_t minute = (totalSecond/60)%60;
int32_t hour = (totalSecond/3600)%24;
int32_t day = (totalSecond/(24*3600))%365;
int32_t year = totalSecond/(24*3600*365);
_os << year << "y " << day << "d " << hour << "h" << minute << ":"<< second << "s " << millisecond << "ms " << microsecond << "µs " << nanosecond << "ns";
return _os;
}
}
std::chrono::time_point<std::chrono::system_clock> airtaudio::api::Alsa::getStreamTime() { std::chrono::time_point<std::chrono::system_clock> airtaudio::api::Alsa::getStreamTime() {
if (m_startTime == std::chrono::system_clock::time_point()) {
snd_pcm_uframes_t avail; snd_pcm_uframes_t avail;
snd_htimestamp_t tstamp; snd_htimestamp_t tstamp;
if (m_private->handles[0] != nullptr) { if (m_private->handles[0] != nullptr) {
@ -929,7 +952,12 @@ std::chrono::time_point<std::chrono::system_clock> airtaudio::api::Alsa::getStre
int plop = snd_pcm_htimestamp(m_private->handles[1], &avail, &tstamp); int plop = snd_pcm_htimestamp(m_private->handles[1], &avail, &tstamp);
} }
//ATA_WARNING("plop : " << tstamp.tv_sec << " sec " << tstamp.tv_nsec); //ATA_WARNING("plop : " << tstamp.tv_sec << " sec " << tstamp.tv_nsec);
return std::chrono::system_clock::from_time_t(tstamp.tv_sec) + std::chrono::nanoseconds(tstamp.tv_nsec); //return std::chrono::system_clock::from_time_t(tstamp.tv_sec) + std::chrono::nanoseconds(tstamp.tv_nsec);
//m_startTime = std::chrono::system_clock::from_time_t(tstamp.tv_sec) + std::chrono::nanoseconds(tstamp.tv_nsec);
//m_duration = std::chrono::microseconds(0);
}
ATA_DEBUG(" createTimeStamp : " << m_startTime + m_duration);
return m_startTime + m_duration;
} }
@ -952,21 +980,22 @@ void airtaudio::api::Alsa::callbackEventOneCycle() {
} }
int32_t doStopStream = 0; int32_t doStopStream = 0;
std::chrono::system_clock::time_point streamTime = getStreamTime(); std::chrono::system_clock::time_point streamTime = getStreamTime();
enum airtaudio::status status = airtaudio::status_ok; std::vector<enum airtaudio::status> status;
if ( m_mode != airtaudio::mode_input if ( m_mode != airtaudio::mode_input
&& m_private->xrun[0] == true) { && m_private->xrun[0] == true) {
status = airtaudio::status_underflow; status.push_back(airtaudio::status_underflow);
m_private->xrun[0] = false; m_private->xrun[0] = false;
} }
if ( m_mode != airtaudio::mode_output if ( m_mode != airtaudio::mode_output
&& m_private->xrun[1] == true) { && m_private->xrun[1] == true) {
status = airtaudio::status_overflow; status.push_back(airtaudio::status_overflow);
m_private->xrun[1] = false; m_private->xrun[1] = false;
} }
doStopStream = m_callbackInfo.callback(&m_userBuffer[0][0], doStopStream = m_callback(&m_userBuffer[1][0],
&m_userBuffer[1][0],
m_bufferSize,
streamTime, streamTime,
&m_userBuffer[0][0],
streamTime,
m_bufferSize,
status); status);
if (doStopStream == 2) { if (doStopStream == 2) {
abortStream(); abortStream();

View File

@ -46,7 +46,7 @@ namespace airtaudio {
enum audio::format _format, enum audio::format _format,
uint32_t *_bufferSize, uint32_t *_bufferSize,
airtaudio::StreamOptions *_options); airtaudio::StreamOptions *_options);
virtual std::chrono::time_point<std::chrono::system_clock> getStreamTime(); virtual std::chrono::system_clock::time_point getStreamTime();
}; };
}; };
}; };

View File

@ -111,19 +111,21 @@ void airtaudio::api::Android::callBackEvent(void* _data,
int32_t _frameRate) { int32_t _frameRate) {
int32_t doStopStream = 0; int32_t doStopStream = 0;
std::chrono::system_clock::time_point streamTime = getStreamTime(); std::chrono::system_clock::time_point streamTime = getStreamTime();
enum airtaudio::status status = airtaudio::status_ok; std::vector<enum airtaudio::status> status;
if (m_doConvertBuffer[airtaudio::mode_output] == true) { if (m_doConvertBuffer[airtaudio::mode_output] == true) {
doStopStream = m_callbackInfo.callback(m_userBuffer[airtaudio::mode_output], doStopStream = m_callback(nullptr,
nullptr, std::chrono::system_clock::time_point(),
_frameRate, m_userBuffer[airtaudio::mode_output],
streamTime, streamTime,
_frameRate,
status); status);
convertBuffer((char*)_data, (char*)m_userBuffer[airtaudio::mode_output], m_convertInfo[airtaudio::mode_output]); convertBuffer((char*)_data, (char*)m_userBuffer[airtaudio::mode_output], m_convertInfo[airtaudio::mode_output]);
} else { } else {
doStopStream = m_callbackInfo.callback(_data, doStopStream = m_callback(_data,
nullptr,
_frameRate,
streamTime, streamTime,
nullptr,
std::chrono::system_clock::time_point(),
_frameRate,
status); status);
} }
if (doStopStream == 2) { if (doStopStream == 2) {

View File

@ -502,9 +502,7 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device,
m_sampleRate = _sampleRate; m_sampleRate = _sampleRate;
m_device[modeToIdTable(_mode)] = _device; m_device[modeToIdTable(_mode)] = _device;
m_state = airtaudio::state_stopped; m_state = airtaudio::state_stopped;
asioCallbackInfo = &m_callbackInfo; if ( _mode == airtaudio::mode_output
m_callbackInfo.object = (void*)this;
if ( m_mode == airtaudio::mode_output
&& _mode == airtaudio::mode_input) { && _mode == airtaudio::mode_input) {
// We had already set up an output stream. // We had already set up an output stream.
m_mode = airtaudio::mode_duplex; m_mode = airtaudio::mode_duplex;
@ -695,19 +693,20 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) {
// draining stream. // draining stream.
if (m_private->drainCounter == 0) { if (m_private->drainCounter == 0) {
std::chrono::system_clock::time_point streamTime = getStreamTime(); std::chrono::system_clock::time_point streamTime = getStreamTime();
rtaudio::streamStatus status = 0; std::vector<enum airtaudio::status status;
if (m_mode != airtaudio::mode_input && asioXRun == true) { if (m_mode != airtaudio::mode_input && asioXRun == true) {
status |= RTAUDIO_airtaudio::status_underflow; status.push_back(airtaudio::status_underflow);
asioXRun = false; asioXRun = false;
} }
if (m_mode != airtaudio::mode_output && asioXRun == true) { if (m_mode != airtaudio::mode_output && asioXRun == true) {
status |= RTAUDIO_airtaudio::mode_input_OVERFLOW; status.push_back(airtaudio::status_underflow;
asioXRun = false; asioXRun = false;
} }
int32_t cbReturnValue = info->callback(m_userBuffer[0], int32_t cbReturnValue = info->callback(m_userBuffer[1],
m_userBuffer[1],
m_bufferSize,
streamTime, streamTime,
m_userBuffer[0],
streamTime,
m_bufferSize,
status); status);
if (cbReturnValue == 2) { if (cbReturnValue == 2) {
m_state = airtaudio::state_stopping; m_state = airtaudio::state_stopping;

View File

@ -1011,7 +1011,9 @@ void airtaudio::api::Core::coreStopStream(void *_userData) {
bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId, bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
const AudioBufferList *_inBufferList, const AudioBufferList *_inBufferList,
const AudioBufferList *_outBufferList) { const std::chrono::system_clock::time_point& _inTime,
const AudioBufferList *_outBufferList,
const std::chrono::system_clock::time_point& _outTime) {
if ( m_state == airtaudio::state_stopped if ( m_state == airtaudio::state_stopped
|| m_state == airtaudio::state_stopping) { || m_state == airtaudio::state_stopping) {
return true; return true;
@ -1020,7 +1022,6 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
ATA_ERROR("the stream is closed ... this shouldn't happen!"); ATA_ERROR("the stream is closed ... this shouldn't happen!");
return false; return false;
} }
CallbackInfo *info = (CallbackInfo *) &m_callbackInfo;
// Check if we were draining the stream and signal is finished. // Check if we were draining the stream and signal is finished.
if (m_private->drainCounter > 3) { if (m_private->drainCounter > 3) {
m_state = airtaudio::state_stopping; m_state = airtaudio::state_stopping;
@ -1038,22 +1039,22 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
// draining stream or duplex mode AND the input/output devices are // draining stream or duplex mode AND the input/output devices are
// different AND this function is called for the input device. // different AND this function is called for the input device.
if (m_private->drainCounter == 0 && (m_mode != airtaudio::mode_duplex || _deviceId == outputDevice)) { if (m_private->drainCounter == 0 && (m_mode != airtaudio::mode_duplex || _deviceId == outputDevice)) {
std::chrono::system_clock::time_point streamTime = getStreamTime(); std::vector<enum airtaudio::status> status;
enum airtaudio::status status = airtaudio::status_ok;
if ( m_mode != airtaudio::mode_input if ( m_mode != airtaudio::mode_input
&& m_private->xrun[0] == true) { && m_private->xrun[0] == true) {
status = airtaudio::status_underflow; status.push_back(airtaudio::status_underflow);
m_private->xrun[0] = false; m_private->xrun[0] = false;
} }
if ( m_mode != airtaudio::mode_output if ( m_mode != airtaudio::mode_output
&& m_private->xrun[1] == true) { && m_private->xrun[1] == true) {
status = airtaudio::status_overflow; status.push_back(airtaudio::status_overflow);
m_private->xrun[1] = false; m_private->xrun[1] = false;
} }
int32_t cbReturnValue = info->callback(&m_userBuffer[0][0], int32_t cbReturnValue = m_callback(&m_userBuffer[1][0],
&m_userBuffer[1][0], _inTime,
&m_userBuffer[0][0],
_outTime,
m_bufferSize, m_bufferSize,
streamTime,
status); status);
if (cbReturnValue == 2) { if (cbReturnValue == 2) {
m_state = airtaudio::state_stopping; m_state = airtaudio::state_stopping;

View File

@ -31,13 +31,11 @@ namespace airtaudio {
enum airtaudio::error stopStream(); enum airtaudio::error stopStream();
enum airtaudio::error abortStream(); enum airtaudio::error abortStream();
long getStreamLatency(); long getStreamLatency();
// This function is intended for internal use only. It must be
// public because it is called by the internal callback handler,
// which is not a member of RtAudio. External use of this function
// will most likely produce highly undesireable results!
bool callbackEvent(AudioDeviceID _deviceId, bool callbackEvent(AudioDeviceID _deviceId,
const AudioBufferList *_inBufferList, const AudioBufferList *_inBufferList,
const AudioBufferList *_outBufferList); const std::chrono::system_clock::time_point& _inTime,
const AudioBufferList *_outBufferList,
const std::chrono::system_clock::time_point& _outTime);
static OSStatus callbackEvent(AudioDeviceID _inDevice, static OSStatus callbackEvent(AudioDeviceID _inDevice,
const AudioTimeStamp* _inNow, const AudioTimeStamp* _inNow,
const AudioBufferList* _inInputData, const AudioBufferList* _inInputData,
@ -57,7 +55,6 @@ namespace airtaudio {
uint32_t *_bufferSize, uint32_t *_bufferSize,
airtaudio::StreamOptions *_options); airtaudio::StreamOptions *_options);
static const char* getErrorCode(OSStatus _code); static const char* getErrorCode(OSStatus _code);
static OSStatus xrunListener(AudioObjectID _inDevice, static OSStatus xrunListener(AudioObjectID _inDevice,
uint32_t _nAddresses, uint32_t _nAddresses,
const AudioObjectPropertyAddress _properties[], const AudioObjectPropertyAddress _properties[],

View File

@ -111,7 +111,7 @@ enum airtaudio::error airtaudio::api::CoreIos::abortStream(void) {
} }
void airtaudio::api::CoreIos::callBackEvent(void* _data, void airtaudio::api::CoreIos::callBackEvent(void* _data,
int32_t _frameRate) { int32_t _nbChunk) {
#if 0 #if 0
static double value=0; static double value=0;
@ -128,19 +128,21 @@ void airtaudio::api::CoreIos::callBackEvent(void* _data,
#endif #endif
int32_t doStopStream = 0; int32_t doStopStream = 0;
std::chrono::system_clock::time_point streamTime = getStreamTime(); std::chrono::system_clock::time_point streamTime = getStreamTime();
enum airtaudio::status status = airtaudio::status_ok; std::vector<enum airtaudio::status> status;
if (m_doConvertBuffer[modeToIdTable(airtaudio::mode_output)] == true) { if (m_doConvertBuffer[modeToIdTable(airtaudio::mode_output)] == true) {
doStopStream = m_callbackInfo.callback(&m_userBuffer[modeToIdTable(airtaudio::mode_output)][0], doStopStream = m_callback(nullptr,
nullptr,
_frameRate,
streamTime, streamTime,
&m_userBuffer[modeToIdTable(airtaudio::mode_output)][0],
streamTime,
_nbChunk,
status); status);
convertBuffer((char*)_data, &m_userBuffer[modeToIdTable(airtaudio::mode_output)][0], m_convertInfo[modeToIdTable(airtaudio::mode_output)]); convertBuffer((char*)_data, &m_userBuffer[modeToIdTable(airtaudio::mode_output)][0], m_convertInfo[modeToIdTable(airtaudio::mode_output)]);
} else { } else {
doStopStream = m_callbackInfo.callback(_data, doStopStream = m_callback(_data,
nullptr,
_frameRate,
streamTime, streamTime,
nullptr,
streamTime,
_nbChunk,
status); status);
} }
if (doStopStream == 2) { if (doStopStream == 2) {

View File

@ -74,6 +74,8 @@ namespace airtaudio {
namespace api { namespace api {
class DsPrivate { class DsPrivate {
public: public:
std::unique_ptr<std::thread> thread;
bool threadRunning;
uint32_t drainCounter; // Tracks callback counts when draining uint32_t drainCounter; // Tracks callback counts when draining
bool internalDrain; // Indicates if stop is initiated from callback or not. bool internalDrain; // Indicates if stop is initiated from callback or not.
void *id[2]; void *id[2];
@ -85,6 +87,7 @@ namespace airtaudio {
HANDLE condition; HANDLE condition;
std::vector<DsDevice> dsDevices; std::vector<DsDevice> dsDevices;
DsPrivate() : DsPrivate() :
threadRunning(false),
drainCounter(0), drainCounter(0),
internalDrain(false) { internalDrain(false) {
id[0] = 0; id[0] = 0;
@ -738,15 +741,16 @@ bool airtaudio::api::Ds::probeDeviceOpen(uint32_t _device,
setConvertInfo(_mode, _firstChannel); setConvertInfo(_mode, _firstChannel);
} }
// Setup the callback thread. // Setup the callback thread.
if (m_callbackInfo.isRunning == false) { if (m_private->threadRunning == false) {
m_callbackInfo.isRunning = true; m_private->threadRunning = true;
m_callbackInfo.thread = new std::thread(&airtaudio::api::Ds::dsCallbackEvent, this); std::unique_ptr<std::thread> tmpThread(new std::thread(&airtaudio::api::Ds::dsCallbackEvent, this));
if (m_callbackInfo.thread == nullptr) { m_private->thread = std::move(tmpThread);
if (m_private->thread == nullptr) {
ATA_ERROR("error creating callback thread!"); ATA_ERROR("error creating callback thread!");
goto error; goto error;
} }
// Boost DS thread priority // Boost DS thread priority
SetThreadPriority((HANDLE)m_callbackInfo.thread, THREAD_PRIORITY_HIGHEST); SetThreadPriority((HANDLE)m_private->thread, THREAD_PRIORITY_HIGHEST);
} }
return true; return true;
error: error:
@ -784,9 +788,9 @@ enum airtaudio::error airtaudio::api::Ds::closeStream() {
return airtaudio::error_warning; return airtaudio::error_warning;
} }
// Stop the callback thread. // Stop the callback thread.
m_callbackInfo.isRunning = false; m_private->threadRunning = false;
WaitForSingleObject((HANDLE) m_callbackInfo.thread, INFINITE); WaitForSingleObject((HANDLE) m_private->thread, INFINITE);
CloseHandle((HANDLE) m_callbackInfo.thread); CloseHandle((HANDLE) m_private->thread);
if (m_private->buffer[0]) { // the object pointer can be nullptr and valid if (m_private->buffer[0]) { // the object pointer can be nullptr and valid
LPDIRECTSOUND object = (LPDIRECTSOUND) m_private->id[0]; LPDIRECTSOUND object = (LPDIRECTSOUND) m_private->id[0];
LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0]; LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0];
@ -968,7 +972,6 @@ void airtaudio::api::Ds::callbackEvent() {
ATA_ERROR("the stream is closed ... this shouldn't happen!"); ATA_ERROR("the stream is closed ... this shouldn't happen!");
return; return;
} }
CallbackInfo *info = (CallbackInfo *) &m_callbackInfo;
// Check if we were draining the stream and signal is finished. // Check if we were draining the stream and signal is finished.
if (m_private->drainCounter > m_nBuffers + 2) { if (m_private->drainCounter > m_nBuffers + 2) {
m_state = airtaudio::state_stopping; m_state = airtaudio::state_stopping;
@ -994,10 +997,11 @@ void airtaudio::api::Ds::callbackEvent() {
status = airtaudio::status_overflow; status = airtaudio::status_overflow;
m_private->xrun[1] = false; m_private->xrun[1] = false;
} }
int32_t cbReturnValue = info->callback(&m_userBuffer[0][0], int32_t cbReturnValue = info->callback(&m_userBuffer[1][0],
&m_userBuffer[1][0],
m_bufferSize,
streamTime, streamTime,
&m_userBuffer[0][0],
streamTime,
m_bufferSize,
status); status);
if (cbReturnValue == 2) { if (cbReturnValue == 2) {
m_state = airtaudio::state_stopping; m_state = airtaudio::state_stopping;
@ -1257,7 +1261,7 @@ void airtaudio::api::Ds::callbackEvent() {
} }
} else { // _mode == airtaudio::mode_input } else { // _mode == airtaudio::mode_input
while ( safeReadPointer < endRead while ( safeReadPointer < endRead
&& m_callbackInfo.isRunning) { && m_private->threadRunning) {
// See comments for playback. // See comments for playback.
double millis = (endRead - safeReadPointer) * 1000.0; double millis = (endRead - safeReadPointer) * 1000.0;
millis /= (audio::getFormatBytes(m_deviceFormat[1]) * m_nDeviceChannels[1] * m_sampleRate); millis /= (audio::getFormatBytes(m_deviceFormat[1]) * m_nDeviceChannels[1] * m_sampleRate);
@ -1327,9 +1331,9 @@ unlock:
} }
void airtaudio::api::Ds::dsCallbackEvent(void *_userData) { void airtaudio::api::Ds::dsCallbackEvent(void *_userData) {
etk::log::setThreadName("DS IO"); etk::log::setThreadName("DS IO-" + m_name);
airtaudio::api::Ds* myClass = reinterpret_cast<airtaudio::api::Ds*>(_userData); airtaudio::api::Ds* myClass = reinterpret_cast<airtaudio::api::Ds*>(_userData);
while (myClass->m_callbackInfo.isRunning == true) { while (myClass->m_private->threadRunning == true) {
myClass->callbackEvent(); myClass->callbackEvent();
} }
} }

View File

@ -654,19 +654,20 @@ bool airtaudio::api::Jack::callbackEvent(uint64_t _nframes) {
// Invoke user callback first, to get fresh output data. // Invoke user callback first, to get fresh output data.
if (m_private->drainCounter == 0) { if (m_private->drainCounter == 0) {
std::chrono::time_point<std::chrono::system_clock> streamTime = getStreamTime(); std::chrono::time_point<std::chrono::system_clock> streamTime = getStreamTime();
enum airtaudio::status status = airtaudio::status_ok; std::vector<enum airtaudio::status> status;
if (m_mode != airtaudio::mode_input && m_private->xrun[0] == true) { if (m_mode != airtaudio::mode_input && m_private->xrun[0] == true) {
status = airtaudio::status_underflow; status.push_back(airtaudio::status_underflow);
m_private->xrun[0] = false; m_private->xrun[0] = false;
} }
if (m_mode != airtaudio::mode_output && m_private->xrun[1] == true) { if (m_mode != airtaudio::mode_output && m_private->xrun[1] == true) {
status = airtaudio::status_overflow; status.push_back(airtaudio::status_overflow);
m_private->xrun[1] = false; m_private->xrun[1] = false;
} }
int32_t cbReturnValue = m_callbackInfo.callback(&m_userBuffer[0][0], int32_t cbReturnValue = m_callback(&m_userBuffer[1][0],
&m_userBuffer[1][0],
m_bufferSize,
streamTime, streamTime,
&m_userBuffer[0][0],
streamTime,
m_bufferSize,
status); status);
if (cbReturnValue == 2) { if (cbReturnValue == 2) {
m_state = airtaudio::state_stopping; m_state = airtaudio::state_stopping;

View File

@ -36,8 +36,11 @@ namespace airtaudio {
bool xrun[2]; bool xrun[2];
bool triggered; bool triggered;
std::condition_variable runnable; std::condition_variable runnable;
std::unique_ptr<std::thread> thread;
bool threadRunning;
OssPrivate(): OssPrivate():
triggered(false) { triggered(false),
threadRunning(false) {
id[0] = 0; id[0] = 0;
id[1] = 0; id[1] = 0;
xrun[0] = false; xrun[0] = false;
@ -503,10 +506,10 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device,
} else { } else {
m_mode = _mode; m_mode = _mode;
// Setup callback thread. // Setup callback thread.
m_callbackInfo.isRunning = true; m_private->threadRunning = true;
m_callbackInfo.thread = new std::thread(ossCallbackHandler, this); m_private->thread = new std::thread(ossCallbackHandler, this);
if (m_callbackInfo.thread == nullptr) { if (m_private->thread == nullptr) {
m_callbackInfo.isRunning = false; m_private->threadRunning = false;
ATA_ERROR("creating callback thread!"); ATA_ERROR("creating callback thread!");
goto error; goto error;
} }
@ -539,13 +542,13 @@ enum airtaudio::error airtaudio::api::Oss::closeStream() {
ATA_ERROR("no open stream to close!"); ATA_ERROR("no open stream to close!");
return airtaudio::error_warning; return airtaudio::error_warning;
} }
m_callbackInfo.isRunning = false; m_private->threadRunning = false;
m_mutex.lock(); m_mutex.lock();
if (m_state == airtaudio::state_stopped) { if (m_state == airtaudio::state_stopped) {
m_private->runnable.notify_one(); m_private->runnable.notify_one();
} }
m_mutex.unlock(); m_mutex.unlock();
m_callbackInfo.thread->join(); m_private->thread->join();
if (m_state == airtaudio::state_running) { if (m_state == airtaudio::state_running) {
if (m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { if (m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) {
ioctl(m_private->id[0], SNDCTL_DSP_HALT, 0); ioctl(m_private->id[0], SNDCTL_DSP_HALT, 0);
@ -712,21 +715,22 @@ void airtaudio::api::Oss::callbackEvent() {
// Invoke user callback to get fresh output data. // Invoke user callback to get fresh output data.
int32_t doStopStream = 0; int32_t doStopStream = 0;
std::chrono::system_clock::time_point streamTime = getStreamTime(); std::chrono::system_clock::time_point streamTime = getStreamTime();
rtaudio::streamStatus status = 0; std::vector<enum airtaudio::status> status;
if ( m_mode != airtaudio::mode_input if ( m_mode != airtaudio::mode_input
&& m_private->xrun[0] == true) { && m_private->xrun[0] == true) {
status |= RTAUDIO_airtaudio::status_underflow; status.push_back(airtaudio::status_underflow);
m_private->xrun[0] = false; m_private->xrun[0] = false;
} }
if ( m_mode != airtaudio::mode_output if ( m_mode != airtaudio::mode_output
&& m_private->xrun[1] == true) { && m_private->xrun[1] == true) {
status |= RTAUDIO_airtaudio::mode_input_OVERFLOW; status.push_back(airtaudio::status_overflow);
m_private->xrun[1] = false; m_private->xrun[1] = false;
} }
doStopStream = m_callbackInfo.callback(m_userBuffer[0], doStopStream = m_callback(m_userBuffer[1],
m_userBuffer[1],
m_bufferSize,
streamTime, streamTime,
m_userBuffer[0],
streamTime,
m_bufferSize,
status); status);
if (doStopStream == 2) { if (doStopStream == 2) {
this->abortStream(); this->abortStream();
@ -818,9 +822,9 @@ unlock:
} }
static void ossCallbackHandler(void* _userData) { static void ossCallbackHandler(void* _userData) {
etk::log::setThreadName("OSS callback"); etk::log::setThreadName("OSS callback-" + m_name);
airtaudio::api::Alsa* myClass = reinterpret_cast<airtaudio::api::Oss*>(_userData); airtaudio::api::Alsa* myClass = reinterpret_cast<airtaudio::api::Oss*>(_userData);
while (myClass->m_callbackInfo->isRunning == true) { while (myClass->m_private->threadRunning == true) {
myClass->callbackEvent(); myClass->callbackEvent();
} }
} }

View File

@ -12,9 +12,6 @@
#include <limits.h> #include <limits.h>
#include <airtaudio/Interface.h> #include <airtaudio/Interface.h>
#include <airtaudio/debug.h> #include <airtaudio/debug.h>
// Code written by Peter Meerwald, pmeerw@pmeerw.net
// and Tristan Matthews.
#include <pulse/error.h> #include <pulse/error.h>
#include <pulse/simple.h> #include <pulse/simple.h>
#include <cstdio> #include <cstdio>
@ -56,12 +53,14 @@ namespace airtaudio {
public: public:
pa_simple *s_play; pa_simple *s_play;
pa_simple *s_rec; pa_simple *s_rec;
std::thread* thread; std::unique_ptr<std::thread> thread;
bool threadRunning;
std::condition_variable runnable_cv; std::condition_variable runnable_cv;
bool runnable; bool runnable;
PulsePrivate() : PulsePrivate() :
s_play(0), s_play(0),
s_rec(0), s_rec(0),
threadRunning(false),
runnable(false) { runnable(false) {
} }
@ -102,19 +101,19 @@ airtaudio::DeviceInfo airtaudio::api::Pulse::getDeviceInfo(uint32_t _device) {
} }
static void pulseaudio_callback(void* _userData) { static void pulseaudio_callback(void* _userData) {
etk::log::setThreadName("Pulse IO");
airtaudio::api::Pulse* myClass = reinterpret_cast<airtaudio::api::Pulse*>(_userData); airtaudio::api::Pulse* myClass = reinterpret_cast<airtaudio::api::Pulse*>(_userData);
myClass->callbackEvent(); myClass->callbackEvent();
} }
void airtaudio::api::Pulse::callbackEvent() { void airtaudio::api::Pulse::callbackEvent() {
while (m_callbackInfo.isRunning == true) { etk::log::setThreadName("Pulse IO-" + m_name);
while (m_private->threadRunning == true) {
callbackEventOneCycle(); callbackEventOneCycle();
} }
} }
enum airtaudio::error airtaudio::api::Pulse::closeStream() { enum airtaudio::error airtaudio::api::Pulse::closeStream() {
m_callbackInfo.isRunning = false; m_private->threadRunning = false;
m_mutex.lock(); m_mutex.lock();
if (m_state == airtaudio::state_stopped) { if (m_state == airtaudio::state_stopped) {
m_private->runnable = true; m_private->runnable = true;
@ -152,11 +151,12 @@ void airtaudio::api::Pulse::callbackEventOneCycle() {
return; return;
} }
std::chrono::system_clock::time_point streamTime = getStreamTime(); std::chrono::system_clock::time_point streamTime = getStreamTime();
enum airtaudio::status status = airtaudio::status_ok; std::vector<enum airtaudio::status> status;
int32_t doStopStream = m_callbackInfo.callback(&m_userBuffer[airtaudio::modeToIdTable(airtaudio::mode_output)][0], int32_t doStopStream = m_callback(&m_userBuffer[airtaudio::modeToIdTable(airtaudio::mode_input)][0],
&m_userBuffer[airtaudio::modeToIdTable(airtaudio::mode_input)][0],
m_bufferSize,
streamTime, streamTime,
&m_userBuffer[airtaudio::modeToIdTable(airtaudio::mode_output)][0],
streamTime,
m_bufferSize,
status); status);
if (doStopStream == 2) { if (doStopStream == 2) {
abortStream(); abortStream();
@ -396,9 +396,10 @@ bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t _device,
}else { }else {
m_mode = airtaudio::mode_duplex; m_mode = airtaudio::mode_duplex;
} }
if (!m_callbackInfo.isRunning) { if (!m_private->threadRunning) {
m_callbackInfo.isRunning = true; m_private->threadRunning = true;
m_private->thread = new std::thread(pulseaudio_callback, this); std::unique_ptr<std::thread> tmpThread(new std::thread(&pulseaudio_callback, this));
m_private->thread = std::move(tmpThread);
if (m_private->thread == nullptr) { if (m_private->thread == nullptr) {
ATA_ERROR("error creating thread."); ATA_ERROR("error creating thread.");
goto error; goto error;

View File

@ -18,63 +18,7 @@
#include <airtaudio/status.h> #include <airtaudio/status.h>
#include <airtaudio/Flags.h> #include <airtaudio/Flags.h>
// defien type : uintXX_t and intXX_t #include <airtaudio/CallbackInfo.h>
#define __STDC_LIMIT_MACROS
// note in android include the macro of min max are overwitten
#include <stdint.h>
#if defined(HAVE_GETTIMEOFDAY)
#include <sys/time.h>
#endif
//#include <etk/Stream.h>
namespace airtaudio {
//! Defined error types.
/**
* @brief airtaudio callback function prototype.
*
* All airtaudio clients must create a function of type AirTAudioCallback
* to read and/or write data from/to the audio stream. When the
* underlying audio system is ready for new input or output data, this
* function will be invoked.
*
* @param _outputBuffer For output (or duplex) streams, the client
* should write \c nFrames of audio sample frames into this
* buffer. This argument should be recast to the datatype
* specified when the stream was opened. For input-only
* streams, this argument will be nullptr.
*
* @param _inputBuffer For input (or duplex) streams, this buffer will
* hold \c nFrames of input audio sample frames. This
* argument should be recast to the datatype specified when the
* stream was opened. For output-only streams, this argument
* will be nullptr.
*
* @param _nbChunk The number of chunk of input or output
* data in the buffers. The actual buffer size in bytes is
* dependent on the data type and number of channels in use.
*
* @param _time The number of seconds that have elapsed since the
* stream was started.
*
* @param _status If non-zero, this argument indicates a data overflow
* or underflow condition for the stream. The particular
* condition can be determined by comparison with the
* streamStatus flags.
*
* To continue normal stream operation, the RtAudioCallback function
* should return a value of zero. To stop the stream and drain the
* output buffer, the function should return a value of one. To abort
* the stream immediately, the client should return a value of two.
*/
typedef std::function<int32_t (void* _outputBuffer,
void* _inputBuffer,
uint32_t _nbChunk,
const std::chrono::system_clock::time_point& _time,
airtaudio::status _status)> AirTAudioCallback;
}
#include <airtaudio/DeviceInfo.h> #include <airtaudio/DeviceInfo.h>
#include <airtaudio/StreamOptions.h> #include <airtaudio/StreamOptions.h>
#include <airtaudio/StreamParameters.h> #include <airtaudio/StreamParameters.h>