[DEV] rework and correct timestamp of Alsa (pase 1)
This commit is contained in:
parent
b72d6f31df
commit
24fef86124
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user