[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() :
m_callback(nullptr),
m_deviceBuffer(nullptr) {
m_device[0] = 11111;
m_device[1] = 11111;
@ -138,7 +139,7 @@ enum airtaudio::error airtaudio::Api::openStream(airtaudio::StreamParameters *oP
return airtaudio::error_systemError;
}
}
m_callbackInfo.callback = callback;
m_callback = callback;
if (options != nullptr) {
options->numberOfBuffers = m_nBuffers;
}
@ -175,7 +176,9 @@ bool airtaudio::Api::probeDeviceOpen(uint32_t /*device*/,
}
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() {
@ -224,10 +227,9 @@ void airtaudio::Api::clearStreamInfo() {
m_nBuffers = 0;
m_userFormat = audio::format_unknow;
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_callbackInfo.callback = 0;
m_callbackInfo.isRunning = false;
m_callback = nullptr;
for (int32_t iii=0; iii<2; ++iii) {
m_device[iii] = 11111;
m_doConvertBuffer[iii] = false;

View File

@ -18,6 +18,22 @@
namespace airtaudio {
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.
class ConvertInfo {
public:
@ -31,9 +47,14 @@ namespace airtaudio {
};
class Api {
protected:
std::string m_name;
public:
Api();
virtual ~Api();
void setName(const std::string& _name) {
m_name = _name;
}
virtual airtaudio::type getCurrentApi() = 0;
virtual uint32_t getDeviceCount() = 0;
virtual airtaudio::DeviceInfo getDeviceInfo(uint32_t _device) = 0;
@ -43,7 +64,7 @@ namespace airtaudio {
airtaudio::StreamParameters* _inputParameters,
audio::format _format,
uint32_t _sampleRate,
uint32_t* _bufferFrames,
uint32_t* _nbChunk,
airtaudio::AirTAudioCallback _callback,
airtaudio::StreamOptions* _options);
virtual enum airtaudio::error closeStream();
@ -62,6 +83,7 @@ namespace airtaudio {
protected:
mutable std::mutex m_mutex;
airtaudio::AirTAudioCallback m_callback;
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::state m_state; // STOPPED, RUNNING, or CLOSED
@ -70,22 +92,20 @@ namespace airtaudio {
bool m_doConvertBuffer[2]; // Playback and record, respectively.
bool m_deviceInterleaved[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_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_channelOffset[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_deviceFormat[2]; // Playback and record, respectively.
// TODO : Remove this ...
airtaudio::CallbackInfo m_callbackInfo;
airtaudio::ConvertInfo m_convertInfo[2];
//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::duration<int64_t, std::micro> m_duration; //!< duration from wich the stream is started
std::chrono::system_clock::time_point m_startTime; //!< start time of the stream (restart at every stop, pause ...)
std::chrono::nanoseconds m_duration; //!< duration from wich the stream is started
/**
* @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:
airtaudio::Api *m_rtapi;
public:
/**
* @brief A static function to determine the current airtaudio version.
*/
static std::string getVersion() {
return "4.0.12";
void setName(const std::string& _name) {
if (m_rtapi == nullptr) {
return;
}
m_rtapi->setName(_name);
}
/**
* @brief A static function to determine the available compiled audio APIs.

View File

@ -30,14 +30,17 @@ namespace airtaudio {
bool xrun[2];
std::condition_variable runnable_cv;
bool runnable;
std::unique_ptr<std::thread> thread;
bool threadRunning;
AlsaPrivate() :
synchronized(false),
runnable(false) {
runnable(false),
threadRunning(false) {
handles[0] = nullptr;
handles[1] = nullptr;
xrun[0] = false;
xrun[1] = false;
// TODO : Wait thread ...
}
};
};
@ -692,10 +695,11 @@ foundDevice:
} else {
m_mode = _mode;
// Setup callback thread.
m_callbackInfo.isRunning = true;
m_callbackInfo.thread = new std::thread(&airtaudio::api::Alsa::alsaCallbackEvent, this);
if (m_callbackInfo.thread == nullptr) {
m_callbackInfo.isRunning = false;
m_private->threadRunning = true;
std::unique_ptr<std::thread> tmpThread(new std::thread(&airtaudio::api::Alsa::alsaCallbackEvent, this));
m_private->thread = std::move(tmpThread);
if (m_private->thread == nullptr) {
m_private->threadRunning = false;
ATA_ERROR("creating callback thread!");
goto error;
}
@ -729,15 +733,15 @@ enum airtaudio::error airtaudio::api::Alsa::closeStream() {
ATA_ERROR("no open stream to close!");
return airtaudio::error_warning;
}
m_callbackInfo.isRunning = false;
m_private->threadRunning = false;
m_mutex.lock();
if (m_state == airtaudio::state_stopped) {
m_private->runnable = true;
m_private->runnable_cv.notify_one();
}
m_mutex.unlock();
if (m_callbackInfo.thread != nullptr) {
m_callbackInfo.thread->join();
if (m_private->thread != nullptr) {
m_private->thread->join();
}
if (m_state == airtaudio::state_running) {
m_state = airtaudio::state_stopped;
@ -909,27 +913,51 @@ unlock:
void airtaudio::api::Alsa::alsaCallbackEvent(void *_userData) {
etk::log::setThreadName("Alsa IO");
airtaudio::api::Alsa* myClass = reinterpret_cast<airtaudio::api::Alsa*>(_userData);
myClass->callbackEvent();
}
void airtaudio::api::Alsa::callbackEvent() {
while (m_callbackInfo.isRunning == true) {
etk::log::setThreadName("Alsa IO-" + m_name);
while (m_private->threadRunning == true) {
callbackEventOneCycle();
}
}
std::chrono::time_point<std::chrono::system_clock> airtaudio::api::Alsa::getStreamTime() {
snd_pcm_uframes_t avail;
snd_htimestamp_t tstamp;
if (m_private->handles[0] != nullptr) {
int plop = snd_pcm_htimestamp(m_private->handles[0], &avail, &tstamp);
} else if (m_private->handles[1] != nullptr) {
int plop = snd_pcm_htimestamp(m_private->handles[1], &avail, &tstamp);
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;
}
//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);
}
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_htimestamp_t tstamp;
if (m_private->handles[0] != nullptr) {
int plop = snd_pcm_htimestamp(m_private->handles[0], &avail, &tstamp);
} else if (m_private->handles[1] != nullptr) {
int plop = snd_pcm_htimestamp(m_private->handles[1], &avail, &tstamp);
}
//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);
//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,22 +980,23 @@ void airtaudio::api::Alsa::callbackEventOneCycle() {
}
int32_t doStopStream = 0;
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
&& m_private->xrun[0] == true) {
status = airtaudio::status_underflow;
status.push_back(airtaudio::status_underflow);
m_private->xrun[0] = false;
}
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;
}
doStopStream = m_callbackInfo.callback(&m_userBuffer[0][0],
&m_userBuffer[1][0],
m_bufferSize,
streamTime,
status);
doStopStream = m_callback(&m_userBuffer[1][0],
streamTime,
&m_userBuffer[0][0],
streamTime,
m_bufferSize,
status);
if (doStopStream == 2) {
abortStream();
return;

View File

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

View File

@ -111,20 +111,22 @@ void airtaudio::api::Android::callBackEvent(void* _data,
int32_t _frameRate) {
int32_t doStopStream = 0;
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) {
doStopStream = m_callbackInfo.callback(m_userBuffer[airtaudio::mode_output],
nullptr,
_frameRate,
streamTime,
status);
doStopStream = m_callback(nullptr,
std::chrono::system_clock::time_point(),
m_userBuffer[airtaudio::mode_output],
streamTime,
_frameRate,
status);
convertBuffer((char*)_data, (char*)m_userBuffer[airtaudio::mode_output], m_convertInfo[airtaudio::mode_output]);
} else {
doStopStream = m_callbackInfo.callback(_data,
nullptr,
_frameRate,
streamTime,
status);
doStopStream = m_callback(_data,
streamTime,
nullptr,
std::chrono::system_clock::time_point(),
_frameRate,
status);
}
if (doStopStream == 2) {
abortStream();

View File

@ -502,9 +502,7 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device,
m_sampleRate = _sampleRate;
m_device[modeToIdTable(_mode)] = _device;
m_state = airtaudio::state_stopped;
asioCallbackInfo = &m_callbackInfo;
m_callbackInfo.object = (void*)this;
if ( m_mode == airtaudio::mode_output
if ( _mode == airtaudio::mode_output
&& _mode == airtaudio::mode_input) {
// We had already set up an output stream.
m_mode = airtaudio::mode_duplex;
@ -695,19 +693,20 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) {
// draining stream.
if (m_private->drainCounter == 0) {
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) {
status |= RTAUDIO_airtaudio::status_underflow;
status.push_back(airtaudio::status_underflow);
asioXRun = false;
}
if (m_mode != airtaudio::mode_output && asioXRun == true) {
status |= RTAUDIO_airtaudio::mode_input_OVERFLOW;
status.push_back(airtaudio::status_underflow;
asioXRun = false;
}
int32_t cbReturnValue = info->callback(m_userBuffer[0],
m_userBuffer[1],
m_bufferSize,
int32_t cbReturnValue = info->callback(m_userBuffer[1],
streamTime,
m_userBuffer[0],
streamTime,
m_bufferSize,
status);
if (cbReturnValue == 2) {
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,
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
|| m_state == airtaudio::state_stopping) {
return true;
@ -1020,7 +1022,6 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
ATA_ERROR("the stream is closed ... this shouldn't happen!");
return false;
}
CallbackInfo *info = (CallbackInfo *) &m_callbackInfo;
// Check if we were draining the stream and signal is finished.
if (m_private->drainCounter > 3) {
m_state = airtaudio::state_stopping;
@ -1038,23 +1039,23 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
// draining stream or duplex mode AND the input/output devices are
// different AND this function is called for the input device.
if (m_private->drainCounter == 0 && (m_mode != airtaudio::mode_duplex || _deviceId == outputDevice)) {
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
&& m_private->xrun[0] == true) {
status = airtaudio::status_underflow;
status.push_back(airtaudio::status_underflow);
m_private->xrun[0] = false;
}
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;
}
int32_t cbReturnValue = info->callback(&m_userBuffer[0][0],
&m_userBuffer[1][0],
m_bufferSize,
streamTime,
status);
int32_t cbReturnValue = m_callback(&m_userBuffer[1][0],
_inTime,
&m_userBuffer[0][0],
_outTime,
m_bufferSize,
status);
if (cbReturnValue == 2) {
m_state = airtaudio::state_stopping;
ATA_VERBOSE("Set state as stopping");

View File

@ -31,13 +31,11 @@ namespace airtaudio {
enum airtaudio::error stopStream();
enum airtaudio::error abortStream();
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,
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,
const AudioTimeStamp* _inNow,
const AudioBufferList* _inInputData,
@ -57,11 +55,10 @@ namespace airtaudio {
uint32_t *_bufferSize,
airtaudio::StreamOptions *_options);
static const char* getErrorCode(OSStatus _code);
static OSStatus xrunListener(AudioObjectID _inDevice,
uint32_t _nAddresses,
const AudioObjectPropertyAddress _properties[],
void* _userData);
static OSStatus xrunListener(AudioObjectID _inDevice,
uint32_t _nAddresses,
const AudioObjectPropertyAddress _properties[],
void* _userData);
};
};
};

View File

@ -111,7 +111,7 @@ enum airtaudio::error airtaudio::api::CoreIos::abortStream(void) {
}
void airtaudio::api::CoreIos::callBackEvent(void* _data,
int32_t _frameRate) {
int32_t _nbChunk) {
#if 0
static double value=0;
@ -128,20 +128,22 @@ void airtaudio::api::CoreIos::callBackEvent(void* _data,
#endif
int32_t doStopStream = 0;
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) {
doStopStream = m_callbackInfo.callback(&m_userBuffer[modeToIdTable(airtaudio::mode_output)][0],
nullptr,
_frameRate,
streamTime,
status);
doStopStream = m_callback(nullptr,
streamTime,
&m_userBuffer[modeToIdTable(airtaudio::mode_output)][0],
streamTime,
_nbChunk,
status);
convertBuffer((char*)_data, &m_userBuffer[modeToIdTable(airtaudio::mode_output)][0], m_convertInfo[modeToIdTable(airtaudio::mode_output)]);
} else {
doStopStream = m_callbackInfo.callback(_data,
nullptr,
_frameRate,
streamTime,
status);
doStopStream = m_callback(_data,
streamTime,
nullptr,
streamTime,
_nbChunk,
status);
}
if (doStopStream == 2) {
abortStream();
@ -152,11 +154,11 @@ void airtaudio::api::CoreIos::callBackEvent(void* _data,
static OSStatus playbackCallback(void *_userData,
AudioUnitRenderActionFlags* _ioActionFlags,
const AudioTimeStamp* _inTimeStamp,
uint32_t _inBusNumber,
uint32_t _inNumberFrames,
AudioBufferList* _ioData) {
AudioUnitRenderActionFlags* _ioActionFlags,
const AudioTimeStamp* _inTimeStamp,
uint32_t _inBusNumber,
uint32_t _inNumberFrames,
AudioBufferList* _ioData) {
if (_userData == nullptr) {
ATA_ERROR("callback event ... nullptr pointer");
return -1;

View File

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

View File

@ -654,20 +654,21 @@ bool airtaudio::api::Jack::callbackEvent(uint64_t _nframes) {
// Invoke user callback first, to get fresh output data.
if (m_private->drainCounter == 0) {
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) {
status = airtaudio::status_underflow;
status.push_back(airtaudio::status_underflow);
m_private->xrun[0] = false;
}
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;
}
int32_t cbReturnValue = m_callbackInfo.callback(&m_userBuffer[0][0],
&m_userBuffer[1][0],
m_bufferSize,
streamTime,
status);
int32_t cbReturnValue = m_callback(&m_userBuffer[1][0],
streamTime,
&m_userBuffer[0][0],
streamTime,
m_bufferSize,
status);
if (cbReturnValue == 2) {
m_state = airtaudio::state_stopping;
m_private->drainCounter = 2;

View File

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

View File

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

View File

@ -18,63 +18,7 @@
#include <airtaudio/status.h>
#include <airtaudio/Flags.h>
// defien type : uintXX_t and intXX_t
#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/CallbackInfo.h>
#include <airtaudio/DeviceInfo.h>
#include <airtaudio/StreamOptions.h>
#include <airtaudio/StreamParameters.h>