From 24fef861249d3955e8320a0ee3e1e262a6003955 Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Tue, 17 Feb 2015 21:08:15 +0100 Subject: [PATCH] [DEV] rework and correct timestamp of Alsa (pase 1) --- airtaudio/Api.cpp | 12 +++--- airtaudio/Api.h | 34 ++++++++++++---- airtaudio/CallbackInfo.h | 41 ------------------- airtaudio/Interface.h | 11 ++--- airtaudio/api/Alsa.cpp | 85 ++++++++++++++++++++++++++------------- airtaudio/api/Alsa.h | 2 +- airtaudio/api/Android.cpp | 24 ++++++----- airtaudio/api/Asio.cpp | 17 ++++---- airtaudio/api/Core.cpp | 23 ++++++----- airtaudio/api/Core.h | 17 ++++---- airtaudio/api/CoreIos.mm | 36 +++++++++-------- airtaudio/api/Ds.cpp | 36 +++++++++-------- airtaudio/api/Jack.cpp | 17 ++++---- airtaudio/api/Oss.cpp | 38 +++++++++-------- airtaudio/api/Pulse.cpp | 33 +++++++-------- airtaudio/base.h | 58 +------------------------- 16 files changed, 225 insertions(+), 259 deletions(-) diff --git a/airtaudio/Api.cpp b/airtaudio/Api.cpp index 0ba0e8e..84b6ceb 100644 --- a/airtaudio/Api.cpp +++ b/airtaudio/Api.cpp @@ -43,6 +43,7 @@ const std::vector& 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((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(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; diff --git a/airtaudio/Api.h b/airtaudio/Api.h index 8bc2ee0..2d657cf 100644 --- a/airtaudio/Api.h +++ b/airtaudio/Api.h @@ -18,6 +18,22 @@ namespace airtaudio { const std::vector& 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& _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 m_startTime; //!< start time of the stream (restart at every stop, pause ...) - std::chrono::duration 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 diff --git a/airtaudio/CallbackInfo.h b/airtaudio/CallbackInfo.h index d55f5bc..e69de29 100644 --- a/airtaudio/CallbackInfo.h +++ b/airtaudio/CallbackInfo.h @@ -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 -#include -#include - -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 - diff --git a/airtaudio/Interface.h b/airtaudio/Interface.h index d621c92..63fd766 100644 --- a/airtaudio/Interface.h +++ b/airtaudio/Interface.h @@ -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. diff --git a/airtaudio/api/Alsa.cpp b/airtaudio/api/Alsa.cpp index bd41389..34cf99a 100644 --- a/airtaudio/api/Alsa.cpp +++ b/airtaudio/api/Alsa.cpp @@ -30,14 +30,17 @@ namespace airtaudio { bool xrun[2]; std::condition_variable runnable_cv; bool runnable; - + std::unique_ptr 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 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(_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 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(_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 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 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; diff --git a/airtaudio/api/Alsa.h b/airtaudio/api/Alsa.h index 6d93ce8..c2124fa 100644 --- a/airtaudio/api/Alsa.h +++ b/airtaudio/api/Alsa.h @@ -46,7 +46,7 @@ namespace airtaudio { enum audio::format _format, uint32_t *_bufferSize, airtaudio::StreamOptions *_options); - virtual std::chrono::time_point getStreamTime(); + virtual std::chrono::system_clock::time_point getStreamTime(); }; }; }; diff --git a/airtaudio/api/Android.cpp b/airtaudio/api/Android.cpp index 89a3410..c793588 100644 --- a/airtaudio/api/Android.cpp +++ b/airtaudio/api/Android.cpp @@ -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 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(); diff --git a/airtaudio/api/Asio.cpp b/airtaudio/api/Asio.cpp index 36f9342..92775e8 100644 --- a/airtaudio/api/Asio.cpp +++ b/airtaudio/api/Asio.cpp @@ -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::vectorcallback(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; diff --git a/airtaudio/api/Core.cpp b/airtaudio/api/Core.cpp index cc0f5f5..92c8553 100644 --- a/airtaudio/api/Core.cpp +++ b/airtaudio/api/Core.cpp @@ -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 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"); diff --git a/airtaudio/api/Core.h b/airtaudio/api/Core.h index 1fc1b6a..aa3d044 100644 --- a/airtaudio/api/Core.h +++ b/airtaudio/api/Core.h @@ -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); }; }; }; diff --git a/airtaudio/api/CoreIos.mm b/airtaudio/api/CoreIos.mm index d89e4dd..e5b8a2f 100644 --- a/airtaudio/api/CoreIos.mm +++ b/airtaudio/api/CoreIos.mm @@ -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 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; diff --git a/airtaudio/api/Ds.cpp b/airtaudio/api/Ds.cpp index 0c5744c..9a5a34e 100644 --- a/airtaudio/api/Ds.cpp +++ b/airtaudio/api/Ds.cpp @@ -74,17 +74,20 @@ namespace airtaudio { namespace api { class DsPrivate { public: + std::unique_ptr 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 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 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(_userData); - while (myClass->m_callbackInfo.isRunning == true) { + while (myClass->m_private->threadRunning == true) { myClass->callbackEvent(); } } diff --git a/airtaudio/api/Jack.cpp b/airtaudio/api/Jack.cpp index b8b42dc..300ac2a 100644 --- a/airtaudio/api/Jack.cpp +++ b/airtaudio/api/Jack.cpp @@ -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 streamTime = getStreamTime(); - enum airtaudio::status status = airtaudio::status_ok; + std::vector 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; diff --git a/airtaudio/api/Oss.cpp b/airtaudio/api/Oss.cpp index a982abd..83cffb6 100644 --- a/airtaudio/api/Oss.cpp +++ b/airtaudio/api/Oss.cpp @@ -36,8 +36,11 @@ namespace airtaudio { bool xrun[2]; bool triggered; std::condition_variable runnable; + std::unique_ptr 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 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(_userData); - while (myClass->m_callbackInfo->isRunning == true) { + while (myClass->m_private->threadRunning == true) { myClass->callbackEvent(); } } diff --git a/airtaudio/api/Pulse.cpp b/airtaudio/api/Pulse.cpp index bc72761..95cc650 100644 --- a/airtaudio/api/Pulse.cpp +++ b/airtaudio/api/Pulse.cpp @@ -12,9 +12,6 @@ #include #include #include -// Code written by Peter Meerwald, pmeerw@pmeerw.net -// and Tristan Matthews. - #include #include #include @@ -56,12 +53,14 @@ namespace airtaudio { public: pa_simple *s_play; pa_simple *s_rec; - std::thread* thread; + std::unique_ptr 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(_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 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 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; diff --git a/airtaudio/base.h b/airtaudio/base.h index b23376a..4cf3b8e 100644 --- a/airtaudio/base.h +++ b/airtaudio/base.h @@ -18,63 +18,7 @@ #include #include -// defien type : uintXX_t and intXX_t -#define __STDC_LIMIT_MACROS -// note in android include the macro of min max are overwitten -#include - -#if defined(HAVE_GETTIMEOFDAY) - #include -#endif -//#include - -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 AirTAudioCallback; -} - +#include #include #include #include