From 366d9a7049a82bddd098e9d7d7c85e20c3e0be32 Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Wed, 12 Mar 2014 23:55:49 +0100 Subject: [PATCH] [DEV] remove many exception --- airtaudio/Api.cpp | 130 ++-- airtaudio/Api.h | 17 +- airtaudio/CallbackInfo.h | 2 - airtaudio/Interface.cpp | 9 +- airtaudio/Interface.h | 20 +- airtaudio/api/Alsa.cpp | 268 ++++---- airtaudio/api/Asio.cpp | 31 +- airtaudio/api/Asio.h | 8 +- airtaudio/api/Core.cpp | 1298 ++++++++++++++++++-------------------- airtaudio/api/Core.h | 8 +- airtaudio/api/Ds.cpp | 109 ++-- airtaudio/api/Ds.h | 8 +- airtaudio/api/Dummy.cpp | 18 +- airtaudio/api/Dummy.h | 8 +- airtaudio/api/Jack.cpp | 387 +++++------- airtaudio/api/Jack.h | 8 +- airtaudio/api/Oss.cpp | 28 +- airtaudio/api/Oss.h | 8 +- airtaudio/api/Pulse.cpp | 182 +++--- airtaudio/api/Pulse.h | 8 +- airtaudio/base.h | 9 +- airtaudio/debug.cpp | 12 + airtaudio/debug.h | 28 + lutin_airtaudio.py | 8 +- 24 files changed, 1215 insertions(+), 1397 deletions(-) create mode 100644 airtaudio/debug.cpp create mode 100644 airtaudio/debug.h diff --git a/airtaudio/Api.cpp b/airtaudio/Api.cpp index 62c8931..9c962e4 100644 --- a/airtaudio/Api.cpp +++ b/airtaudio/Api.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -16,8 +17,20 @@ // Static variable definitions. const uint32_t airtaudio::api::MAX_SAMPLE_RATES = 14; const uint32_t airtaudio::api::SAMPLE_RATES[] = { - 4000, 5512, 8000, 9600, 11025, 16000, 22050, - 32000, 44100, 48000, 88200, 96000, 176400, 192000 + 4000, + 5512, + 8000, + 9600, + 11025, + 16000, + 22050, + 32000, + 44100, + 48000, + 88200, + 96000, + 176400, + 192000 }; @@ -27,7 +40,6 @@ airtaudio::Api::Api(void) { m_stream.apiHandle = 0; m_stream.userBuffer[0] = 0; m_stream.userBuffer[1] = 0; - m_showWarnings = true; } airtaudio::Api::~Api(void) { @@ -41,50 +53,42 @@ enum airtaudio::errorType airtaudio::Api::openStream(airtaudio::StreamParameters uint32_t *bufferFrames, airtaudio::AirTAudioCallback callback, void *userData, - airtaudio::StreamOptions *options, - airtaudio::AirTAudioErrorCallback errorCallback) { + airtaudio::StreamOptions *options) { if (m_stream.state != airtaudio::api::STREAM_CLOSED) { - m_errorText = "airtaudio::Api::openStream: a stream is already open!"; - error(airtaudio::errorInvalidUse); - return; + ATA_ERROR("airtaudio::Api::openStream: a stream is already open!"); + return airtaudio::errorInvalidUse; } if (oParams && oParams->nChannels < 1) { - m_errorText = "airtaudio::Api::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one."; - error(airtaudio::errorInvalidUse); - return; + ATA_ERROR("airtaudio::Api::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one."); + return airtaudio::errorInvalidUse; } if (iParams && iParams->nChannels < 1) { - m_errorText = "airtaudio::Api::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one."; - error(airtaudio::errorInvalidUse); - return; + ATA_ERROR("airtaudio::Api::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one."); + return airtaudio::errorInvalidUse; } if (oParams == NULL && iParams == NULL) { - m_errorText = "airtaudio::Api::openStream: input and output StreamParameters structures are both NULL!"; - error(airtaudio::errorInvalidUse); - return; + ATA_ERROR("airtaudio::Api::openStream: input and output StreamParameters structures are both NULL!"); + return airtaudio::errorInvalidUse; } if (formatBytes(format) == 0) { - m_errorText = "airtaudio::Api::openStream: 'format' parameter value is undefined."; - error(airtaudio::errorInvalidUse); - return; + ATA_ERROR("airtaudio::Api::openStream: 'format' parameter value is undefined."); + return airtaudio::errorInvalidUse; } uint32_t nDevices = getDeviceCount(); uint32_t oChannels = 0; if (oParams) { oChannels = oParams->nChannels; if (oParams->deviceId >= nDevices) { - m_errorText = "airtaudio::Api::openStream: output device parameter value is invalid."; - error(airtaudio::errorInvalidUse); - return; + ATA_ERROR("airtaudio::Api::openStream: output device parameter value is invalid."); + return airtaudio::errorInvalidUse; } } uint32_t iChannels = 0; if (iParams) { iChannels = iParams->nChannels; if (iParams->deviceId >= nDevices) { - m_errorText = "airtaudio::Api::openStream: input device parameter value is invalid."; - error(airtaudio::errorInvalidUse); - return; + ATA_ERROR("airtaudio::Api::openStream: input device parameter value is invalid."); + return airtaudio::errorInvalidUse; } } clearStreamInfo(); @@ -99,8 +103,8 @@ enum airtaudio::errorType airtaudio::Api::openStream(airtaudio::StreamParameters bufferFrames, options); if (result == false) { - error(airtaudio::errorSystemError); - return; + ATA_ERROR("system ERROR"); + return airtaudio::errorSystemError; } } if (iChannels > 0) { @@ -116,17 +120,17 @@ enum airtaudio::errorType airtaudio::Api::openStream(airtaudio::StreamParameters if (oChannels > 0) { closeStream(); } - error(airtaudio::errorSystemError); - return; + ATA_ERROR("system error"); + return airtaudio::errorSystemError; } } m_stream.callbackInfo.callback = (void *) callback; m_stream.callbackInfo.userData = userData; - m_stream.callbackInfo.errorCallback = (void *) errorCallback; if (options != NULL) { options->numberOfBuffers = m_stream.nBuffers; } m_stream.state = airtaudio::api::STREAM_STOPPED; + return airtaudio::errorNone; } uint32_t airtaudio::Api::getDefaultInputDevice(void) { @@ -167,19 +171,25 @@ void airtaudio::Api::tickStreamTime(void) { } long airtaudio::Api::getStreamLatency(void) { - verifyStream(); + if (verifyStream() != airtaudio::errorNone) { + return 0; + } long totalLatency = 0; - if (m_stream.mode == airtaudio::api::OUTPUT || m_stream.mode == airtaudio::api::DUPLEX) { + if ( m_stream.mode == airtaudio::api::OUTPUT + || m_stream.mode == airtaudio::api::DUPLEX) { totalLatency = m_stream.latency[0]; } - if (m_stream.mode == airtaudio::api::INPUT || m_stream.mode == airtaudio::api::DUPLEX) { + if ( m_stream.mode == airtaudio::api::INPUT + || m_stream.mode == airtaudio::api::DUPLEX) { totalLatency += m_stream.latency[1]; } return totalLatency; } double airtaudio::Api::getStreamTime(void) { - verifyStream(); + if (verifyStream() != airtaudio::errorNone) { + return 0.0f; + } #if defined(HAVE_GETTIMEOFDAY) // Return a very accurate estimate of the stream time by // adding in the elapsed time since the last tick. @@ -199,49 +209,18 @@ double airtaudio::Api::getStreamTime(void) { } uint32_t airtaudio::Api::getStreamSampleRate(void) { - verifyStream(); + if (verifyStream() != airtaudio::errorNone) { + return 0; + } return m_stream.sampleRate; } -// *************************************************** // -// -// Protected common (OS-independent) RtAudio methods. -// -// *************************************************** // -// This method can be modified to control the behavior of error -// message printing. -void airtaudio::Api::error(airtaudio::errorType _type) { - m_errorStream.str(""); // clear the ostringstream - airtaudio::AirTAudioErrorCallback errorCallback = (airtaudio::AirTAudioErrorCallback) m_stream.callbackInfo.errorCallback; - if (errorCallback) { - // abortStream() can generate new error messages. Ignore them. Just keep original one. - static bool firstErrorOccured = false; - if (firstErrorOccured) { - return; - } - firstErrorOccured = true; - const std::string errorMessage = m_errorText; - if (_type != airtaudio::errorWarning && m_stream.state != airtaudio::api::STREAM_STOPPED) { - m_stream.callbackInfo.isRunning = false; // exit from the thread - abortStream(); - } - errorCallback(_type, errorMessage); - firstErrorOccured = false; - return; - } - if (_type == airtaudio::errorWarning && m_showWarnings == true) { - std::cerr << '\n' << m_errorText << "\n\n"; - } else if (_type != airtaudio::errorWarning) { - //throw(RtError(m_errorText, type)); - std::cout << m_errorText << std::endl; - } -} - -void airtaudio::Api::verifyStream(void) { +enum airtaudio::errorType airtaudio::Api::verifyStream(void) { if (m_stream.state == airtaudio::api::STREAM_CLOSED) { - m_errorText = "airtaudio::Api:: a stream is not open!"; - error(airtaudio::errorInvalidUse); + ATA_ERROR("airtaudio::Api:: a stream is not open!"); + return airtaudio::errorInvalidUse; } + return airtaudio::errorNone; } void airtaudio::Api::clearStreamInfo(void) { @@ -258,7 +237,6 @@ void airtaudio::Api::clearStreamInfo(void) { m_stream.callbackInfo.callback = 0; m_stream.callbackInfo.userData = 0; m_stream.callbackInfo.isRunning = false; - m_stream.callbackInfo.errorCallback = 0; for (int32_t iii=0; iii<2; ++iii) { m_stream.device[iii] = 11111; m_stream.doConvertBuffer[iii] = false; @@ -294,8 +272,8 @@ uint32_t airtaudio::Api::formatBytes(airtaudio::format _format) } else if (_format == airtaudio::SINT8) { return 1; } - m_errorText = "airtaudio::Api::formatBytes: undefined format."; - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::Api::formatBytes: undefined format."); + // TODO : airtaudio::errorWarning; return 0; } diff --git a/airtaudio/Api.h b/airtaudio/Api.h index a8df633..617e39a 100644 --- a/airtaudio/Api.h +++ b/airtaudio/Api.h @@ -130,8 +130,7 @@ namespace airtaudio { uint32_t *_bufferFrames, airtaudio::AirTAudioCallback _callback, void *_userData, - airtaudio::StreamOptions *_options, - airtaudio::AirTAudioErrorCallback _errorCallback); + airtaudio::StreamOptions *_options); virtual enum airtaudio::errorType closeStream(void); virtual enum airtaudio::errorType startStream(void) = 0; virtual enum airtaudio::errorType stopStream(void) = 0; @@ -145,14 +144,8 @@ namespace airtaudio { bool isStreamRunning(void) const { return m_stream.state == airtaudio::api::STREAM_RUNNING; } - void showWarnings(bool _value) { - m_showWarnings = _value; - } protected: - std::ostringstream m_errorStream; - std::string m_errorText; - bool m_showWarnings; airtaudio::api::Stream m_stream; /*! @@ -175,17 +168,13 @@ namespace airtaudio { void tickStreamTime(void); //! Protected common method to clear an RtApiStream structure. - void clearStreamInfo(); + void clearStreamInfo(void); /*! Protected common method that throws an RtError (type = INVALID_USE) if a stream is not open. */ - void verifyStream(void); - - //! Protected common error method to allow global control over error handling. - void error(airtaudio::errorType _type); - + enum airtaudio::errorType verifyStream(void); /** * @brief Protected method used to perform format, channel number, and/or interleaving * conversions between the user and device buffers. diff --git a/airtaudio/CallbackInfo.h b/airtaudio/CallbackInfo.h index 6f13102..a975bbd 100644 --- a/airtaudio/CallbackInfo.h +++ b/airtaudio/CallbackInfo.h @@ -21,7 +21,6 @@ namespace airtaudio { std::thread* thread; void* callback; void* userData; - void* errorCallback; void* apiInfo; // void pointer for API specific callback information bool isRunning; bool doRealtime; @@ -32,7 +31,6 @@ namespace airtaudio { object(0), callback(0), userData(0), - errorCallback(0), apiInfo(0), isRunning(false), doRealtime(false) { diff --git a/airtaudio/Interface.cpp b/airtaudio/Interface.cpp index 6e798c5..c1174c2 100644 --- a/airtaudio/Interface.cpp +++ b/airtaudio/Interface.cpp @@ -7,6 +7,7 @@ */ #include +#include #include std::vector airtaudio::Interface::getCompiledApi(void) { @@ -117,10 +118,9 @@ enum airtaudio::errorType airtaudio::Interface::openStream( uint32_t* _bufferFrames, airtaudio::AirTAudioCallback _callback, void* _userData, - airtaudio::StreamOptions* _options, - airtaudio::AirTAudioErrorCallback _errorCallback) { + airtaudio::StreamOptions* _options) { if (m_rtapi == NULL) { - return; + return airtaudio::errorInputNull; } return m_rtapi->openStream(_outputParameters, _inputParameters, @@ -129,8 +129,7 @@ enum airtaudio::errorType airtaudio::Interface::openStream( _bufferFrames, _callback, _userData, - _options, - _errorCallback); + _options); } diff --git a/airtaudio/Interface.h b/airtaudio/Interface.h index 2a5e131..190ca55 100644 --- a/airtaudio/Interface.h +++ b/airtaudio/Interface.h @@ -192,8 +192,7 @@ namespace airtaudio { uint32_t *_bufferFrames, airtaudio::AirTAudioCallback _callback, void *_userData = NULL, - airtaudio::StreamOptions *_options = NULL, - airtaudio::AirTAudioErrorCallback _errorCallback = NULL); + airtaudio::StreamOptions *_options = NULL); /** * @brief A function that closes a stream and frees any associated stream memory. @@ -203,7 +202,7 @@ namespace airtaudio { */ enum airtaudio::errorType closeStream(void) { if (m_rtapi == NULL) { - return; + return airtaudio::errorInputNull; } return m_rtapi->closeStream(); } @@ -217,7 +216,7 @@ namespace airtaudio { */ enum airtaudio::errorType startStream(void) { if (m_rtapi == NULL) { - return; + return airtaudio::errorInputNull; } return m_rtapi->startStream(); } @@ -231,7 +230,7 @@ namespace airtaudio { */ enum airtaudio::errorType stopStream(void) { if (m_rtapi == NULL) { - return; + return airtaudio::errorInputNull; } return m_rtapi->stopStream(); } @@ -244,7 +243,7 @@ namespace airtaudio { */ enum airtaudio::errorType abortStream(void) { if (m_rtapi == NULL) { - return; + return airtaudio::errorInputNull; } return m_rtapi->abortStream(); } @@ -303,15 +302,6 @@ namespace airtaudio { } return m_rtapi->getStreamSampleRate(); } - /** - * @brief Specify whether warning messages should be printed to stderr. - */ - void showWarnings(bool _value = true) { - if (m_rtapi == NULL) { - return; - } - m_rtapi->showWarnings(_value); - } protected: void openRtApi(airtaudio::api::type _api); }; diff --git a/airtaudio/api/Alsa.cpp b/airtaudio/api/Alsa.cpp index ea96f97..6a9e547 100644 --- a/airtaudio/api/Alsa.cpp +++ b/airtaudio/api/Alsa.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include airtaudio::Api* airtaudio::api::Alsa::Create(void) { @@ -60,18 +61,16 @@ uint32_t airtaudio::api::Alsa::getDeviceCount(void) { sprintf(name, "hw:%d", card); result = snd_ctl_open(&handle, name, 0); if (result < 0) { - m_errorStream << "airtaudio::api::Alsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Alsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror(result) << "."); + // TODO : Return error airtaudio::errorWarning; goto nextcard; } subdevice = -1; while(1) { result = snd_ctl_pcm_next_device(handle, &subdevice); if (result < 0) { - m_errorStream << "airtaudio::api::Alsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Alsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror(result) << "."); + // TODO : Return error airtaudio::errorWarning; break; } if (subdevice < 0) { @@ -79,7 +78,6 @@ uint32_t airtaudio::api::Alsa::getDeviceCount(void) { } nDevices++; } - nextcard: snd_ctl_close(handle); snd_card_next(&card); @@ -106,18 +104,14 @@ airtaudio::DeviceInfo airtaudio::api::Alsa::getDeviceInfo(uint32_t _device) { sprintf(name, "hw:%d", card); result = snd_ctl_open(&chandle, name, SND_CTL_NONBLOCK); if (result < 0) { - m_errorStream << "airtaudio::api::Alsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_WARNING("airtaudio::api::Alsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror(result) << "."); goto nextcard; } subdevice = -1; while(1) { result = snd_ctl_pcm_next_device(chandle, &subdevice); if (result < 0) { - m_errorStream << "airtaudio::api::Alsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_WARNING("airtaudio::api::Alsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror(result) << "."); break; } if (subdevice < 0) { @@ -142,13 +136,13 @@ airtaudio::DeviceInfo airtaudio::api::Alsa::getDeviceInfo(uint32_t _device) { nDevices++; } if (nDevices == 0) { - m_errorText = "airtaudio::api::Alsa::getDeviceInfo: no devices found!"; - error(airtaudio::errorInvalidUse); + ATA_ERROR("airtaudio::api::Alsa::getDeviceInfo: no devices found!"); + // TODO : airtaudio::errorInvalidUse; return info; } if (_device >= nDevices) { - m_errorText = "airtaudio::api::Alsa::getDeviceInfo: device ID is invalid!"; - error(airtaudio::errorInvalidUse); + ATA_ERROR("airtaudio::api::Alsa::getDeviceInfo: device ID is invalid!"); + // TODO : airtaudio::errorInvalidUse; return info; } @@ -160,8 +154,8 @@ foundDevice: || m_stream.device[1] == _device)) { snd_ctl_close(chandle); if (_device >= m_devices.size()) { - m_errorText = "airtaudio::api::Alsa::getDeviceInfo: device ID was not present before stream was opened."; - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Alsa::getDeviceInfo: device ID was not present before stream was opened."); + // TODO : return airtaudio::errorWarning; return info; } return m_devices[ _device ]; @@ -187,18 +181,16 @@ foundDevice: } result = snd_pcm_open(&phandle, name, stream, openMode | SND_PCM_NONBLOCK); if (result < 0) { - m_errorStream << "airtaudio::api::Alsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Alsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror(result) << "."); + // TODO : Return airtaudio::errorWarning; goto captureProbe; } // The device is open ... fill the parameter structure. result = snd_pcm_hw_params_any(phandle, params); if (result < 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Alsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror(result) << "."); + // TODO : Return airtaudio::errorWarning; goto captureProbe; } // Get output channel information. @@ -206,9 +198,8 @@ foundDevice: result = snd_pcm_hw_params_get_channels_max(params, &value); if (result < 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Alsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror(result) << "."); + // TODO : Return airtaudio::errorWarning; goto captureProbe; } info.outputChannels = value; @@ -231,9 +222,8 @@ captureProbe: } result = snd_pcm_open(&phandle, name, stream, openMode | SND_PCM_NONBLOCK); if (result < 0) { - m_errorStream << "airtaudio::api::Alsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Alsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror(result) << "."); + // TODO : Return airtaudio::errorWarning; if (info.outputChannels == 0) { return info; } @@ -243,9 +233,8 @@ captureProbe: result = snd_pcm_hw_params_any(phandle, params); if (result < 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Alsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror(result) << "."); + // TODO : Return airtaudio::errorWarning; if (info.outputChannels == 0) { return info; } @@ -254,9 +243,8 @@ captureProbe: result = snd_pcm_hw_params_get_channels_max(params, &value); if (result < 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Alsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror(result) << "."); + // TODO : Return airtaudio::errorWarning; if (info.outputChannels == 0) { return info; } @@ -290,18 +278,16 @@ probeParameters: snd_pcm_info_set_stream(pcminfo, stream); result = snd_pcm_open(&phandle, name, stream, openMode | SND_PCM_NONBLOCK); if (result < 0) { - m_errorStream << "airtaudio::api::Alsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Alsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror(result) << "."); + // TODO : Return airtaudio::errorWarning; return info; } // The device is open ... fill the parameter structure. result = snd_pcm_hw_params_any(phandle, params); if (result < 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Alsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror(result) << "."); + // TODO : Return airtaudio::errorWarning; return info; } // Test our discrete set of sample rate values. @@ -313,9 +299,8 @@ probeParameters: } if (info.sampleRates.size() == 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::getDeviceInfo: no supported sample rates found for device (" << name << ")."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Alsa::getDeviceInfo: no supported sample rates found for device (" << name << ")."); + // TODO : Return airtaudio::errorWarning; return info; } // Probe the supported data formats ... we don't care about endian-ness just yet @@ -347,9 +332,8 @@ probeParameters: } // Check that we have at least one supported format if (info.nativeFormats == 0) { - m_errorStream << "airtaudio::api::Alsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Alsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio."); + // TODO : Return airtaudio::errorWarning; return info; } // Get the device name @@ -382,10 +366,6 @@ bool airtaudio::api::Alsa::probeDeviceOpen(uint32_t _device, airtaudio::format _format, uint32_t *_bufferSize, airtaudio::StreamOptions *_options) { -#if defined(__RTAUDIO_DEBUG__) - snd_output_t *out; - snd_output_stdio_attach(&out, stderr, 0); -#endif // I'm not using the "plug" interface ... too much inconsistent behavior. unsigned nDevices = 0; int32_t result, subdevice, card; @@ -401,8 +381,7 @@ bool airtaudio::api::Alsa::probeDeviceOpen(uint32_t _device, sprintf(name, "hw:%d", card); result = snd_ctl_open(&chandle, name, SND_CTL_NONBLOCK); if (result < 0) { - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror(result) << "."); return FAILURE; } subdevice = -1; @@ -430,12 +409,12 @@ bool airtaudio::api::Alsa::probeDeviceOpen(uint32_t _device, } if (nDevices == 0) { // This should not happen because a check is made before this function is called. - m_errorText = "airtaudio::api::Alsa::probeDeviceOpen: no devices found!"; + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: no devices found!"); return FAILURE; } if (_device >= nDevices) { // This should not happen because a check is made before this function is called. - m_errorText = "airtaudio::api::Alsa::probeDeviceOpen: device ID is invalid!"; + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: device ID is invalid!"); return FAILURE; } } @@ -461,11 +440,10 @@ foundDevice: result = snd_pcm_open(&phandle, name, stream, openMode); if (result < 0) { if (_mode == OUTPUT) { - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: pcm device (" << name << ") won't open for output."; + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: pcm device (" << name << ") won't open for output."); } else { - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: pcm device (" << name << ") won't open for input."; + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: pcm device (" << name << ") won't open for input."); } - m_errorText = m_errorStream.str(); return FAILURE; } // Fill the parameter structure. @@ -474,14 +452,9 @@ foundDevice: result = snd_pcm_hw_params_any(phandle, hw_params); if (result < 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror(result) << "."); return FAILURE; } -#if defined(__RTAUDIO_DEBUG__) - fprintf(stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n"); - snd_pcm_hw_params_dump(hw_params, out); -#endif // Set access ... check user preference. if ( _options != NULL && _options->flags & airtaudio::NONINTERLEAVED) { @@ -505,8 +478,7 @@ foundDevice: } if (result < 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror(result) << "."); return FAILURE; } // Determine how to set the device format. @@ -562,16 +534,14 @@ foundDevice: } // If we get here, no supported format was found. snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: pcm device " << _device << " data format not supported by RtAudio."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: pcm device " << _device << " data format not supported by RtAudio."); return FAILURE; setFormat: result = snd_pcm_hw_params_set_format(phandle, hw_params, deviceFormat); if (result < 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror(result) << "."); return FAILURE; } // Determine whether byte-swaping is necessary. @@ -582,8 +552,7 @@ setFormat: m_stream.doByteSwap[_mode] = true; } else if (result < 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror(result) << "."); return FAILURE; } } @@ -591,8 +560,7 @@ setFormat: result = snd_pcm_hw_params_set_rate_near(phandle, hw_params, (uint32_t*) &_sampleRate, 0); if (result < 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror(result) << "."); return FAILURE; } // Determine the number of channels for this device. We support a possible @@ -604,15 +572,13 @@ setFormat: if ( result < 0 || deviceChannels < _channels + _firstChannel) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror(result) << "."); return FAILURE; } result = snd_pcm_hw_params_get_channels_min(hw_params, &value); if (result < 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror(result) << "."); return FAILURE; } deviceChannels = value; @@ -624,8 +590,7 @@ setFormat: result = snd_pcm_hw_params_set_channels(phandle, hw_params, deviceChannels); if (result < 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror(result) << "."); return FAILURE; } // Set the buffer (or period) size. @@ -634,8 +599,7 @@ setFormat: result = snd_pcm_hw_params_set_period_size_near(phandle, hw_params, &periodSize, &dir); if (result < 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror(result) << "."); return FAILURE; } *_bufferSize = periodSize; @@ -647,16 +611,14 @@ setFormat: result = snd_pcm_hw_params_set_periods_near(phandle, hw_params, &periods, &dir); if (result < 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror(result) << "."); return FAILURE; } // If attempting to setup a duplex stream, the bufferSize parameter // MUST be the same in both directions! if (m_stream.mode == OUTPUT && _mode == INPUT && *_bufferSize != m_stream.bufferSize) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ")."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ")."); return FAILURE; } m_stream.bufferSize = *_bufferSize; @@ -664,14 +626,9 @@ setFormat: result = snd_pcm_hw_params(phandle, hw_params); if (result < 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror(result) << "."); return FAILURE; } -#if defined(__RTAUDIO_DEBUG__) - fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n"); - snd_pcm_hw_params_dump(hw_params, out); -#endif // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns. snd_pcm_sw_params_t *sw_params = NULL; snd_pcm_sw_params_alloca(&sw_params); @@ -690,14 +647,9 @@ setFormat: result = snd_pcm_sw_params(phandle, sw_params); if (result < 0) { snd_pcm_close(phandle); - m_errorStream << "airtaudio::api::Alsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror(result) << "."); return FAILURE; } -#if defined(__RTAUDIO_DEBUG__) - fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n"); - snd_pcm_sw_params_dump(sw_params, out); -#endif // Set flags for buffer conversion m_stream.doConvertBuffer[_mode] = false; if (m_stream.userFormat != m_stream.deviceFormat[_mode]) { @@ -713,11 +665,9 @@ setFormat: // Allocate the ApiHandle if necessary and then save. AlsaHandle *apiInfo = 0; if (m_stream.apiHandle == 0) { - try { - apiInfo = (AlsaHandle *) new AlsaHandle; - } - catch (std::bad_alloc&) { - m_errorText = "airtaudio::api::Alsa::probeDeviceOpen: error allocating AlsaHandle memory."; + apiInfo = (AlsaHandle *) new AlsaHandle; + if (apiInfo == NULL) { + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: error allocating AlsaHandle memory."); goto error; } m_stream.apiHandle = (void *) apiInfo; @@ -733,7 +683,7 @@ setFormat: bufferBytes = m_stream.nUserChannels[_mode] * *_bufferSize * formatBytes(m_stream.userFormat); m_stream.userBuffer[_mode] = (char *) calloc(bufferBytes, 1); if (m_stream.userBuffer[_mode] == NULL) { - m_errorText = "airtaudio::api::Alsa::probeDeviceOpen: error allocating user buffer memory."; + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: error allocating user buffer memory."); goto error; } if (m_stream.doConvertBuffer[_mode]) { @@ -755,7 +705,7 @@ setFormat: } m_stream.deviceBuffer = (char *) calloc(bufferBytes, 1); if (m_stream.deviceBuffer == NULL) { - m_errorText = "airtaudio::api::Alsa::probeDeviceOpen: error allocating device buffer memory."; + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: error allocating device buffer memory."); goto error; } } @@ -778,8 +728,8 @@ setFormat: if (snd_pcm_link(apiInfo->handles[0], apiInfo->handles[1]) == 0) { apiInfo->synchronized = true; } else { - m_errorText = "airtaudio::api::Alsa::probeDeviceOpen: unable to synchronize input and output devices."; - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Alsa::probeDeviceOpen: unable to synchronize input and output devices."); + // TODO : airtaudio::errorWarning; } } else { m_stream.mode = _mode; @@ -789,7 +739,7 @@ setFormat: m_stream.callbackInfo.thread = new std::thread(alsaCallbackHandler, &m_stream.callbackInfo); if (m_stream.callbackInfo.thread == NULL) { m_stream.callbackInfo.isRunning = false; - m_errorText = "airtaudio::api::Alsa::error creating callback thread!"; + ATA_ERROR("airtaudio::api::Alsa::error creating callback thread!"); goto error; } } @@ -825,9 +775,8 @@ error: enum airtaudio::errorType airtaudio::api::Alsa::closeStream(void) { if (m_stream.state == STREAM_CLOSED) { - m_errorText = "airtaudio::api::Alsa::closeStream(): no open stream to close!"; - error(airtaudio::errorWarning); - return; + ATA_ERROR("airtaudio::api::Alsa::closeStream(): no open stream to close!"); + return airtaudio::errorWarning; } AlsaHandle *apiInfo = (AlsaHandle *) m_stream.apiHandle; m_stream.callbackInfo.isRunning = false; @@ -874,15 +823,17 @@ enum airtaudio::errorType airtaudio::api::Alsa::closeStream(void) { } m_stream.mode = UNINITIALIZED; m_stream.state = STREAM_CLOSED; + return airtaudio::errorNone; } enum airtaudio::errorType airtaudio::api::Alsa::startStream(void) { // This method calls snd_pcm_prepare if the device isn't already in that state. - verifyStream(); + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } if (m_stream.state == STREAM_RUNNING) { - m_errorText = "airtaudio::api::Alsa::startStream(): the stream is already running!"; - error(airtaudio::errorWarning); - return; + ATA_ERROR("airtaudio::api::Alsa::startStream(): the stream is already running!"); + return airtaudio::errorWarning; } std::unique_lock lck(m_stream.mutex); int32_t result = 0; @@ -894,8 +845,7 @@ enum airtaudio::errorType airtaudio::api::Alsa::startStream(void) { if (state != SND_PCM_STATE_PREPARED) { result = snd_pcm_prepare(handle[0]); if (result < 0) { - m_errorStream << "airtaudio::api::Alsa::startStream: error preparing output pcm device, " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::startStream: error preparing output pcm device, " << snd_strerror(result) << "."); goto unlock; } } @@ -907,8 +857,7 @@ enum airtaudio::errorType airtaudio::api::Alsa::startStream(void) { if (state != SND_PCM_STATE_PREPARED) { result = snd_pcm_prepare(handle[1]); if (result < 0) { - m_errorStream << "airtaudio::api::Alsa::startStream: error preparing input pcm device, " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::startStream: error preparing input pcm device, " << snd_strerror(result) << "."); goto unlock; } } @@ -918,17 +867,18 @@ unlock: apiInfo->runnable = true; apiInfo->runnable_cv.notify_one(); if (result >= 0) { - return; + return airtaudio::errorNone; } - error(airtaudio::errorSystemError); + return airtaudio::errorSystemError; } enum airtaudio::errorType airtaudio::api::Alsa::stopStream(void) { - verifyStream(); + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } if (m_stream.state == STREAM_STOPPED) { - m_errorText = "airtaudio::api::Alsa::stopStream(): the stream is already stopped!"; - error(airtaudio::errorWarning); - return; + ATA_ERROR("airtaudio::api::Alsa::stopStream(): the stream is already stopped!"); + return airtaudio::errorWarning; } m_stream.state = STREAM_STOPPED; std::unique_lock lck(m_stream.mutex); @@ -943,8 +893,7 @@ enum airtaudio::errorType airtaudio::api::Alsa::stopStream(void) { result = snd_pcm_drain(handle[0]); } if (result < 0) { - m_errorStream << "airtaudio::api::Alsa::stopStream: error draining output pcm device, " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::stopStream: error draining output pcm device, " << snd_strerror(result) << "."); goto unlock; } } @@ -953,24 +902,24 @@ enum airtaudio::errorType airtaudio::api::Alsa::stopStream(void) { && !apiInfo->synchronized) { result = snd_pcm_drop(handle[1]); if (result < 0) { - m_errorStream << "airtaudio::api::Alsa::stopStream: error stopping input pcm device, " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::stopStream: error stopping input pcm device, " << snd_strerror(result) << "."); goto unlock; } } unlock: if (result >= 0) { - return; + return airtaudio::errorNone; } - error(airtaudio::errorSystemError); + return airtaudio::errorSystemError; } enum airtaudio::errorType airtaudio::api::Alsa::abortStream(void) { - verifyStream(); + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } if (m_stream.state == STREAM_STOPPED) { - m_errorText = "airtaudio::api::Alsa::abortStream(): the stream is already stopped!"; - error(airtaudio::errorWarning); - return; + ATA_ERROR("airtaudio::api::Alsa::abortStream(): the stream is already stopped!"); + return airtaudio::errorWarning; } m_stream.state = STREAM_STOPPED; std::unique_lock lck(m_stream.mutex); @@ -981,8 +930,7 @@ enum airtaudio::errorType airtaudio::api::Alsa::abortStream(void) { || m_stream.mode == DUPLEX) { result = snd_pcm_drop(handle[0]); if (result < 0) { - m_errorStream << "airtaudio::api::Alsa::abortStream: error aborting output pcm device, " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::abortStream: error aborting output pcm device, " << snd_strerror(result) << "."); goto unlock; } } @@ -991,16 +939,15 @@ enum airtaudio::errorType airtaudio::api::Alsa::abortStream(void) { && !apiInfo->synchronized) { result = snd_pcm_drop(handle[1]); if (result < 0) { - m_errorStream << "airtaudio::api::Alsa::abortStream: error aborting input pcm device, " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::abortStream: error aborting input pcm device, " << snd_strerror(result) << "."); goto unlock; } } unlock: if (result >= 0) { - return; + return airtaudio::errorNone; } - error(airtaudio::errorSystemError); + return airtaudio::errorSystemError; } void airtaudio::api::Alsa::callbackEvent(void) { @@ -1018,9 +965,8 @@ void airtaudio::api::Alsa::callbackEvent(void) { } } if (m_stream.state == STREAM_CLOSED) { - m_errorText = "airtaudio::api::Alsa::callbackEvent(): the stream is closed ... this shouldn't happen!"; - error(airtaudio::errorWarning); - return; + ATA_CRITICAL("airtaudio::api::Alsa::callbackEvent(): the stream is closed ... this shouldn't happen!"); + return; // TODO : notify appl: airtaudio::errorWarning; } int32_t doStopStream = 0; airtaudio::AirTAudioCallback callback = (airtaudio::AirTAudioCallback) m_stream.callbackInfo.callback; @@ -1086,18 +1032,15 @@ void airtaudio::api::Alsa::callbackEvent(void) { apiInfo->xrun[1] = true; result = snd_pcm_prepare(handle[1]); if (result < 0) { - m_errorStream << "airtaudio::api::Alsa::callbackEvent: error preparing device after overrun, " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::callbackEvent: error preparing device after overrun, " << snd_strerror(result) << "."); } } else { - m_errorStream << "airtaudio::api::Alsa::callbackEvent: error, current state is " << snd_pcm_state_name(state) << ", " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::callbackEvent: error, current state is " << snd_pcm_state_name(state) << ", " << snd_strerror(result) << "."); } } else { - m_errorStream << "airtaudio::api::Alsa::callbackEvent: audio read error, " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::callbackEvent: audio read error, " << snd_strerror(result) << "."); } - error(airtaudio::errorWarning); + // TODO : Notify application ... airtaudio::errorWarning; goto tryOutput; } // Do byte swapping if necessary. @@ -1110,7 +1053,9 @@ void airtaudio::api::Alsa::callbackEvent(void) { } // Check stream latency result = snd_pcm_delay(handle[1], &frames); - if (result == 0 && frames > 0) m_stream.latency[1] = frames; + if (result == 0 && frames > 0) { + m_stream.latency[1] = frames; + } } tryOutput: @@ -1150,23 +1095,22 @@ tryOutput: apiInfo->xrun[0] = true; result = snd_pcm_prepare(handle[0]); if (result < 0) { - m_errorStream << "airtaudio::api::Alsa::callbackEvent: error preparing device after underrun, " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::callbackEvent: error preparing device after underrun, " << snd_strerror(result) << "."); } } else { - m_errorStream << "airtaudio::api::Alsa::callbackEvent: error, current state is " << snd_pcm_state_name(state) << ", " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::callbackEvent: error, current state is " << snd_pcm_state_name(state) << ", " << snd_strerror(result) << "."); } } else { - m_errorStream << "airtaudio::api::Alsa::callbackEvent: audio write error, " << snd_strerror(result) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Alsa::callbackEvent: audio write error, " << snd_strerror(result) << "."); } - error(airtaudio::errorWarning); + // TODO : Notuify application airtaudio::errorWarning; goto unlock; } // Check stream latency result = snd_pcm_delay(handle[0], &frames); - if (result == 0 && frames > 0) m_stream.latency[0] = frames; + if (result == 0 && frames > 0) { + m_stream.latency[0] = frames; + } } unlock: diff --git a/airtaudio/api/Asio.cpp b/airtaudio/api/Asio.cpp index 6071f01..a91b54b 100644 --- a/airtaudio/api/Asio.cpp +++ b/airtaudio/api/Asio.cpp @@ -10,6 +10,7 @@ #if defined(__WINDOWS_ASIO__) // ASIO API on Windows #include +#include airtaudio::Api* airtaudio::api::Asio::Create(void) { return new airtaudio::api::Asio(); @@ -183,15 +184,15 @@ rtaudio::DeviceInfo airtaudio::api::Asio::getDeviceInfo(uint32_t device) info.nativeFormats = 0; if (channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB) - info.nativeFormats |= RTAUDIO_SINT16; + info.nativeFormats |= SINT16; else if (channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB) - info.nativeFormats |= RTAUDIO_SINT32; + info.nativeFormats |= SINT32; else if (channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB) - info.nativeFormats |= RTAUDIO_FLOAT32; + info.nativeFormats |= FLOAT32; else if (channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB) - info.nativeFormats |= RTAUDIO_FLOAT64; + info.nativeFormats |= FLOAT64; else if (channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB) - info.nativeFormats |= RTAUDIO_SINT24; + info.nativeFormats |= SINT24; if (info.outputChannels > 0) if (getDefaultOutputDevice() == device) info.isDefaultOutput = true; @@ -579,7 +580,7 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t device, StreamMode mode, uin return FAILURE; } -void airtaudio::api::Asio::closeStream() +enum airtaudio::errorType airtaudio::api::Asio::closeStream() { if (m_stream.state == STREAM_CLOSED) { m_errorText = "airtaudio::api::Asio::closeStream(): no open stream to close!"; @@ -621,9 +622,11 @@ void airtaudio::api::Asio::closeStream() bool stopThreadCalled = false; -void airtaudio::api::Asio::startStream() +enum airtaudio::errorType airtaudio::api::Asio::startStream() { - verifyStream(); + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } if (m_stream.state == STREAM_RUNNING) { m_errorText = "airtaudio::api::Asio::startStream(): the stream is already running!"; error(airtaudio::errorWarning); @@ -651,9 +654,11 @@ void airtaudio::api::Asio::startStream() error(airtaudio::errorSystemError); } -void airtaudio::api::Asio::stopStream() +enum airtaudio::errorType airtaudio::api::Asio::stopStream() { - verifyStream(); + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } if (m_stream.state == STREAM_STOPPED) { m_errorText = "airtaudio::api::Asio::stopStream(): the stream is already stopped!"; error(airtaudio::errorWarning); @@ -680,9 +685,11 @@ void airtaudio::api::Asio::stopStream() error(airtaudio::errorSystemError); } -void airtaudio::api::Asio::abortStream() +enum airtaudio::errorType airtaudio::api::Asio::abortStream() { - verifyStream(); + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } if (m_stream.state == STREAM_STOPPED) { m_errorText = "airtaudio::api::Asio::abortStream(): the stream is already stopped!"; error(airtaudio::errorWarning); diff --git a/airtaudio/api/Asio.h b/airtaudio/api/Asio.h index af1b357..1d37b9b 100644 --- a/airtaudio/api/Asio.h +++ b/airtaudio/api/Asio.h @@ -22,10 +22,10 @@ namespace airtaudio { } uint32_t getDeviceCount(void); airtaudio::DeviceInfo getDeviceInfo(uint32_t _device); - void closeStream(void); - void startStream(void); - void stopStream(void); - void abortStream(void); + enum airtaudio::errorType closeStream(void); + enum airtaudio::errorType startStream(void); + enum airtaudio::errorType stopStream(void); + enum airtaudio::errorType abortStream(void); long getStreamLatency(void); // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, diff --git a/airtaudio/api/Core.cpp b/airtaudio/api/Core.cpp index e299c37..124c17d 100644 --- a/airtaudio/api/Core.cpp +++ b/airtaudio/api/Core.cpp @@ -16,6 +16,7 @@ #if defined(__MACOSX_CORE__) #include +#include airtaudio::Api* airtaudio::api::Core::Create(void) { return new airtaudio::api::Core(); @@ -40,164 +41,198 @@ airtaudio::Api* airtaudio::api::Core::Create(void) { // A structure to hold various information related to the CoreAudio API // implementation. struct CoreHandle { - AudioDeviceID id[2]; // device ids + AudioDeviceID id[2]; // device ids #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) AudioDeviceIOProcID procId[2]; #endif - UInt32 iStream[2]; // device stream index (or first if using multiple) - UInt32 nStreams[2]; // number of streams to use + uint32_t iStream[2]; // device stream index (or first if using multiple) + uint32_t nStreams[2]; // number of streams to use bool xrun[2]; char *deviceBuffer; - pthread_cond_t condition; - int32_t drainCounter; // Tracks callback counts when draining - bool internalDrain; // Indicates if stop is initiated from callback or not. - - CoreHandle() - :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; } + std::condition_variable condition; + int32_t drainCounter; // Tracks callback counts when draining + bool internalDrain; // Indicates if stop is initiated from callback or not. + CoreHandle(void) : + deviceBuffer(0), + drainCounter(0), + internalDrain(false) { + nStreams[0] = 1; + nStreams[1] = 1; + id[0] = 0; + id[1] = 0; + xrun[0] = false; + xrun[1] = false; + } }; -airtaudio::api::Core::Core() -{ +airtaudio::api::Core::Core(void) { #if defined(AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER) // This is a largely undocumented but absolutely necessary // requirement starting with OS-X 10.6. If not called, queries and // updates to various audio device properties are not handled // correctly. CFRunLoopRef theRunLoop = NULL; - AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - OSStatus result = AudioObjectSetPropertyData(kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop); + AudioObjectPropertyAddress property = { + kAudioHardwarePropertyRunLoop, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + OSStatus result = AudioObjectSetPropertyData(kAudioObjectSystemObject, + &property, + 0, + NULL, + sizeof(CFRunLoopRef), + &theRunLoop); if (result != noErr) { - m_errorText = "airtaudio::api::Core::RtApiCore: error setting run loop property!"; - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Core::RtApiCore: error setting run loop property!"); } #endif } -airtaudio::api::Core::~Core() -{ +airtaudio::api::Core::~Core(void) { // The subclass destructor gets called before the base class // destructor, so close an existing stream before deallocating // apiDeviceId memory. - if (m_stream.state != STREAM_CLOSED) closeStream(); + if (m_stream.state != STREAM_CLOSED) { + closeStream(); + } } -uint32_t airtaudio::api::Core::getDeviceCount(void) -{ +uint32_t airtaudio::api::Core::getDeviceCount(void) { // Find out how many audio devices there are, if any. - UInt32 dataSize; - AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + uint32_t dataSize; + AudioObjectPropertyAddress propertyAddress = { + kAudioHardwarePropertyDevices, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; OSStatus result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize); if (result != noErr) { - m_errorText = "airtaudio::api::Core::getDeviceCount: OS-X error getting device info!"; - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Core::getDeviceCount: OS-X error getting device info!"); return 0; } - return dataSize / sizeof(AudioDeviceID); } -uint32_t airtaudio::api::Core::getDefaultInputDevice(void) -{ +uint32_t airtaudio::api::Core::getDefaultInputDevice(void) { uint32_t nDevices = getDeviceCount(); - if (nDevices <= 1) return 0; - - AudioDeviceID id; - UInt32 dataSize = sizeof(AudioDeviceID); - AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; - OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id); - if (result != noErr) { - m_errorText = "airtaudio::api::Core::getDefaultInputDevice: OS-X system error getting device."; - error(airtaudio::errorWarning); + if (nDevices <= 1) { + return 0; + } + AudioDeviceID id; + uint32_t dataSize = sizeof(AudioDeviceID); + AudioObjectPropertyAddress property = { + kAudioHardwarePropertyDefaultInputDevice, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, + &property, + 0, + NULL, + &dataSize, + &id); + if (result != noErr) { + ATA_ERROR("airtaudio::api::Core::getDefaultInputDevice: OS-X system error getting device."); return 0; } - dataSize *= nDevices; AudioDeviceID deviceList[ nDevices ]; property.mSelector = kAudioHardwarePropertyDevices; - result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList); + result = AudioObjectGetPropertyData(kAudioObjectSystemObject, + &property, + 0, + NULL, + &dataSize, + (void*)&deviceList); if (result != noErr) { - m_errorText = "airtaudio::api::Core::getDefaultInputDevice: OS-X system error getting device IDs."; - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Core::getDefaultInputDevice: OS-X system error getting device IDs."); return 0; } - - for (uint32_t i=0; i= nDevices) { + if (_device >= nDevices) { m_errorText = "airtaudio::api::Core::getDeviceInfo: device ID is invalid!"; - error(airtaudio::errorInvalidUse); return info; } - AudioDeviceID deviceList[ nDevices ]; - UInt32 dataSize = sizeof(AudioDeviceID) * nDevices; - AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, - 0, NULL, &dataSize, (void *) &deviceList); + uint32_t dataSize = sizeof(AudioDeviceID) * nDevices; + AudioObjectPropertyAddress property = { + kAudioHardwarePropertyDevices, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, + &property, + 0, + NULL, + &dataSize, + (void*)&deviceList); if (result != noErr) { - m_errorText = "airtaudio::api::Core::getDeviceInfo: OS-X system error getting device IDs."; - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Core::getDeviceInfo: OS-X system error getting device IDs."); return info; } - - AudioDeviceID id = deviceList[ device ]; - + AudioDeviceID id = deviceList[ _device ]; // Get the device name. info.name.erase(); CFStringRef cfname; @@ -205,12 +240,9 @@ rtaudio::DeviceInfo airtaudio::api::Core::getDeviceInfo(uint32_t device) property.mSelector = kAudioObjectPropertyManufacturer; result = AudioObjectGetPropertyData(id, &property, 0, NULL, &dataSize, &cfname); if (result != noErr) { - m_errorStream << "airtaudio::api::Core::probeDeviceInfo: system error (" << getErrorCode(result) << ") getting device manufacturer."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Core::probeDeviceInfo: system error (" << getErrorCode(result) << ") getting device manufacturer."); return info; } - //const char *mname = CFStringGetCStringPtr(cfname, CFStringGetSystemEncoding()); int32_t length = CFStringGetLength(cfname); char *mname = (char *)malloc(length * 3 + 1); @@ -219,16 +251,12 @@ rtaudio::DeviceInfo airtaudio::api::Core::getDeviceInfo(uint32_t device) info.name.append(": "); CFRelease(cfname); free(mname); - property.mSelector = kAudioObjectPropertyName; result = AudioObjectGetPropertyData(id, &property, 0, NULL, &dataSize, &cfname); if (result != noErr) { - m_errorStream << "airtaudio::api::Core::probeDeviceInfo: system error (" << getErrorCode(result) << ") getting device name."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Core::probeDeviceInfo: system error (" << getErrorCode(result) << ") getting device name."); return info; } - //const char *name = CFStringGetCStringPtr(cfname, CFStringGetSystemEncoding()); length = CFStringGetLength(cfname); char *name = (char *)malloc(length * 3 + 1); @@ -236,7 +264,6 @@ rtaudio::DeviceInfo airtaudio::api::Core::getDeviceInfo(uint32_t device) info.name.append((const char *)name, strlen(name)); CFRelease(cfname); free(name); - // Get the output stream "configuration". AudioBufferList *bufferList = nil; property.mSelector = kAudioDevicePropertyStreamConfiguration; @@ -245,250 +272,233 @@ rtaudio::DeviceInfo airtaudio::api::Core::getDeviceInfo(uint32_t device) dataSize = 0; result = AudioObjectGetPropertyDataSize(id, &property, 0, NULL, &dataSize); if (result != noErr || dataSize == 0) { - m_errorStream << "airtaudio::api::Core::getDeviceInfo: system error (" << getErrorCode(result) << ") getting output stream configuration info for device (" << device << ")."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Core::getDeviceInfo: system error (" << getErrorCode(result) << ") getting output stream configuration info for device (" << device << ")."); return info; } - // Allocate the AudioBufferList. bufferList = (AudioBufferList *) malloc(dataSize); if (bufferList == NULL) { - m_errorText = "airtaudio::api::Core::getDeviceInfo: memory error allocating output AudioBufferList."; - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Core::getDeviceInfo: memory error allocating output AudioBufferList."); return info; } - result = AudioObjectGetPropertyData(id, &property, 0, NULL, &dataSize, bufferList); - if (result != noErr || dataSize == 0) { + if ( result != noErr + || dataSize == 0) { free(bufferList); - m_errorStream << "airtaudio::api::Core::getDeviceInfo: system error (" << getErrorCode(result) << ") getting output stream configuration for device (" << device << ")."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Core::getDeviceInfo: system error (" << getErrorCode(result) << ") getting output stream configuration for device (" << _device << ")."); return info; } - // Get output channel information. uint32_t i, nStreams = bufferList->mNumberBuffers; - for (i=0; imBuffers[i].mNumberChannels; + } free(bufferList); - // Get the input stream "configuration". property.mScope = kAudioDevicePropertyScopeInput; result = AudioObjectGetPropertyDataSize(id, &property, 0, NULL, &dataSize); - if (result != noErr || dataSize == 0) { - m_errorStream << "airtaudio::api::Core::getDeviceInfo: system error (" << getErrorCode(result) << ") getting input stream configuration info for device (" << device << ")."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + if ( result != noErr + || dataSize == 0) { + ATA_ERROR("airtaudio::api::Core::getDeviceInfo: system error (" << getErrorCode(result) << ") getting input stream configuration info for device (" << _device << ")."); return info; } - // Allocate the AudioBufferList. bufferList = (AudioBufferList *) malloc(dataSize); if (bufferList == NULL) { - m_errorText = "airtaudio::api::Core::getDeviceInfo: memory error allocating input AudioBufferList."; - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Core::getDeviceInfo: memory error allocating input AudioBufferList."); return info; } - result = AudioObjectGetPropertyData(id, &property, 0, NULL, &dataSize, bufferList); if (result != noErr || dataSize == 0) { free(bufferList); - m_errorStream << "airtaudio::api::Core::getDeviceInfo: system error (" << getErrorCode(result) << ") getting input stream configuration for device (" << device << ")."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Core::getDeviceInfo: system error (" << getErrorCode(result) << ") getting input stream configuration for device (" << _device << ")."); return info; } - // Get input channel information. nStreams = bufferList->mNumberBuffers; - for (i=0; imBuffers[i].mNumberChannels; + } free(bufferList); - // If device opens for both playback and capture, we determine the channels. - if (info.outputChannels > 0 && info.inputChannels > 0) + if ( info.outputChannels > 0 + && info.inputChannels > 0) { info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; - + } // Probe the device sample rates. bool isInput = false; - if (info.outputChannels == 0) isInput = true; - + if (info.outputChannels == 0) { + isInput = true; + } // Determine the supported sample rates. property.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; if (isInput == false) property.mScope = kAudioDevicePropertyScopeOutput; result = AudioObjectGetPropertyDataSize(id, &property, 0, NULL, &dataSize); - if (result != kAudioHardwareNoError || dataSize == 0) { - m_errorStream << "airtaudio::api::Core::getDeviceInfo: system error (" << getErrorCode(result) << ") getting sample rate info."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + if ( result != kAudioHardwareNoError + || dataSize == 0) { + ATA_ERROR("airtaudio::api::Core::getDeviceInfo: system error (" << getErrorCode(result) << ") getting sample rate info."); return info; } - - UInt32 nRanges = dataSize / sizeof(AudioValueRange); + uint32_t nRanges = dataSize / sizeof(AudioValueRange); AudioValueRange rangeList[ nRanges ]; result = AudioObjectGetPropertyData(id, &property, 0, NULL, &dataSize, &rangeList); if (result != kAudioHardwareNoError) { - m_errorStream << "airtaudio::api::Core::getDeviceInfo: system error (" << getErrorCode(result) << ") getting sample rates."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Core::getDeviceInfo: system error (" << getErrorCode(result) << ") getting sample rates."); return info; } - - Float64 minimumRate = 100000000.0, maximumRate = 0.0; - for (UInt32 i=0; i maximumRate) maximumRate = rangeList[i].mMaximum; + double minimumRate = 100000000.0, maximumRate = 0.0; + for (uint32_t i=0; i maximumRate) { + maximumRate = rangeList[i].mMaximum; + } } - info.sampleRates.clear(); for (uint32_t k=0; k= (uint32_t) minimumRate && SAMPLE_RATES[k] <= (uint32_t) maximumRate) + if (SAMPLE_RATES[k] >= (uint32_t) minimumRate && SAMPLE_RATES[k] <= (uint32_t) maximumRate) { info.sampleRates.push_back(SAMPLE_RATES[k]); + } } - if (info.sampleRates.size() == 0) { - m_errorStream << "airtaudio::api::Core::probeDeviceInfo: No supported sample rates found for device (" << device << ")."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Core::probeDeviceInfo: No supported sample rates found for device (" << _device << ")."); return info; } - // CoreAudio always uses 32-bit floating point data for PCM streams. // Thus, any other "physical" formats supported by the device are of // no interest to the client. - info.nativeFormats = RTAUDIO_FLOAT32; - - if (info.outputChannels > 0) - if (getDefaultOutputDevice() == device) info.isDefaultOutput = true; - if (info.inputChannels > 0) - if (getDefaultInputDevice() == device) info.isDefaultInput = true; - + info.nativeFormats = FLOAT32; + if (info.outputChannels > 0) { + if (getDefaultOutputDevice() == _device) { + info.isDefaultOutput = true; + } + } + if (info.inputChannels > 0) { + if (getDefaultInputDevice() == _device) { + info.isDefaultInput = true; + } + } info.probed = true; return info; } - - - -static OSStatus callbackHandler(AudioDeviceID inDevice, - const AudioTimeStamp* /*inNow*/, - const AudioBufferList* inInputData, - const AudioTimeStamp* /*inInputTime*/, - AudioBufferList* outOutputData, - const AudioTimeStamp* /*inOutputTime*/, - void* infoPointer) -{ - CallbackInfo *info = (CallbackInfo *) infoPointer; - - RtApiCore *object = (RtApiCore *) info->object; - if (object->callbackEvent(inDevice, inInputData, outOutputData) == false) +static OSStatus callbackHandler(AudioDeviceID _inDevice, + const AudioTimeStamp* _inNow, + const AudioBufferList* _inInputData, + const AudioTimeStamp* _inInputTime, + AudioBufferList* _outOutputData, + const AudioTimeStamp* _inOutputTime, + void* _infoPointer) { + CallbackInfo* info = (CallbackInfo*)_infoPointer; + RtApiCore* object = (RtApiCore*)info->object; + if (object->callbackEvent(_inDevice, _inInputData, _outOutputData) == false) { return kAudioHardwareUnspecifiedError; - else + } else { return kAudioHardwareNoError; + } } -static OSStatus xrunListener(AudioObjectID /*inDevice*/, - UInt32 nAddresses, - const AudioObjectPropertyAddress properties[], - void* handlePointer) -{ - CoreHandle *handle = (CoreHandle *) handlePointer; - for (UInt32 i=0; ixrun[1] = true; - else + } else { handle->xrun[0] = true; + } } } - return kAudioHardwareNoError; } -static OSStatus rateListener(AudioObjectID inDevice, - UInt32 /*nAddresses*/, - const AudioObjectPropertyAddress /*properties*/[], - void* ratePointer) -{ - - Float64 *rate = (Float64 *) ratePointer; - UInt32 dataSize = sizeof(Float64); - AudioObjectPropertyAddress property = { kAudioDevicePropertyNominalSampleRate, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - AudioObjectGetPropertyData(inDevice, &property, 0, NULL, &dataSize, rate); +static OSStatus rateListener(AudioObjectID _inDevice, + uint32_t _nAddresses, + const AudioObjectPropertyAddress _properties[], + void* _ratePointer) { + double *rate = (double*)_ratePointer; + uint32_t dataSize = sizeof(double); + AudioObjectPropertyAddress property = { + kAudioDevicePropertyNominalSampleRate, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + AudioObjectGetPropertyData(_inDevice, &property, 0, NULL, &dataSize, rate); return kAudioHardwareNoError; } -bool airtaudio::api::Core::probeDeviceOpen(uint32_t device, StreamMode mode, uint32_t channels, - uint32_t firstChannel, uint32_t sampleRate, - rtaudio::format format, uint32_t *bufferSize, - rtaudio::StreamOptions *options) -{ +bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device, + airtaudio::api::StreamMode _mode, + uint32_t _channels, + uint32_t _firstChannel, + uint32_t _sampleRate, + airtaudio::format _format, + uint32_t *_bufferSize, + airtaudio::StreamOptions *_options) { // Get device ID uint32_t nDevices = getDeviceCount(); if (nDevices == 0) { // This should not happen because a check is made before this function is called. - m_errorText = "airtaudio::api::Core::probeDeviceOpen: no devices found!"; + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: no devices found!"); return FAILURE; } - - if (device >= nDevices) { + if (_device >= nDevices) { // This should not happen because a check is made before this function is called. - m_errorText = "airtaudio::api::Core::probeDeviceOpen: device ID is invalid!"; + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: device ID is invalid!"); return FAILURE; } - AudioDeviceID deviceList[ nDevices ]; - UInt32 dataSize = sizeof(AudioDeviceID) * nDevices; - AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, - 0, NULL, &dataSize, (void *) &deviceList); + uint32_t dataSize = sizeof(AudioDeviceID) * nDevices; + AudioObjectPropertyAddress property = { + kAudioHardwarePropertyDevices, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, + &property, + 0, + NULL, + &dataSize, + (void *) &deviceList); if (result != noErr) { - m_errorText = "airtaudio::api::Core::probeDeviceOpen: OS-X system error getting device IDs."; + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: OS-X system error getting device IDs."); return FAILURE; } - - AudioDeviceID id = deviceList[ device ]; - + AudioDeviceID id = deviceList[ _device ]; // Setup for stream mode. bool isInput = false; - if (mode == INPUT) { + if (_mode == INPUT) { isInput = true; property.mScope = kAudioDevicePropertyScopeInput; - } - else + } else { property.mScope = kAudioDevicePropertyScopeOutput; - + } // Get the stream "configuration". AudioBufferList *bufferList = nil; dataSize = 0; property.mSelector = kAudioDevicePropertyStreamConfiguration; result = AudioObjectGetPropertyDataSize(id, &property, 0, NULL, &dataSize); - if (result != noErr || dataSize == 0) { - m_errorStream << "airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") getting stream configuration info for device (" << device << ")."; - m_errorText = m_errorStream.str(); + if ( result != noErr + || dataSize == 0) { + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") getting stream configuration info for device (" << _device << ")."); return FAILURE; } - // Allocate the AudioBufferList. bufferList = (AudioBufferList *) malloc(dataSize); if (bufferList == NULL) { - m_errorText = "airtaudio::api::Core::probeDeviceOpen: memory error allocating AudioBufferList."; + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: memory error allocating AudioBufferList."); return FAILURE; } - result = AudioObjectGetPropertyData(id, &property, 0, NULL, &dataSize, bufferList); - if (result != noErr || dataSize == 0) { - m_errorStream << "airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") getting stream configuration for device (" << device << ")."; - m_errorText = m_errorStream.str(); + if ( result != noErr + || dataSize == 0) { + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") getting stream configuration for device (" << _device << ")."); return FAILURE; } - // Search for one or more streams that contain the desired number of // channels. CoreAudio devices can have an arbitrary number of // streams and each stream can have an arbitrary number of channels. @@ -497,180 +507,164 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t device, StreamMode mode, uin // data or multiple consecutive single-channel streams. However, we // now support multiple consecutive multi-channel streams of // interleaved data as well. - UInt32 iStream, offsetCounter = firstChannel; - UInt32 nStreams = bufferList->mNumberBuffers; + uint32_t iStream, offsetCounter = _firstChannel; + uint32_t nStreams = bufferList->mNumberBuffers; bool monoMode = false; bool foundStream = false; - // First check that the device supports the requested number of // channels. - UInt32 deviceChannels = 0; - for (iStream=0; iStreammBuffers[iStream].mNumberChannels; - - if (deviceChannels < (channels + firstChannel)) { + } + if (deviceChannels < (_channels + _firstChannel)) { free(bufferList); - m_errorStream << "airtaudio::api::Core::probeDeviceOpen: the device (" << device << ") does not support the requested channel count."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: the device (" << _device << ") does not support the requested channel count."); return FAILURE; } - // Look for a single stream meeting our needs. - UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0; + uint32_t firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0; for (iStream=0; iStreammBuffers[iStream].mNumberChannels; - if (streamChannels >= channels + offsetCounter) { + if (streamChannels >= _channels + offsetCounter) { firstStream = iStream; channelOffset = offsetCounter; foundStream = true; break; } - if (streamChannels > offsetCounter) break; + if (streamChannels > offsetCounter) { + break; + } offsetCounter -= streamChannels; } - // If we didn't find a single stream above, then we should be able // to meet the channel specification with multiple streams. if (foundStream == false) { monoMode = true; - offsetCounter = firstChannel; + offsetCounter = _firstChannel; for (iStream=0; iStreammBuffers[iStream].mNumberChannels; - if (streamChannels > offsetCounter) break; + if (streamChannels > offsetCounter) { + break; + } offsetCounter -= streamChannels; } - firstStream = iStream; channelOffset = offsetCounter; - Int32 channelCounter = channels + offsetCounter - streamChannels; - - if (streamChannels > 1) monoMode = false; + Int32 channelCounter = _channels + offsetCounter - streamChannels; + if (streamChannels > 1) { + monoMode = false; + } while (channelCounter > 0) { streamChannels = bufferList->mBuffers[++iStream].mNumberChannels; - if (streamChannels > 1) monoMode = false; + if (streamChannels > 1) { + monoMode = false; + } channelCounter -= streamChannels; streamCount++; } } - free(bufferList); - // Determine the buffer size. AudioValueRange bufferRange; dataSize = sizeof(AudioValueRange); property.mSelector = kAudioDevicePropertyBufferFrameSizeRange; result = AudioObjectGetPropertyData(id, &property, 0, NULL, &dataSize, &bufferRange); - if (result != noErr) { - m_errorStream << "airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") getting buffer size range for device (" << device << ")."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") getting buffer size range for device (" << _device << ")."); return FAILURE; } - - if (bufferRange.mMinimum > *bufferSize) *bufferSize = (uint64_t) bufferRange.mMinimum; - else if (bufferRange.mMaximum < *bufferSize) *bufferSize = (uint64_t) bufferRange.mMaximum; - if (options && options->flags & RTAUDIO_MINIMIZE_LATENCY) *bufferSize = (uint64_t) bufferRange.mMinimum; - + if (bufferRange.mMinimum > *_bufferSize) { + *_bufferSize = (uint64_t) bufferRange.mMinimum; + } else if (bufferRange.mMaximum < *_bufferSize) { + *_bufferSize = (uint64_t) bufferRange.mMaximum; + } + if ( _options != NULL + && _options->flags & RTAUDIO_MINIMIZE_LATENCY) { + *_bufferSize = (uint64_t) bufferRange.mMinimum; + } // Set the buffer size. For multiple streams, I'm assuming we only // need to make this setting for the master channel. - UInt32 theSize = (UInt32) *bufferSize; - dataSize = sizeof(UInt32); + uint32_t theSize = (uint32_t) *_bufferSize; + dataSize = sizeof(uint32_t); property.mSelector = kAudioDevicePropertyBufferFrameSize; result = AudioObjectSetPropertyData(id, &property, 0, NULL, dataSize, &theSize); - if (result != noErr) { - m_errorStream << "airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") setting the buffer size for device (" << device << ")."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") setting the buffer size for device (" << _device << ")."); return FAILURE; } - // If attempting to setup a duplex stream, the bufferSize parameter // MUST be the same in both directions! - *bufferSize = theSize; - if (m_stream.mode == OUTPUT && mode == INPUT && *bufferSize != m_stream.bufferSize) { - m_errorStream << "airtaudio::api::Core::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << device << ")."; - m_errorText = m_errorStream.str(); + *_bufferSize = theSize; + if ( m_stream.mode == OUTPUT + && _mode == INPUT + && *_bufferSize != m_stream.bufferSize) { + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << _device << ")."); return FAILURE; } - - m_stream.bufferSize = *bufferSize; + m_stream.bufferSize = *_bufferSize; m_stream.nBuffers = 1; - // Try to set "hog" mode ... it's not clear to me this is working. - if (options && options->flags & RTAUDIO_HOG_DEVICE) { + if ( _options != NULL + && _options->flags & RTAUDIO_HOG_DEVICE) { pid_t hog_pid; dataSize = sizeof(hog_pid); property.mSelector = kAudioDevicePropertyHogMode; result = AudioObjectGetPropertyData(id, &property, 0, NULL, &dataSize, &hog_pid); if (result != noErr) { - m_errorStream << "airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") getting 'hog' state!"; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") getting 'hog' state!"); return FAILURE; } - if (hog_pid != getpid()) { hog_pid = getpid(); result = AudioObjectSetPropertyData(id, &property, 0, NULL, dataSize, &hog_pid); if (result != noErr) { - m_errorStream << "airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") setting 'hog' state!"; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") setting 'hog' state!"); return FAILURE; } } } - // Check and if necessary, change the sample rate for the device. - Float64 nominalRate; - dataSize = sizeof(Float64); + double nominalRate; + dataSize = sizeof(double); property.mSelector = kAudioDevicePropertyNominalSampleRate; result = AudioObjectGetPropertyData(id, &property, 0, NULL, &dataSize, &nominalRate); - if (result != noErr) { - m_errorStream << "airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") getting current sample rate."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") getting current sample rate."); return FAILURE; } - // Only change the sample rate if off by more than 1 Hz. - if (fabs(nominalRate - (double)sampleRate) > 1.0) { - + if (fabs(nominalRate - (double)_sampleRate) > 1.0) { // Set a property listener for the sample rate change - Float64 reportedRate = 0.0; + double reportedRate = 0.0; AudioObjectPropertyAddress tmp = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; result = AudioObjectAddPropertyListener(id, &tmp, rateListener, (void *) &reportedRate); if (result != noErr) { - m_errorStream << "airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") setting sample rate property listener for device (" << device << ")."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") setting sample rate property listener for device (" << _device << ")."); return FAILURE; } - - nominalRate = (Float64) sampleRate; + nominalRate = (double) _sampleRate; result = AudioObjectSetPropertyData(id, &property, 0, NULL, dataSize, &nominalRate); - if (result != noErr) { - m_errorStream << "airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") setting sample rate for device (" << device << ")."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") setting sample rate for device (" << _device << ")."); return FAILURE; } - // Now wait until the reported nominal rate is what we just set. - UInt32 microCounter = 0; + uint32_t microCounter = 0; while (reportedRate != nominalRate) { microCounter += 5000; - if (microCounter > 5000000) break; + if (microCounter > 5000000) { + break; + } usleep(5000); } - // Remove the property listener. AudioObjectRemovePropertyListener(id, &tmp, rateListener, (void *) &reportedRate); - if (microCounter > 5000000) { - m_errorStream << "airtaudio::api::Core::probeDeviceOpen: timeout waiting for sample rate update for device (" << device << ")."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: timeout waiting for sample rate update for device (" << _device << ")."); return FAILURE; } } - // Now set the stream format for all streams. Also, check the // physical format of the device and change that if necessary. AudioStreamBasicDescription description; @@ -678,79 +672,71 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t device, StreamMode mode, uin property.mSelector = kAudioStreamPropertyVirtualFormat; result = AudioObjectGetPropertyData(id, &property, 0, NULL, &dataSize, &description); if (result != noErr) { - m_errorStream << "airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") getting stream format for device (" << device << ")."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") getting stream format for device (" << _device << ")."); return FAILURE; } - // Set the sample rate and data format id. However, only make the // change if the sample rate is not within 1.0 of the desired // rate and the format is not linear pcm. bool updateFormat = false; - if (fabs(description.mSampleRate - (Float64)sampleRate) > 1.0) { - description.mSampleRate = (Float64) sampleRate; + if (fabs(description.mSampleRate - (double)_sampleRate) > 1.0) { + description.mSampleRate = (double) _sampleRate; updateFormat = true; } - if (description.mFormatID != kAudioFormatLinearPCM) { description.mFormatID = kAudioFormatLinearPCM; updateFormat = true; } - if (updateFormat) { result = AudioObjectSetPropertyData(id, &property, 0, NULL, dataSize, &description); if (result != noErr) { - m_errorStream << "airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") setting sample rate or data format for device (" << device << ")."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") setting sample rate or data format for device (" << _device << ")."); return FAILURE; } } - // Now check the physical format. property.mSelector = kAudioStreamPropertyPhysicalFormat; result = AudioObjectGetPropertyData(id, &property, 0, NULL, &dataSize, &description); if (result != noErr) { - m_errorStream << "airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") getting stream physical format for device (" << device << ")."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: system error (" << getErrorCode(result) << ") getting stream physical format for device (" << _device << ")."); return FAILURE; } - //std::cout << "Current physical stream format:" << std::endl; //std::cout << " mBitsPerChan = " << description.mBitsPerChannel << std::endl; //std::cout << " aligned high = " << (description.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (description.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl; //std::cout << " bytesPerFrame = " << description.mBytesPerFrame << std::endl; //std::cout << " sample rate = " << description.mSampleRate << std::endl; - - if (description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 16) { + if ( description.mFormatID != kAudioFormatLinearPCM + || description.mBitsPerChannel < 16) { description.mFormatID = kAudioFormatLinearPCM; - //description.mSampleRate = (Float64) sampleRate; + //description.mSampleRate = (double) sampleRate; AudioStreamBasicDescription testDescription = description; - UInt32 formatFlags; - + uint32_t formatFlags; // We'll try higher bit rates first and then work our way down. - std::vector< std::pair > physicalFormats; + std::vector< std::pair > physicalFormats; formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsFloat) & ~kLinearPCMFormatFlagIsSignedInteger; - physicalFormats.push_back(std::pair(32, formatFlags)); + physicalFormats.push_back(std::pair(32, formatFlags)); formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat; - physicalFormats.push_back(std::pair(32, formatFlags)); - physicalFormats.push_back(std::pair(24, formatFlags)); // 24-bit packed + physicalFormats.push_back(std::pair(32, formatFlags)); + physicalFormats.push_back(std::pair(24, formatFlags)); // 24-bit packed formatFlags &= ~(kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh); - physicalFormats.push_back(std::pair(24.2, formatFlags)); // 24-bit in 4 bytes, aligned low + physicalFormats.push_back(std::pair(24.2, formatFlags)); // 24-bit in 4 bytes, aligned low formatFlags |= kAudioFormatFlagIsAlignedHigh; - physicalFormats.push_back(std::pair(24.4, formatFlags)); // 24-bit in 4 bytes, aligned high + physicalFormats.push_back(std::pair(24.4, formatFlags)); // 24-bit in 4 bytes, aligned high formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat; - physicalFormats.push_back(std::pair(16, formatFlags)); - physicalFormats.push_back(std::pair(8, formatFlags)); - + physicalFormats.push_back(std::pair(16, formatFlags)); + physicalFormats.push_back(std::pair(8, formatFlags)); bool setPhysicalFormat = false; for(uint32_t i=0; iflags & RTAUDIO_NONINTERLEAVED) m_stream.userInterleaved = false; - else m_stream.userInterleaved = true; - m_stream.deviceInterleaved[mode] = true; - if (monoMode == true) m_stream.deviceInterleaved[mode] = false; - - // Set flags for buffer conversion. - m_stream.doConvertBuffer[mode] = false; - if (m_stream.userFormat != m_stream.deviceFormat[mode]) - m_stream.doConvertBuffer[mode] = true; - if (m_stream.nUserChannels[mode] < m_stream.nDeviceChannels[mode]) - m_stream.doConvertBuffer[mode] = true; + m_stream.userFormat = _format; + m_stream.deviceFormat[_mode] = FLOAT32; if (streamCount == 1) { - if (m_stream.nUserChannels[mode] > 1 && - m_stream.userInterleaved != m_stream.deviceInterleaved[mode]) - m_stream.doConvertBuffer[mode] = true; + m_stream.nDeviceChannels[_mode] = description.mChannelsPerFrame; + } else { + // multiple streams + m_stream.nDeviceChannels[_mode] = _channels; + } + m_stream.nUserChannels[_mode] = _channels; + m_stream.channelOffset[_mode] = channelOffset; // offset within a CoreAudio stream + if ( _options != NULL + && _options->flags & RTAUDIO_NONINTERLEAVED) { + m_stream.userInterleaved = false; + } else { + m_stream.userInterleaved = true; + } + m_stream.deviceInterleaved[_mode] = true; + if (monoMode == true) { + m_stream.deviceInterleaved[_mode] = false; + } + // Set flags for buffer conversion. + m_stream.doConvertBuffer[_mode] = false; + if (m_stream.userFormat != m_stream.deviceFormat[_mode]) { + m_stream.doConvertBuffer[_mode] = true; + } + if (m_stream.nUserChannels[_mode] < m_stream.nDeviceChannels[_mode]) { + m_stream.doConvertBuffer[_mode] = true; + } + if (streamCount == 1) { + if ( m_stream.nUserChannels[_mode] > 1 + && m_stream.userInterleaved != m_stream.deviceInterleaved[_mode]) { + m_stream.doConvertBuffer[_mode] = true; + } + } else if (monoMode && m_stream.userInterleaved) { + m_stream.doConvertBuffer[_mode] = true; } - else if (monoMode && m_stream.userInterleaved) - m_stream.doConvertBuffer[mode] = true; - // Allocate our CoreHandle structure for the stream. CoreHandle *handle = 0; if (m_stream.apiHandle == 0) { - try { - handle = new CoreHandle; - } - catch (std::bad_alloc&) { - m_errorText = "airtaudio::api::Core::probeDeviceOpen: error allocating CoreHandle memory."; - goto error; - } - - if (pthread_cond_init(&handle->condition, NULL)) { - m_errorText = "airtaudio::api::Core::probeDeviceOpen: error initializing pthread condition variable."; + handle = new CoreHandle; + if (handle == NULL); + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: error allocating CoreHandle memory."); goto error; } m_stream.apiHandle = (void *) handle; - } - else + } else { handle = (CoreHandle *) m_stream.apiHandle; - handle->iStream[mode] = firstStream; - handle->nStreams[mode] = streamCount; - handle->id[mode] = id; - + } + handle->iStream[_mode] = firstStream; + handle->nStreams[_mode] = streamCount; + handle->id[_mode] = id; // Allocate necessary internal buffers. uint64_t bufferBytes; - bufferBytes = m_stream.nUserChannels[mode] * *bufferSize * formatBytes(m_stream.userFormat); - // m_stream.userBuffer[mode] = (char *) calloc(bufferBytes, 1); - m_stream.userBuffer[mode] = (char *) malloc(bufferBytes * sizeof(char)); - memset(m_stream.userBuffer[mode], 0, bufferBytes * sizeof(char)); - if (m_stream.userBuffer[mode] == NULL) { - m_errorText = "airtaudio::api::Core::probeDeviceOpen: error allocating user buffer memory."; + bufferBytes = m_stream.nUserChannels[_mode] * *_bufferSize * formatBytes(m_stream.userFormat); + // m_stream.userBuffer[_mode] = (char *) calloc(bufferBytes, 1); + m_stream.userBuffer[_mode] = (char *) malloc(bufferBytes * sizeof(char)); + memset(m_stream.userBuffer[_mode], 0, bufferBytes * sizeof(char)); + if (m_stream.userBuffer[_mode] == NULL) { + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: error allocating user buffer memory."); goto error; } - // If possible, we will make use of the CoreAudio stream buffers as // "device buffers". However, we can't do this if using multiple // streams. - if (m_stream.doConvertBuffer[mode] && handle->nStreams[mode] > 1) { - + if ( m_stream.doConvertBuffer[_mode] + && handle->nStreams[_mode] > 1) { bool makeBuffer = true; - bufferBytes = m_stream.nDeviceChannels[mode] * formatBytes(m_stream.deviceFormat[mode]); - if (mode == INPUT) { - if (m_stream.mode == OUTPUT && m_stream.deviceBuffer) { + bufferBytes = m_stream.nDeviceChannels[_mode] * formatBytes(m_stream.deviceFormat[_mode]); + if (_mode == INPUT) { + if ( m_stream.mode == OUTPUT + && m_stream.deviceBuffer) { uint64_t bytesOut = m_stream.nDeviceChannels[0] * formatBytes(m_stream.deviceFormat[0]); - if (bufferBytes <= bytesOut) makeBuffer = false; + if (bufferBytes <= bytesOut) { + makeBuffer = false; + } } } - if (makeBuffer) { - bufferBytes *= *bufferSize; - if (m_stream.deviceBuffer) free(m_stream.deviceBuffer); + bufferBytes *= *_bufferSize; + if (m_stream.deviceBuffer) { + free(m_stream.deviceBuffer); + m_stream.deviceBuffer = NULL; + } m_stream.deviceBuffer = (char *) calloc(bufferBytes, 1); if (m_stream.deviceBuffer == NULL) { - m_errorText = "airtaudio::api::Core::probeDeviceOpen: error allocating device buffer memory."; + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: error allocating device buffer memory."); goto error; } } } - - m_stream.sampleRate = sampleRate; - m_stream.device[mode] = device; + m_stream.sampleRate = _sampleRate; + m_stream.device[_mode] = _device; m_stream.state = STREAM_STOPPED; m_stream.callbackInfo.object = (void *) this; - // Setup the buffer conversion information structure. - if (m_stream.doConvertBuffer[mode]) { - if (streamCount > 1) setConvertInfo(mode, 0); - else setConvertInfo(mode, channelOffset); + if (m_stream.doConvertBuffer[_mode]) { + if (streamCount > 1) { + setConvertInfo(_mode, 0); + } else { + setConvertInfo(_mode, channelOffset); + } } - - if (mode == INPUT && m_stream.mode == OUTPUT && m_stream.device[0] == device) + if ( _mode == INPUT + && m_stream.mode == OUTPUT + && m_stream.device[0] == _device) { // Only one callback procedure per device. m_stream.mode = DUPLEX; - else { + } else { #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) - result = AudioDeviceCreateIOProcID(id, callbackHandler, (void *) &m_stream.callbackInfo, &handle->procId[mode]); + result = AudioDeviceCreateIOProcID(id, callbackHandler, (void *) &m_stream.callbackInfo, &handle->procId[_mode]); #else // deprecated in favor of AudioDeviceCreateIOProcID() result = AudioDeviceAddIOProc(id, callbackHandler, (void *) &m_stream.callbackInfo); #endif if (result != noErr) { - m_errorStream << "airtaudio::api::Core::probeDeviceOpen: system error setting callback for device (" << device << ")."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Core::probeDeviceOpen: system error setting callback for device (" << _device << ")."); goto error; } - if (m_stream.mode == OUTPUT && mode == INPUT) + if ( m_stream.mode == OUTPUT + && _mode == INPUT) { m_stream.mode = DUPLEX; - else - m_stream.mode = mode; + } else { + m_stream.mode = _mode; + } } - // Setup the device property listener for over/underload. property.mSelector = kAudioDeviceProcessorOverload; result = AudioObjectAddPropertyListener(id, &property, xrunListener, (void *) handle); - return SUCCESS; - - error: +error: if (handle) { - pthread_cond_destroy(&handle->condition); delete handle; m_stream.apiHandle = 0; } - for (int32_t i=0; i<2; i++) { if (m_stream.userBuffer[i]) { free(m_stream.userBuffer[i]); m_stream.userBuffer[i] = 0; } } - if (m_stream.deviceBuffer) { free(m_stream.deviceBuffer); m_stream.deviceBuffer = 0; } - m_stream.state = STREAM_CLOSED; return FAILURE; } -void airtaudio::api::Core::closeStream(void) -{ +enum airtaudio::errorType airtaudio::api::Core::closeStream(void) { if (m_stream.state == STREAM_CLOSED) { - m_errorText = "airtaudio::api::Core::closeStream(): no open stream to close!"; - error(airtaudio::errorWarning); - return; + ATA_ERROR("airtaudio::api::Core::closeStream(): no open stream to close!"); + return airtaudio::errorWarning; } - CoreHandle *handle = (CoreHandle *) m_stream.apiHandle; - if (m_stream.mode == OUTPUT || m_stream.mode == DUPLEX) { - if (m_stream.state == STREAM_RUNNING) + if ( m_stream.mode == OUTPUT + || m_stream.mode == DUPLEX) { + if (m_stream.state == STREAM_RUNNING) { AudioDeviceStop(handle->id[0], callbackHandler); + } #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) AudioDeviceDestroyIOProcID(handle->id[0], handle->procId[0]); #else @@ -959,10 +939,12 @@ void airtaudio::api::Core::closeStream(void) AudioDeviceRemoveIOProc(handle->id[0], callbackHandler); #endif } - - if (m_stream.mode == INPUT || (m_stream.mode == DUPLEX && m_stream.device[0] != m_stream.device[1])) { - if (m_stream.state == STREAM_RUNNING) + if ( m_stream.mode == INPUT + || ( m_stream.mode == DUPLEX + && m_stream.device[0] != m_stream.device[1])) { + if (m_stream.state == STREAM_RUNNING) { AudioDeviceStop(handle->id[1], callbackHandler); + } #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) AudioDeviceDestroyIOProcID(handle->id[1], handle->procId[1]); #else @@ -970,52 +952,45 @@ void airtaudio::api::Core::closeStream(void) AudioDeviceRemoveIOProc(handle->id[1], callbackHandler); #endif } - for (int32_t i=0; i<2; i++) { if (m_stream.userBuffer[i]) { free(m_stream.userBuffer[i]); - m_stream.userBuffer[i] = 0; + m_stream.userBuffer[i] = NULL; } } - if (m_stream.deviceBuffer) { free(m_stream.deviceBuffer); - m_stream.deviceBuffer = 0; + m_stream.deviceBuffer = NULL; } - - // Destroy pthread condition variable. - pthread_cond_destroy(&handle->condition); delete handle; m_stream.apiHandle = 0; - m_stream.mode = UNINITIALIZED; m_stream.state = STREAM_CLOSED; + return airtaudio::errorNone; } -void airtaudio::api::Core::startStream(void) -{ - verifyStream(); - if (m_stream.state == STREAM_RUNNING) { - m_errorText = "airtaudio::api::Core::startStream(): the stream is already running!"; - error(airtaudio::errorWarning); - return; +enum airtaudio::errorType airtaudio::api::Core::startStream(void) { + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } + if (m_stream.state == STREAM_RUNNING) { + ATA_ERROR("airtaudio::api::Core::startStream(): the stream is already running!"); + return airtaudio::errorWarning; } - OSStatus result = noErr; CoreHandle *handle = (CoreHandle *) m_stream.apiHandle; - if (m_stream.mode == OUTPUT || m_stream.mode == DUPLEX) { - + if ( m_stream.mode == OUTPUT + || m_stream.mode == DUPLEX) { result = AudioDeviceStart(handle->id[0], callbackHandler); if (result != noErr) { - m_errorStream << "airtaudio::api::Core::startStream: system error (" << getErrorCode(result) << ") starting callback procedure on device (" << m_stream.device[0] << ")."; + ATA_ERROR("airtaudio::api::Core::startStream: system error (" << getErrorCode(result) << ") starting callback procedure on device (" << m_stream.device[0] << ")."); m_errorText = m_errorStream.str(); goto unlock; } } - - if (m_stream.mode == INPUT || - (m_stream.mode == DUPLEX && m_stream.device[0] != m_stream.device[1])) { - + if ( m_stream.mode == INPUT + || ( m_stream.mode == DUPLEX + && m_stream.device[0] != m_stream.device[1])) { result = AudioDeviceStart(handle->id[1], callbackHandler); if (result != noErr) { m_errorStream << "airtaudio::api::Core::startStream: system error starting input callback procedure on device (" << m_stream.device[1] << ")."; @@ -1023,72 +998,67 @@ void airtaudio::api::Core::startStream(void) goto unlock; } } - handle->drainCounter = 0; handle->internalDrain = false; m_stream.state = STREAM_RUNNING; - - unlock: - if (result == noErr) return; - error(airtaudio::errorSystemError); +unlock: + if (result == noErr) { + return airtaudio::errorNone; + } + return airtaudio::errorSystemError; } -void airtaudio::api::Core::stopStream(void) -{ - verifyStream(); - if (m_stream.state == STREAM_STOPPED) { - m_errorText = "airtaudio::api::Core::stopStream(): the stream is already stopped!"; - error(airtaudio::errorWarning); - return; +enum airtaudio::errorType airtaudio::api::Core::stopStream(void) { + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } + if (m_stream.state == STREAM_STOPPED) { + ATA_ERROR("airtaudio::api::Core::stopStream(): the stream is already stopped!"); + return airtaudio::errorWarning; } - OSStatus result = noErr; CoreHandle *handle = (CoreHandle *) m_stream.apiHandle; - if (m_stream.mode == OUTPUT || m_stream.mode == DUPLEX) { - + if ( m_stream.mode == OUTPUT + || m_stream.mode == DUPLEX) { if (handle->drainCounter == 0) { + std::unique_lock lck(m_stream.mutex); handle->drainCounter = 2; - pthread_cond_wait(&handle->condition, &m_stream.mutex); // block until signaled + handle->condition.wait(lck); } - result = AudioDeviceStop(handle->id[0], callbackHandler); if (result != noErr) { - m_errorStream << "airtaudio::api::Core::stopStream: system error (" << getErrorCode(result) << ") stopping callback procedure on device (" << m_stream.device[0] << ")."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Core::stopStream: system error (" << getErrorCode(result) << ") stopping callback procedure on device (" << m_stream.device[0] << ")."); goto unlock; } } - - if (m_stream.mode == INPUT || (m_stream.mode == DUPLEX && m_stream.device[0] != m_stream.device[1])) { - + if ( m_stream.mode == INPUT + || ( m_stream.mode == DUPLEX + && m_stream.device[0] != m_stream.device[1])) { result = AudioDeviceStop(handle->id[1], callbackHandler); if (result != noErr) { - m_errorStream << "airtaudio::api::Core::stopStream: system error (" << getErrorCode(result) << ") stopping input callback procedure on device (" << m_stream.device[1] << ")."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Core::stopStream: system error (" << getErrorCode(result) << ") stopping input callback procedure on device (" << m_stream.device[1] << ")."); goto unlock; } } - m_stream.state = STREAM_STOPPED; - - unlock: - if (result == noErr) return; - error(airtaudio::errorSystemError); +unlock: + if (result == noErr) { + return airtaudio::errorNone; + } + return airtaudio::errorSystemError; } -void airtaudio::api::Core::abortStream(void) -{ - verifyStream(); - if (m_stream.state == STREAM_STOPPED) { - m_errorText = "airtaudio::api::Core::abortStream(): the stream is already stopped!"; - error(airtaudio::errorWarning); - return; +enum airtaudio::errorType airtaudio::api::Core::abortStream(void) { + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; } - - CoreHandle *handle = (CoreHandle *) m_stream.apiHandle; + if (m_stream.state == STREAM_STOPPED) { + ATA_ERROR("airtaudio::api::Core::abortStream(): the stream is already stopped!"); + return airtaudio::errorWarning; + } + CoreHandle* handle = (CoreHandle*)m_stream.apiHandle; handle->drainCounter = 2; - - stopStream(); + return stopStream(); } // This function will be called by a spawned thread when the user @@ -1096,135 +1066,133 @@ void airtaudio::api::Core::abortStream(void) // aborted. It is better to handle it this way because the // callbackEvent() function probably should return before the AudioDeviceStop() // function is called. -static void *coreStopStream(void *ptr) -{ - CallbackInfo *info = (CallbackInfo *) ptr; - RtApiCore *object = (RtApiCore *) info->object; - +static void coreStopStream(void *_ptr) { + CallbackInfo* info = (CallbackInfo*)_ptr; + RtApiCore* object = (RtApiCore*)info->object; object->stopStream(); - pthread_exit(NULL); } -bool airtaudio::api::Core::callbackEvent(AudioDeviceID deviceId, - const AudioBufferList *inBufferList, - const AudioBufferList *outBufferList) -{ - if (m_stream.state == STREAM_STOPPED || m_stream.state == STREAM_STOPPING) return SUCCESS; - if (m_stream.state == STREAM_CLOSED) { - m_errorText = "airtaudio::api::Core::callbackEvent(): the stream is closed ... this shouldn't happen!"; - error(airtaudio::errorWarning); - return FAILURE; - } - - CallbackInfo *info = (CallbackInfo *) &m_stream.callbackInfo; - CoreHandle *handle = (CoreHandle *) m_stream.apiHandle; - - // Check if we were draining the stream and signal is finished. - if (handle->drainCounter > 3) { - pthread_t threadId; - - m_stream.state = STREAM_STOPPING; - if (handle->internalDrain == true) - pthread_create(&threadId, NULL, coreStopStream, info); - else // external call to stopStream() - pthread_cond_signal(&handle->condition); +bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId, + const AudioBufferList *_inBufferList, + const AudioBufferList *_outBufferList) { + if ( m_stream.state == STREAM_STOPPED + || m_stream.state == STREAM_STOPPING) { + return SUCCESS; + } + if (m_stream.state == STREAM_CLOSED) { + ATA_ERROR("airtaudio::api::Core::callbackEvent(): the stream is closed ... this shouldn't happen!"); + return FAILURE; + } + CallbackInfo *info = (CallbackInfo *) &m_stream.callbackInfo; + CoreHandle *handle = (CoreHandle *) m_stream.apiHandle; + // Check if we were draining the stream and signal is finished. + if (handle->drainCounter > 3) { + m_stream.state = STREAM_STOPPING; + if (handle->internalDrain == true) { + new std::thread(coreStopStream, info); + } else { + // external call to stopStream() + handle->condition.notify_one(); + } return SUCCESS; } - AudioDeviceID outputDevice = handle->id[0]; - // Invoke user callback to get fresh output data UNLESS we are // draining stream or duplex mode AND the input/output devices are // different AND this function is called for the input device. - if (handle->drainCounter == 0 && (m_stream.mode != DUPLEX || deviceId == outputDevice)) { + if (handle->drainCounter == 0 && (m_stream.mode != DUPLEX || _deviceId == outputDevice)) { airtaudio::AirTAudioCallback callback = (airtaudio::AirTAudioCallback) info->callback; double streamTime = getStreamTime(); rtaudio::streamStatus status = 0; - if (m_stream.mode != INPUT && handle->xrun[0] == true) { + if ( m_stream.mode != INPUT + && handle->xrun[0] == true) { status |= RTAUDIO_OUTPUT_UNDERFLOW; handle->xrun[0] = false; } - if (m_stream.mode != OUTPUT && handle->xrun[1] == true) { + if ( m_stream.mode != OUTPUT + && handle->xrun[1] == true) { status |= RTAUDIO_INPUT_OVERFLOW; handle->xrun[1] = false; } - - int32_t cbReturnValue = callback(m_stream.userBuffer[0], m_stream.userBuffer[1], - m_stream.bufferSize, streamTime, status, info->userData); + int32_t cbReturnValue = callback(m_stream.userBuffer[0], + m_stream.userBuffer[1], + m_stream.bufferSize, + streamTime, + status, + info->userData); if (cbReturnValue == 2) { m_stream.state = STREAM_STOPPING; handle->drainCounter = 2; abortStream(); return SUCCESS; - } - else if (cbReturnValue == 1) { + } else if (cbReturnValue == 1) { handle->drainCounter = 1; handle->internalDrain = true; } } - - if (m_stream.mode == OUTPUT || (m_stream.mode == DUPLEX && deviceId == outputDevice)) { - - if (handle->drainCounter > 1) { // write zeros to the output stream - + if ( m_stream.mode == OUTPUT + || ( m_stream.mode == DUPLEX + && _deviceId == outputDevice)) { + if (handle->drainCounter > 1) { + // write zeros to the output stream if (handle->nStreams[0] == 1) { - memset(outBufferList->mBuffers[handle->iStream[0]].mData, - 0, - outBufferList->mBuffers[handle->iStream[0]].mDataByteSize); - } - else { // fill multiple streams with zeros + memset(_outBufferList->mBuffers[handle->iStream[0]].mData, + 0, + _outBufferList->mBuffers[handle->iStream[0]].mDataByteSize); + } else { + // fill multiple streams with zeros for (uint32_t i=0; inStreams[0]; i++) { - memset(outBufferList->mBuffers[handle->iStream[0]+i].mData, - 0, - outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize); + memset(_outBufferList->mBuffers[handle->iStream[0]+i].mData, + 0, + _outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize); } } - } - else if (handle->nStreams[0] == 1) { - if (m_stream.doConvertBuffer[0]) { // convert directly to CoreAudio stream buffer - convertBuffer((char *) outBufferList->mBuffers[handle->iStream[0]].mData, - m_stream.userBuffer[0], m_stream.convertInfo[0]); + } else if (handle->nStreams[0] == 1) { + if (m_stream.doConvertBuffer[0]) { + // convert directly to CoreAudio stream buffer + convertBuffer((char*)_outBufferList->mBuffers[handle->iStream[0]].mData, + m_stream.userBuffer[0], + m_stream.convertInfo[0]); + } else { + // copy from user buffer + memcpy(_outBufferList->mBuffers[handle->iStream[0]].mData, + m_stream.userBuffer[0], + _outBufferList->mBuffers[handle->iStream[0]].mDataByteSize); } - else { // copy from user buffer - memcpy(outBufferList->mBuffers[handle->iStream[0]].mData, - m_stream.userBuffer[0], - outBufferList->mBuffers[handle->iStream[0]].mDataByteSize); - } - } - else { // fill multiple streams - Float32 *inBuffer = (Float32 *) m_stream.userBuffer[0]; + } else { + // fill multiple streams + float *inBuffer = (float *) m_stream.userBuffer[0]; if (m_stream.doConvertBuffer[0]) { convertBuffer(m_stream.deviceBuffer, m_stream.userBuffer[0], m_stream.convertInfo[0]); - inBuffer = (Float32 *) m_stream.deviceBuffer; + inBuffer = (float *) m_stream.deviceBuffer; } - if (m_stream.deviceInterleaved[0] == false) { // mono mode - UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize; + uint32_t bufferBytes = _outBufferList->mBuffers[handle->iStream[0]].mDataByteSize; for (uint32_t i=0; imBuffers[handle->iStream[0]+i].mData, - (void *)&inBuffer[i*m_stream.bufferSize], bufferBytes); + memcpy(_outBufferList->mBuffers[handle->iStream[0]+i].mData, + (void *)&inBuffer[i*m_stream.bufferSize], + bufferBytes); } - } - else { // fill multiple multi-channel streams with interleaved data - UInt32 streamChannels, channelsLeft, inJump, outJump, inOffset; - Float32 *out, *in; - + } else { + // fill multiple multi-channel streams with interleaved data + uint32_t streamChannels, channelsLeft, inJump, outJump, inOffset; + float *out, *in; bool inInterleaved = (m_stream.userInterleaved) ? true : false; - UInt32 inChannels = m_stream.nUserChannels[0]; + uint32_t inChannels = m_stream.nUserChannels[0]; if (m_stream.doConvertBuffer[0]) { inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode inChannels = m_stream.nDeviceChannels[0]; } - - if (inInterleaved) inOffset = 1; - else inOffset = m_stream.bufferSize; - + if (inInterleaved) { + inOffset = 1; + } else { + inOffset = m_stream.bufferSize; + } channelsLeft = inChannels; for (uint32_t i=0; inStreams[0]; i++) { in = inBuffer; - out = (Float32 *) outBufferList->mBuffers[handle->iStream[0]+i].mData; - streamChannels = outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels; - + out = (float *) _outBufferList->mBuffers[handle->iStream[0]+i].mData; + streamChannels = _outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels; outJump = 0; // Account for possible channel offset in first stream if (i == 0 && m_stream.channelOffset[0] > 0) { @@ -1232,23 +1200,19 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID deviceId, outJump = m_stream.channelOffset[0]; out += outJump; } - // Account for possible unfilled channels at end of the last stream if (streamChannels > channelsLeft) { outJump = streamChannels - channelsLeft; streamChannels = channelsLeft; } - // Determine input buffer offsets and skips if (inInterleaved) { inJump = inChannels; in += inChannels - channelsLeft; - } - else { + } else { inJump = 1; in += (inChannels - channelsLeft) * inOffset; } - for (uint32_t i=0; idrainCounter) { handle->drainCounter++; goto unlock; } } - AudioDeviceID inputDevice; inputDevice = handle->id[1]; - if (m_stream.mode == INPUT || (m_stream.mode == DUPLEX && deviceId == inputDevice)) { - + if ( m_stream.mode == INPUT + || ( m_stream.mode == DUPLEX + && _deviceId == inputDevice)) { if (handle->nStreams[1] == 1) { - if (m_stream.doConvertBuffer[1]) { // convert directly from CoreAudio stream buffer + if (m_stream.doConvertBuffer[1]) { + // convert directly from CoreAudio stream buffer convertBuffer(m_stream.userBuffer[1], - (char *) inBufferList->mBuffers[handle->iStream[1]].mData, - m_stream.convertInfo[1]); - } - else { // copy to user buffer + (char *) _inBufferList->mBuffers[handle->iStream[1]].mData, + m_stream.convertInfo[1]); + } else { // copy to user buffer memcpy(m_stream.userBuffer[1], - inBufferList->mBuffers[handle->iStream[1]].mData, - inBufferList->mBuffers[handle->iStream[1]].mDataByteSize); + _inBufferList->mBuffers[handle->iStream[1]].mData, + _inBufferList->mBuffers[handle->iStream[1]].mDataByteSize); } - } - else { // read from multiple streams - Float32 *outBuffer = (Float32 *) m_stream.userBuffer[1]; - if (m_stream.doConvertBuffer[1]) outBuffer = (Float32 *) m_stream.deviceBuffer; - - if (m_stream.deviceInterleaved[1] == false) { // mono mode - UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize; + } else { // read from multiple streams + float *outBuffer = (float *) m_stream.userBuffer[1]; + if (m_stream.doConvertBuffer[1]) { + outBuffer = (float *) m_stream.deviceBuffer; + } + if (m_stream.deviceInterleaved[1] == false) { + // mono mode + uint32_t bufferBytes = _inBufferList->mBuffers[handle->iStream[1]].mDataByteSize; for (uint32_t i=0; imBuffers[handle->iStream[1]+i].mData, bufferBytes); + _inBufferList->mBuffers[handle->iStream[1]+i].mData, + bufferBytes); } - } - else { // read from multiple multi-channel streams - UInt32 streamChannels, channelsLeft, inJump, outJump, outOffset; - Float32 *out, *in; - + } else { + // read from multiple multi-channel streams + uint32_t streamChannels, channelsLeft, inJump, outJump, outOffset; + float *out, *in; bool outInterleaved = (m_stream.userInterleaved) ? true : false; - UInt32 outChannels = m_stream.nUserChannels[1]; + uint32_t outChannels = m_stream.nUserChannels[1]; if (m_stream.doConvertBuffer[1]) { outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode outChannels = m_stream.nDeviceChannels[1]; } - - if (outInterleaved) outOffset = 1; - else outOffset = m_stream.bufferSize; - + if (outInterleaved) { + outOffset = 1; + } else { + outOffset = m_stream.bufferSize; + } channelsLeft = outChannels; for (uint32_t i=0; inStreams[1]; i++) { out = outBuffer; - in = (Float32 *) inBufferList->mBuffers[handle->iStream[1]+i].mData; - streamChannels = inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels; - + in = (float *) _inBufferList->mBuffers[handle->iStream[1]+i].mData; + streamChannels = _inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels; inJump = 0; // Account for possible channel offset in first stream if (i == 0 && m_stream.channelOffset[1] > 0) { @@ -1321,23 +1285,19 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID deviceId, inJump = m_stream.channelOffset[1]; in += inJump; } - // Account for possible unread channels at end of the last stream if (streamChannels > channelsLeft) { inJump = streamChannels - channelsLeft; streamChannels = channelsLeft; } - // Determine output buffer offsets and skips if (outInterleaved) { outJump = outChannels; out += outChannels - channelsLeft; - } - else { + } else { outJump = 1; out += (outChannels - channelsLeft) * outOffset; } - for (uint32_t i=0; i +#include airtaudio::Api* airtaudio::api::Ds::Create(void) { return new airtaudio::api::Ds(); @@ -843,7 +844,7 @@ bool airtaudio::api::Ds::probeDeviceOpen(uint32_t device, StreamMode mode, uint3 return FAILURE; } -void airtaudio::api::Ds::closeStream() +enum airtaudio::errorType airtaudio::api::Ds::closeStream() { if (m_stream.state == STREAM_CLOSED) { m_errorText = "airtaudio::api::Ds::closeStream(): no open stream to close!"; @@ -897,9 +898,11 @@ void airtaudio::api::Ds::closeStream() m_stream.state = STREAM_CLOSED; } -void airtaudio::api::Ds::startStream() +enum airtaudio::errorType airtaudio::api::Ds::startStream() { - verifyStream(); + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } if (m_stream.state == STREAM_RUNNING) { m_errorText = "airtaudio::api::Ds::startStream(): the stream is already running!"; error(airtaudio::errorWarning); @@ -953,9 +956,11 @@ void airtaudio::api::Ds::startStream() if (FAILED(result)) error(airtaudio::errorSystemError); } -void airtaudio::api::Ds::stopStream() +enum airtaudio::errorType airtaudio::api::Ds::stopStream() { - verifyStream(); + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } if (m_stream.state == STREAM_STOPPED) { m_errorText = "airtaudio::api::Ds::stopStream(): the stream is already stopped!"; error(airtaudio::errorWarning); @@ -1050,9 +1055,11 @@ void airtaudio::api::Ds::stopStream() if (FAILED(result)) error(airtaudio::errorSystemError); } -void airtaudio::api::Ds::abortStream() +enum airtaudio::errorType airtaudio::api::Ds::abortStream() { - verifyStream(); + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } if (m_stream.state == STREAM_STOPPED) { m_errorText = "airtaudio::api::Ds::abortStream(): the stream is already stopped!"; error(airtaudio::errorWarning); @@ -1492,9 +1499,9 @@ static std::string convertTChar(LPCTSTR name) } static BOOL CALLBACK deviceQueryCallback(LPGUID lpguid, - LPCTSTR description, - LPCTSTR module, - LPVOID lpContext) + LPCTSTR description, + LPCTSTR module, + LPVOID lpContext) { struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext; std::vector& dsDevices = *probeInfo.dsDevices; @@ -1572,55 +1579,39 @@ static BOOL CALLBACK deviceQueryCallback(LPGUID lpguid, static const char* getErrorString(int32_t code) { switch (code) { - - case DSERR_ALLOCATED: - return "Already allocated"; - - case DSERR_CONTROLUNAVAIL: - return "Control unavailable"; - - case DSERR_INVALIDPARAM: - return "Invalid parameter"; - - case DSERR_INVALIDCALL: - return "Invalid call"; - - case DSERR_GENERIC: - return "Generic error"; - - case DSERR_PRIOLEVELNEEDED: - return "Priority level needed"; - - case DSERR_OUTOFMEMORY: - return "Out of memory"; - - case DSERR_BADFORMAT: - return "The sample rate or the channel format is not supported"; - - case DSERR_UNSUPPORTED: - return "Not supported"; - - case DSERR_NODRIVER: - return "No driver"; - - case DSERR_ALREADYINITIALIZED: - return "Already initialized"; - - case DSERR_NOAGGREGATION: - return "No aggregation"; - - case DSERR_BUFFERLOST: - return "Buffer lost"; - - case DSERR_OTHERAPPHASPRIO: - return "Another application already has priority"; - - case DSERR_UNINITIALIZED: - return "Uninitialized"; - - default: - return "DirectSound unknown error"; + case DSERR_ALLOCATED: + return "Already allocated"; + case DSERR_CONTROLUNAVAIL: + return "Control unavailable"; + case DSERR_INVALIDPARAM: + return "Invalid parameter"; + case DSERR_INVALIDCALL: + return "Invalid call"; + case DSERR_GENERIC: + return "Generic error"; + case DSERR_PRIOLEVELNEEDED: + return "Priority level needed"; + case DSERR_OUTOFMEMORY: + return "Out of memory"; + case DSERR_BADFORMAT: + return "The sample rate or the channel format is not supported"; + case DSERR_UNSUPPORTED: + return "Not supported"; + case DSERR_NODRIVER: + return "No driver"; + case DSERR_ALREADYINITIALIZED: + return "Already initialized"; + case DSERR_NOAGGREGATION: + return "No aggregation"; + case DSERR_BUFFERLOST: + return "Buffer lost"; + case DSERR_OTHERAPPHASPRIO: + return "Another application already has priority"; + case DSERR_UNINITIALIZED: + return "Uninitialized"; + default: + return "DirectSound unknown error"; } } -//******************** End of __WINDOWS_DS__ *********************// + #endif diff --git a/airtaudio/api/Ds.h b/airtaudio/api/Ds.h index 5d6022f..4695937 100644 --- a/airtaudio/api/Ds.h +++ b/airtaudio/api/Ds.h @@ -24,10 +24,10 @@ namespace airtaudio { uint32_t getDefaultOutputDevice(void); uint32_t getDefaultInputDevice(void); airtaudio::DeviceInfo getDeviceInfo(uint32_t _device); - void closeStream(void); - void startStream(void); - void stopStream(void); - void abortStream(void); + enum airtaudio::errorType closeStream(void); + enum airtaudio::errorType startStream(void); + enum airtaudio::errorType stopStream(void); + enum airtaudio::errorType abortStream(void); long getStreamLatency(void); // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, diff --git a/airtaudio/api/Dummy.cpp b/airtaudio/api/Dummy.cpp index 5f4d8f8..4321da4 100644 --- a/airtaudio/api/Dummy.cpp +++ b/airtaudio/api/Dummy.cpp @@ -7,6 +7,8 @@ */ #if defined(__AIRTAUDIO_DUMMY__) +#include +#include airtaudio::Api* airtaudio::api::Dummy::Create(void) { return new airtaudio::api::Dummy(); @@ -28,20 +30,20 @@ rtaudio::DeviceInfo airtaudio::api::Dummy::getDeviceInfo(uint32_t _device) { return info; } -void airtaudio::api::Dummy::closeStream(void) { - +enum airtaudio::errorType airtaudio::api::Dummy::closeStream(void) { + return airtaudio::errorNone; } -void airtaudio::api::Dummy::startStream(void) { - +enum airtaudio::errorType airtaudio::api::Dummy::startStream(void) { + return airtaudio::errorNone; } -void airtaudio::api::Dummy::stopStream(void) { - +enum airtaudio::errorType airtaudio::api::Dummy::stopStream(void) { + return airtaudio::errorNone; } -void airtaudio::api::Dummy::abortStream(void) { - +enum airtaudio::errorType airtaudio::api::Dummy::abortStream(void) { + return airtaudio::errorNone; } bool airtaudio::api::Dummy::probeDeviceOpen(uint32_t _device, diff --git a/airtaudio/api/Dummy.h b/airtaudio/api/Dummy.h index 2f3f0b9..73a6af7 100644 --- a/airtaudio/api/Dummy.h +++ b/airtaudio/api/Dummy.h @@ -23,10 +23,10 @@ namespace airtaudio { } uint32_t getDeviceCount(void); airtaudio::DeviceInfo getDeviceInfo(uint32_t _device); - void closeStream(void); - void startStream(void); - void stopStream(void); - void abortStream(void); + enum airtaudio::errorType closeStream(void); + enum airtaudio::errorType startStream(void); + enum airtaudio::errorType stopStream(void); + enum airtaudio::errorType abortStream(void); private: bool probeDeviceOpen(uint32_t _device, airtaudio::api::StreamMode _mode, diff --git a/airtaudio/api/Jack.cpp b/airtaudio/api/Jack.cpp index d035315..6d248c1 100644 --- a/airtaudio/api/Jack.cpp +++ b/airtaudio/api/Jack.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include airtaudio::Api* airtaudio::api::Jack::Create(void) { @@ -74,14 +75,8 @@ struct JackHandle { } }; -static void jackSilentError(const char *) {}; - airtaudio::api::Jack::Jack(void) { // Nothing to do here. -#if !defined(__RTAUDIO_DEBUG__) - // Turn off Jack's internal error reporting. - jack_set_error_function(&jackSilentError); -#endif } airtaudio::api::Jack::~Jack(void) { @@ -122,16 +117,15 @@ uint32_t airtaudio::api::Jack::getDeviceCount(void) { return nDevices; } -airtaudio::DeviceInfo airtaudio::api::Jack::getDeviceInfo(uint32_t _device) -{ +airtaudio::DeviceInfo airtaudio::api::Jack::getDeviceInfo(uint32_t _device) { airtaudio::DeviceInfo info; info.probed = false; jack_options_t options = (jack_options_t) (JackNoStartServer); //JackNullOption jack_status_t *status = NULL; jack_client_t *client = jack_client_open("RtApiJackInfo", options, status); if (client == NULL) { - m_errorText = "airtaudio::api::Jack::getDeviceInfo: Jack server not found or connection error!"; - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Jack::getDeviceInfo: Jack server not found or connection error!"); + // TODO : airtaudio::errorWarning; return info; } const char **ports; @@ -157,18 +151,15 @@ airtaudio::DeviceInfo airtaudio::api::Jack::getDeviceInfo(uint32_t _device) } while (ports[++nPorts]); free(ports); } - if (_device >= nDevices) { jack_client_close(client); - m_errorText = "airtaudio::api::Jack::getDeviceInfo: device ID is invalid!"; - error(airtaudio::errorInvalidUse); + ATA_ERROR("airtaudio::api::Jack::getDeviceInfo: device ID is invalid!"); + // TODO : airtaudio::errorInvalidUse; return info; } - // Get the current jack server sample rate. info.sampleRates.clear(); info.sampleRates.push_back(jack_get_sample_rate(client)); - // Count the available ports containing the client name as device // channels. Jack "input ports" equal RtAudio output channels. uint32_t nChannels = 0; @@ -192,8 +183,8 @@ airtaudio::DeviceInfo airtaudio::api::Jack::getDeviceInfo(uint32_t _device) } if (info.outputChannels == 0 && info.inputChannels == 0) { jack_client_close(client); - m_errorText = "airtaudio::api::Jack::getDeviceInfo: error determining Jack input/output channels!"; - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Jack::getDeviceInfo: error determining Jack input/output channels!"); + // TODO : airtaudio::errorWarning; return info; } // If device opens for both playback and capture, we determine the channels. @@ -229,11 +220,10 @@ static int32_t jackCallbackHandler(jack_nframes_t _nframes, void *_infoPointer) // server signals that it is shutting down. It is necessary to handle // it this way because the jackShutdown() function must return before // the jack_deactivate() function (in closeStream()) will return. -static void *jackCloseStream(void *_ptr) { +static void jackCloseStream(void *_ptr) { airtaudio::CallbackInfo* info = (airtaudio::CallbackInfo*)_ptr; airtaudio::api::Jack* object = (airtaudio::api::Jack*)info->object; object->closeStream(); - pthread_exit(NULL); } static void jackShutdown(void* _infoPointer) { @@ -247,9 +237,8 @@ static void jackShutdown(void* _infoPointer) { if (object->isStreamRunning() == false) { return; } - pthread_t threadId; - pthread_create(&threadId, NULL, jackCloseStream, info); - std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl; + new std::thread(jackCloseStream, info); + ATA_ERROR("RtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!"); } static int32_t jackXrun(void* _infoPointer) { @@ -263,36 +252,35 @@ static int32_t jackXrun(void* _infoPointer) { return 0; } -bool airtaudio::api::Jack::probeDeviceOpen(uint32_t device, - airtaudio::api::StreamMode mode, - uint32_t channels, - uint32_t firstChannel, - uint32_t sampleRate, - airtaudio::format format, - uint32_t *bufferSize, - airtaudio::StreamOptions *options) { +bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device, + airtaudio::api::StreamMode _mode, + uint32_t _channels, + uint32_t _firstChannel, + uint32_t _sampleRate, + airtaudio::format _format, + uint32_t* _bufferSize, + airtaudio::StreamOptions* _options) { JackHandle *handle = (JackHandle *) m_stream.apiHandle; // Look for jack server and try to become a client (only do once per stream). jack_client_t *client = 0; - if (mode == OUTPUT || (mode == INPUT && m_stream.mode != OUTPUT)) { + if ( _mode == OUTPUT + || ( _mode == INPUT + && m_stream.mode != OUTPUT)) { jack_options_t jackoptions = (jack_options_t) (JackNoStartServer); //JackNullOption; jack_status_t *status = NULL; - if (options && !options->streamName.empty()) { - client = jack_client_open(options->streamName.c_str(), jackoptions, status); + if (_options && !_options->streamName.empty()) { + client = jack_client_open(_options->streamName.c_str(), jackoptions, status); } else { client = jack_client_open("RtApiJack", jackoptions, status); } if (client == 0) { - m_errorText = "airtaudio::api::Jack::probeDeviceOpen: Jack server not found or connection error!"; - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Jack::probeDeviceOpen: Jack server not found or connection error!"); return FAILURE; } - } - else { + } else { // The handle must have been created on an earlier pass. client = handle->client; } - const char **ports; std::string port, previousPort, deviceName; uint32_t nPorts = 0, nDevices = 0; @@ -306,7 +294,9 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t device, if (iColon != std::string::npos) { port = port.substr(0, iColon); if (port != previousPort) { - if (nDevices == device) deviceName = port; + if (nDevices == _device) { + deviceName = port; + } nDevices++; previousPort = port; } @@ -314,156 +304,141 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t device, } while (ports[++nPorts]); free(ports); } - - if (device >= nDevices) { - m_errorText = "airtaudio::api::Jack::probeDeviceOpen: device ID is invalid!"; + if (_device >= nDevices) { + ATA_ERROR("airtaudio::api::Jack::probeDeviceOpen: device ID is invalid!"); return FAILURE; } - // Count the available ports containing the client name as device // channels. Jack "input ports" equal RtAudio output channels. uint32_t nChannels = 0; uint64_t flag = JackPortIsInput; - if (mode == INPUT) flag = JackPortIsOutput; + if (_mode == INPUT) flag = JackPortIsOutput; ports = jack_get_ports(client, deviceName.c_str(), NULL, flag); if (ports) { - while (ports[ nChannels ]) nChannels++; + while (ports[ nChannels ]) { + nChannels++; + } free(ports); } - // Compare the jack ports for specified client to the requested number of channels. - if (nChannels < (channels + firstChannel)) { - m_errorStream << "airtaudio::api::Jack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ")."; - m_errorText = m_errorStream.str(); + if (nChannels < (_channels + _firstChannel)) { + ATA_ERROR("airtaudio::api::Jack::probeDeviceOpen: requested number of channels (" << _channels << ") + offset (" << _firstChannel << ") not found for specified device (" << _device << ":" << deviceName << ")."); return FAILURE; } - // Check the jack server sample rate. uint32_t jackRate = jack_get_sample_rate(client); - if (sampleRate != jackRate) { + if (_sampleRate != jackRate) { jack_client_close(client); - m_errorStream << "airtaudio::api::Jack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ")."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Jack::probeDeviceOpen: the requested sample rate (" << _sampleRate << ") is different than the JACK server rate (" << jackRate << ")."); return FAILURE; } m_stream.sampleRate = jackRate; - // Get the latency of the JACK port. ports = jack_get_ports(client, deviceName.c_str(), NULL, flag); - if (ports[ firstChannel ]) { + if (ports[ _firstChannel ]) { // Added by Ge Wang - jack_latency_callback_mode_t cbmode = (mode == INPUT ? JackCaptureLatency : JackPlaybackLatency); + jack_latency_callback_mode_t cbmode = (_mode == INPUT ? JackCaptureLatency : JackPlaybackLatency); // the range (usually the min and max are equal) jack_latency_range_t latrange; latrange.min = latrange.max = 0; // get the latency range - jack_port_get_latency_range(jack_port_by_name(client, ports[firstChannel]), cbmode, &latrange); + jack_port_get_latency_range(jack_port_by_name(client, ports[_firstChannel]), cbmode, &latrange); // be optimistic, use the min! - m_stream.latency[mode] = latrange.min; - //m_stream.latency[mode] = jack_port_get_latency(jack_port_by_name(client, ports[ firstChannel ])); + m_stream.latency[_mode] = latrange.min; + //m_stream.latency[_mode] = jack_port_get_latency(jack_port_by_name(client, ports[ _firstChannel ])); } free(ports); - // The jack server always uses 32-bit floating-point data. - m_stream.deviceFormat[mode] = FLOAT32; - m_stream.userFormat = format; - - if (options && options->flags & NONINTERLEAVED) m_stream.userInterleaved = false; - else m_stream.userInterleaved = true; - + m_stream.deviceFormat[_mode] = FLOAT32; + m_stream.userFormat = _format; + if (_options && _options->flags & NONINTERLEAVED) { + m_stream.userInterleaved = false; + } else { + m_stream.userInterleaved = true; + } // Jack always uses non-interleaved buffers. - m_stream.deviceInterleaved[mode] = false; - + m_stream.deviceInterleaved[_mode] = false; // Jack always provides host byte-ordered data. - m_stream.doByteSwap[mode] = false; - + m_stream.doByteSwap[_mode] = false; // Get the buffer size. The buffer size and number of buffers // (periods) is set when the jack server is started. m_stream.bufferSize = (int) jack_get_buffer_size(client); - *bufferSize = m_stream.bufferSize; - - m_stream.nDeviceChannels[mode] = channels; - m_stream.nUserChannels[mode] = channels; - + *_bufferSize = m_stream.bufferSize; + m_stream.nDeviceChannels[_mode] = _channels; + m_stream.nUserChannels[_mode] = _channels; // Set flags for buffer conversion. - m_stream.doConvertBuffer[mode] = false; - if (m_stream.userFormat != m_stream.deviceFormat[mode]) - m_stream.doConvertBuffer[mode] = true; - if (m_stream.userInterleaved != m_stream.deviceInterleaved[mode] && - m_stream.nUserChannels[mode] > 1) - m_stream.doConvertBuffer[mode] = true; - + m_stream.doConvertBuffer[_mode] = false; + if (m_stream.userFormat != m_stream.deviceFormat[_mode]) { + m_stream.doConvertBuffer[_mode] = true; + } + if ( m_stream.userInterleaved != m_stream.deviceInterleaved[_mode] + && m_stream.nUserChannels[_mode] > 1) { + m_stream.doConvertBuffer[_mode] = true; + } // Allocate our JackHandle structure for the stream. if (handle == 0) { - try { - handle = new JackHandle; - } - catch (std::bad_alloc&) { - m_errorText = "airtaudio::api::Jack::probeDeviceOpen: error allocating JackHandle memory."; + handle = new JackHandle; + if (handle == NULL) { + ATA_ERROR("airtaudio::api::Jack::probeDeviceOpen: error allocating JackHandle memory."); goto error; } m_stream.apiHandle = (void *) handle; handle->client = client; } - handle->deviceName[mode] = deviceName; - + handle->deviceName[_mode] = deviceName; // Allocate necessary internal buffers. uint64_t bufferBytes; - bufferBytes = m_stream.nUserChannels[mode] * *bufferSize * formatBytes(m_stream.userFormat); - m_stream.userBuffer[mode] = (char *) calloc(bufferBytes, 1); - if (m_stream.userBuffer[mode] == NULL) { - m_errorText = "airtaudio::api::Jack::probeDeviceOpen: error allocating user buffer memory."; + bufferBytes = m_stream.nUserChannels[_mode] * *_bufferSize * formatBytes(m_stream.userFormat); + m_stream.userBuffer[_mode] = (char *) calloc(bufferBytes, 1); + if (m_stream.userBuffer[_mode] == NULL) { + ATA_ERROR("airtaudio::api::Jack::probeDeviceOpen: error allocating user buffer memory."); goto error; } - - if (m_stream.doConvertBuffer[mode]) { - + if (m_stream.doConvertBuffer[_mode]) { bool makeBuffer = true; - if (mode == OUTPUT) + if (_mode == OUTPUT) { bufferBytes = m_stream.nDeviceChannels[0] * formatBytes(m_stream.deviceFormat[0]); - else { // mode == INPUT + } else { // _mode == INPUT bufferBytes = m_stream.nDeviceChannels[1] * formatBytes(m_stream.deviceFormat[1]); if (m_stream.mode == OUTPUT && m_stream.deviceBuffer) { uint64_t bytesOut = m_stream.nDeviceChannels[0] * formatBytes(m_stream.deviceFormat[0]); - if (bufferBytes < bytesOut) makeBuffer = false; + if (bufferBytes < bytesOut) { + makeBuffer = false; + } } } - if (makeBuffer) { - bufferBytes *= *bufferSize; + bufferBytes *= *_bufferSize; if (m_stream.deviceBuffer) free(m_stream.deviceBuffer); m_stream.deviceBuffer = (char *) calloc(bufferBytes, 1); if (m_stream.deviceBuffer == NULL) { - m_errorText = "airtaudio::api::Jack::probeDeviceOpen: error allocating device buffer memory."; + ATA_ERROR("airtaudio::api::Jack::probeDeviceOpen: error allocating device buffer memory."); goto error; } } } - // Allocate memory for the Jack ports (channels) identifiers. - handle->ports[mode] = (jack_port_t **) malloc (sizeof (jack_port_t *) * channels); - if (handle->ports[mode] == NULL) { - m_errorText = "airtaudio::api::Jack::probeDeviceOpen: error allocating port memory."; + handle->ports[_mode] = (jack_port_t **) malloc (sizeof (jack_port_t *) * _channels); + if (handle->ports[_mode] == NULL) { + ATA_ERROR("airtaudio::api::Jack::probeDeviceOpen: error allocating port memory."); goto error; } - - m_stream.device[mode] = device; - m_stream.channelOffset[mode] = firstChannel; + m_stream.device[_mode] = _device; + m_stream.channelOffset[_mode] = _firstChannel; m_stream.state = STREAM_STOPPED; m_stream.callbackInfo.object = (void *) this; - - if (m_stream.mode == OUTPUT && mode == INPUT) + if ( m_stream.mode == OUTPUT + && _mode == INPUT) { // We had already set up the stream for output. m_stream.mode = DUPLEX; - else { - m_stream.mode = mode; + } else { + m_stream.mode = _mode; jack_set_process_callback(handle->client, jackCallbackHandler, (void *) &m_stream.callbackInfo); jack_set_xrun_callback(handle->client, jackXrun, (void *) &handle); jack_on_shutdown(handle->client, jackShutdown, (void *) &m_stream.callbackInfo); } - // Register our ports. char label[64]; - if (mode == OUTPUT) { + if (_mode == OUTPUT) { for (uint32_t i=0; iports[0][i] = jack_port_register(handle->client, @@ -482,15 +457,13 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t device, 0); } } - // Setup the buffer conversion information structure. We don't use // buffers to do channel offsets, so we override that parameter // here. - if (m_stream.doConvertBuffer[mode]) { - setConvertInfo(mode, 0); + if (m_stream.doConvertBuffer[_mode]) { + setConvertInfo(_mode, 0); } return SUCCESS; - error: if (handle) { jack_client_close(handle->client); @@ -501,42 +474,34 @@ error: free(handle->ports[1]); } delete handle; - m_stream.apiHandle = 0; + m_stream.apiHandle = NULL; } - for (int32_t iii=0; iii<2; ++iii) { if (m_stream.userBuffer[iii]) { free(m_stream.userBuffer[iii]); - m_stream.userBuffer[iii] = 0; + m_stream.userBuffer[iii] = NULL; } } - if (m_stream.deviceBuffer) { free(m_stream.deviceBuffer); - m_stream.deviceBuffer = 0; + m_stream.deviceBuffer = NULL; } - return FAILURE; } -void airtaudio::api::Jack::closeStream(void) -{ +enum airtaudio::errorType airtaudio::api::Jack::closeStream(void) { if (m_stream.state == STREAM_CLOSED) { - m_errorText = "airtaudio::api::Jack::closeStream(): no open stream to close!"; - error(airtaudio::errorWarning); - return; + ATA_ERROR("airtaudio::api::Jack::closeStream(): no open stream to close!"); + return airtaudio::errorWarning; } - JackHandle *handle = (JackHandle *) m_stream.apiHandle; - if (handle) { - - if (m_stream.state == STREAM_RUNNING) + if (handle != NULL) { + if (m_stream.state == STREAM_RUNNING) { jack_deactivate(handle->client); - + } jack_client_close(handle->client); } - - if (handle) { + if (handle != NULL) { if (handle->ports[0]) { free(handle->ports[0]); } @@ -544,52 +509,47 @@ void airtaudio::api::Jack::closeStream(void) free(handle->ports[1]); } delete handle; - m_stream.apiHandle = 0; + m_stream.apiHandle = NULL; } - for (int32_t i=0; i<2; i++) { if (m_stream.userBuffer[i]) { free(m_stream.userBuffer[i]); - m_stream.userBuffer[i] = 0; + m_stream.userBuffer[i] = NULL; } } - if (m_stream.deviceBuffer) { free(m_stream.deviceBuffer); - m_stream.deviceBuffer = 0; + m_stream.deviceBuffer = NULL; } - m_stream.mode = UNINITIALIZED; m_stream.state = STREAM_CLOSED; + return airtaudio::errorNone; } -void airtaudio::api::Jack::startStream(void) -{ - verifyStream(); - if (m_stream.state == STREAM_RUNNING) { - m_errorText = "airtaudio::api::Jack::startStream(): the stream is already running!"; - error(airtaudio::errorWarning); - return; +enum airtaudio::errorType airtaudio::api::Jack::startStream(void) { + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } + if (m_stream.state == STREAM_RUNNING) { + ATA_ERROR("airtaudio::api::Jack::startStream(): the stream is already running!"); + return airtaudio::errorWarning; } - JackHandle *handle = (JackHandle *) m_stream.apiHandle; int32_t result = jack_activate(handle->client); if (result) { - m_errorText = "airtaudio::api::Jack::startStream(): unable to activate JACK client!"; + ATA_ERROR("airtaudio::api::Jack::startStream(): unable to activate JACK client!"); goto unlock; } - const char **ports; - // Get the list of available ports. - if (m_stream.mode == OUTPUT || m_stream.mode == DUPLEX) { + if ( m_stream.mode == OUTPUT + || m_stream.mode == DUPLEX) { result = 1; ports = jack_get_ports(handle->client, handle->deviceName[0].c_str(), NULL, JackPortIsInput); if (ports == NULL) { - m_errorText = "airtaudio::api::Jack::startStream(): error determining available JACK input ports!"; + ATA_ERROR("airtaudio::api::Jack::startStream(): error determining available JACK input ports!"); goto unlock; } - // Now make the port connections. Since RtAudio wasn't designed to // allow the user to select particular channels of a device, we'll // just open the first "nChannels" ports with offset. @@ -599,50 +559,51 @@ void airtaudio::api::Jack::startStream(void) result = jack_connect(handle->client, jack_port_name(handle->ports[0][i]), ports[ m_stream.channelOffset[0] + i ]); if (result) { free(ports); - m_errorText = "airtaudio::api::Jack::startStream(): error connecting output ports!"; + ATA_ERROR("airtaudio::api::Jack::startStream(): error connecting output ports!"); goto unlock; } } free(ports); } - - if (m_stream.mode == INPUT || m_stream.mode == DUPLEX) { + if ( m_stream.mode == INPUT + || m_stream.mode == DUPLEX) { result = 1; ports = jack_get_ports(handle->client, handle->deviceName[1].c_str(), NULL, JackPortIsOutput); if (ports == NULL) { - m_errorText = "airtaudio::api::Jack::startStream(): error determining available JACK output ports!"; + ATA_ERROR("airtaudio::api::Jack::startStream(): error determining available JACK output ports!"); goto unlock; } - - // Now make the port connections. See note above. + // Now make the port connections. See note above. for (uint32_t i=0; iclient, ports[ m_stream.channelOffset[1] + i ], jack_port_name(handle->ports[1][i])); + } if (result) { free(ports); - m_errorText = "airtaudio::api::Jack::startStream(): error connecting input ports!"; + ATA_ERROR("airtaudio::api::Jack::startStream(): error connecting input ports!"); goto unlock; } } free(ports); } - handle->drainCounter = 0; handle->internalDrain = false; m_stream.state = STREAM_RUNNING; - - unlock: - if (result == 0) return; - error(airtaudio::errorSystemError); +unlock: + if (result == 0) { + return airtaudio::errorNone; + } + return airtaudio::errorSystemError; } -void airtaudio::api::Jack::stopStream(void) { - verifyStream(); +enum airtaudio::errorType airtaudio::api::Jack::stopStream(void) { + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } if (m_stream.state == STREAM_STOPPED) { - m_errorText = "airtaudio::api::Jack::stopStream(): the stream is already stopped!"; - error(airtaudio::errorWarning); - return; + ATA_ERROR("airtaudio::api::Jack::stopStream(): the stream is already stopped!"); + return airtaudio::errorWarning; } JackHandle *handle = (JackHandle *) m_stream.apiHandle; if ( m_stream.mode == OUTPUT @@ -655,21 +616,20 @@ void airtaudio::api::Jack::stopStream(void) { } jack_deactivate(handle->client); m_stream.state = STREAM_STOPPED; + return airtaudio::errorNone; } -void airtaudio::api::Jack::abortStream(void) -{ - verifyStream(); - if (m_stream.state == STREAM_STOPPED) { - m_errorText = "airtaudio::api::Jack::abortStream(): the stream is already stopped!"; - error(airtaudio::errorWarning); - return; +enum airtaudio::errorType airtaudio::api::Jack::abortStream(void) { + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } + if (m_stream.state == STREAM_STOPPED) { + ATA_ERROR("airtaudio::api::Jack::abortStream(): the stream is already stopped!"); + return airtaudio::errorWarning; } - JackHandle *handle = (JackHandle *) m_stream.apiHandle; handle->drainCounter = 2; - - stopStream(); + return stopStream(); } // This function will be called by a spawned thread when the user @@ -683,23 +643,21 @@ static void jackStopStream(void *_ptr) { object->stopStream(); } -bool airtaudio::api::Jack::callbackEvent(uint64_t nframes) -{ - if (m_stream.state == STREAM_STOPPED || m_stream.state == STREAM_STOPPING) return SUCCESS; +bool airtaudio::api::Jack::callbackEvent(uint64_t _nframes) { + if ( m_stream.state == STREAM_STOPPED + || m_stream.state == STREAM_STOPPING) { + return SUCCESS; + } if (m_stream.state == STREAM_CLOSED) { - m_errorText = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!"; - error(airtaudio::errorWarning); + ATA_ERROR("RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!"); return FAILURE; } - if (m_stream.bufferSize != nframes) { - m_errorText = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!"; - error(airtaudio::errorWarning); + if (m_stream.bufferSize != _nframes) { + ATA_ERROR("RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!"); return FAILURE; } - CallbackInfo *info = (CallbackInfo *) &m_stream.callbackInfo; JackHandle *handle = (JackHandle *) m_stream.apiHandle; - // Check if we were draining the stream and signal is finished. if (handle->drainCounter > 3) { m_stream.state = STREAM_STOPPING; @@ -710,7 +668,6 @@ bool airtaudio::api::Jack::callbackEvent(uint64_t nframes) } return SUCCESS; } - // Invoke user callback first, to get fresh output data. if (handle->drainCounter == 0) { airtaudio::AirTAudioCallback callback = (airtaudio::AirTAudioCallback) info->callback; @@ -724,8 +681,12 @@ bool airtaudio::api::Jack::callbackEvent(uint64_t nframes) status |= INPUT_OVERFLOW; handle->xrun[1] = false; } - int32_t cbReturnValue = callback(m_stream.userBuffer[0], m_stream.userBuffer[1], - m_stream.bufferSize, streamTime, status, info->userData); + int32_t cbReturnValue = callback(m_stream.userBuffer[0], + m_stream.userBuffer[1], + m_stream.bufferSize, + streamTime, + status, + info->userData); if (cbReturnValue == 2) { m_stream.state = STREAM_STOPPING; handle->drainCounter = 2; @@ -737,61 +698,51 @@ bool airtaudio::api::Jack::callbackEvent(uint64_t nframes) handle->internalDrain = true; } } - jack_default_audio_sample_t *jackbuffer; - uint64_t bufferBytes = nframes * sizeof(jack_default_audio_sample_t); + uint64_t bufferBytes = _nframes * sizeof(jack_default_audio_sample_t); if (m_stream.mode == OUTPUT || m_stream.mode == DUPLEX) { - if (handle->drainCounter > 1) { // write zeros to the output stream - for (uint32_t i=0; iports[0][i], (jack_nframes_t) nframes); + jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[0][i], (jack_nframes_t) _nframes); memset(jackbuffer, 0, bufferBytes); } - - } - else if (m_stream.doConvertBuffer[0]) { - + } else if (m_stream.doConvertBuffer[0]) { convertBuffer(m_stream.deviceBuffer, m_stream.userBuffer[0], m_stream.convertInfo[0]); - for (uint32_t i=0; iports[0][i], (jack_nframes_t) nframes); + jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[0][i], (jack_nframes_t) _nframes); memcpy(jackbuffer, &m_stream.deviceBuffer[i*bufferBytes], bufferBytes); } - } - else { // no buffer conversion + } else { // no buffer conversion for (uint32_t i=0; iports[0][i], (jack_nframes_t) nframes); + jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[0][i], (jack_nframes_t) _nframes); memcpy(jackbuffer, &m_stream.userBuffer[0][i*bufferBytes], bufferBytes); } } - if (handle->drainCounter) { handle->drainCounter++; goto unlock; } } - if ( m_stream.mode == INPUT || m_stream.mode == DUPLEX) { if (m_stream.doConvertBuffer[1]) { for (uint32_t i=0; iports[1][i], (jack_nframes_t) nframes); + jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[1][i], (jack_nframes_t) _nframes); memcpy(&m_stream.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes); } convertBuffer(m_stream.userBuffer[1], m_stream.deviceBuffer, m_stream.convertInfo[1]); } else { // no buffer conversion for (uint32_t i=0; iports[1][i], (jack_nframes_t) nframes); + jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[1][i], (jack_nframes_t) _nframes); memcpy(&m_stream.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes); } } } - unlock: airtaudio::Api::tickStreamTime(); return SUCCESS; } - //******************** End of __UNIX_JACK__ *********************// + #endif + diff --git a/airtaudio/api/Jack.h b/airtaudio/api/Jack.h index c7b1911..4ad7e6e 100644 --- a/airtaudio/api/Jack.h +++ b/airtaudio/api/Jack.h @@ -22,10 +22,10 @@ namespace airtaudio { } uint32_t getDeviceCount(void); airtaudio::DeviceInfo getDeviceInfo(uint32_t _device); - void closeStream(void); - void startStream(void); - void stopStream(void); - void abortStream(void); + enum airtaudio::errorType closeStream(void); + enum airtaudio::errorType startStream(void); + enum airtaudio::errorType stopStream(void); + enum airtaudio::errorType abortStream(void); long getStreamLatency(void); // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, diff --git a/airtaudio/api/Oss.cpp b/airtaudio/api/Oss.cpp index 60e179d..7e00b3f 100644 --- a/airtaudio/api/Oss.cpp +++ b/airtaudio/api/Oss.cpp @@ -9,6 +9,7 @@ #if defined(__LINUX_OSS__) #include +#include #include #include @@ -616,8 +617,7 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t device, StreamMode mode, uint return FAILURE; } -void airtaudio::api::Oss::closeStream() -{ +enum airtaudio::errorType airtaudio::api::Oss::closeStream(void) { if (m_stream.state == STREAM_CLOSED) { m_errorText = "airtaudio::api::Oss::closeStream(): no open stream to close!"; error(airtaudio::errorWarning); @@ -664,9 +664,10 @@ void airtaudio::api::Oss::closeStream() m_stream.state = STREAM_CLOSED; } -void airtaudio::api::Oss::startStream() -{ - verifyStream(); +enum airtaudio::errorType airtaudio::api::Oss::startStream(void) { + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } if (m_stream.state == STREAM_RUNNING) { m_errorText = "airtaudio::api::Oss::startStream(): the stream is already running!"; error(airtaudio::errorWarning); @@ -686,9 +687,10 @@ void airtaudio::api::Oss::startStream() pthread_cond_signal(&handle->runnable); } -void airtaudio::api::Oss::stopStream() -{ - verifyStream(); +enum airtaudio::errorType airtaudio::api::Oss::stopStream(void) { + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } if (m_stream.state == STREAM_STOPPED) { m_errorText = "airtaudio::api::Oss::stopStream(): the stream is already stopped!"; error(airtaudio::errorWarning); @@ -758,9 +760,10 @@ void airtaudio::api::Oss::stopStream() error(airtaudio::errorSystemError); } -void airtaudio::api::Oss::abortStream() -{ - verifyStream(); +enum airtaudio::errorType airtaudio::api::Oss::abortStream(void) { + if (verifyStream() != airtaudio::errorNone) { + return airtaudio::errorFail; + } if (m_stream.state == STREAM_STOPPED) { m_errorText = "airtaudio::api::Oss::abortStream(): the stream is already stopped!"; error(airtaudio::errorWarning); @@ -804,8 +807,7 @@ void airtaudio::api::Oss::abortStream() *error(airtaudio::errorSystemError); } -void airtaudio::api::Oss::callbackEvent() -{ +void airtaudio::api::Oss::callbackEvent(void) { OssHandle *handle = (OssHandle *) m_stream.apiHandle; if (m_stream.state == STREAM_STOPPED) { m_stream.mutex.lock(); diff --git a/airtaudio/api/Oss.h b/airtaudio/api/Oss.h index 3734417..729bb82 100644 --- a/airtaudio/api/Oss.h +++ b/airtaudio/api/Oss.h @@ -22,10 +22,10 @@ namespace airtaudio { } uint32_t getDeviceCount(void); airtaudio::DeviceInfo getDeviceInfo(uint32_t _device); - void closeStream(void); - void startStream(void); - void stopStream(void); - void abortStream(void); + enum airtaudio::errorType closeStream(void); + enum airtaudio::errorType startStream(void); + enum airtaudio::errorType stopStream(void); + enum airtaudio::errorType abortStream(void); // 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 diff --git a/airtaudio/api/Pulse.cpp b/airtaudio/api/Pulse.cpp index 1f80824..37ba27b 100644 --- a/airtaudio/api/Pulse.cpp +++ b/airtaudio/api/Pulse.cpp @@ -12,6 +12,7 @@ #include #include #include +#include // Code written by Peter Meerwald, pmeerw@pmeerw.net // and Tristan Matthews. @@ -60,10 +61,10 @@ struct PulseAudioHandle { } }; -airtaudio::api::Pulse::~Pulse() -{ - if (m_stream.state != STREAM_CLOSED) +airtaudio::api::Pulse::~Pulse(void) { + if (m_stream.state != STREAM_CLOSED) { closeStream(); + } } uint32_t airtaudio::api::Pulse::getDeviceCount(void) { @@ -95,7 +96,7 @@ static void pulseaudio_callback(void* _user) { } } -void airtaudio::api::Pulse::closeStream(void) { +enum airtaudio::errorType airtaudio::api::Pulse::closeStream(void) { PulseAudioHandle *pah = static_cast(m_stream.apiHandle); m_stream.callbackInfo.isRunning = false; if (pah) { @@ -114,7 +115,7 @@ void airtaudio::api::Pulse::closeStream(void) { pa_simple_free(pah->s_rec); } delete pah; - m_stream.apiHandle = 0; + m_stream.apiHandle = NULL; } if (m_stream.userBuffer[0] != NULL) { free(m_stream.userBuffer[0]); @@ -126,6 +127,7 @@ void airtaudio::api::Pulse::closeStream(void) { } m_stream.state = STREAM_CLOSED; m_stream.mode = UNINITIALIZED; + return airtaudio::errorNone; } void airtaudio::api::Pulse::callbackEvent(void) { @@ -141,9 +143,7 @@ void airtaudio::api::Pulse::callbackEvent(void) { } } if (m_stream.state == STREAM_CLOSED) { - m_errorText = "airtaudio::api::Pulse::callbackEvent(): the stream is closed ... " - "this shouldn't happen!"; - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Pulse::callbackEvent(): the stream is closed ... this shouldn't happen!"); return; } airtaudio::AirTAudioCallback callback = (airtaudio::AirTAudioCallback) m_stream.callbackInfo.callback; @@ -178,9 +178,8 @@ void airtaudio::api::Pulse::callbackEvent(void) { bytes = m_stream.nUserChannels[OUTPUT] * m_stream.bufferSize * formatBytes(m_stream.userFormat); } if (pa_simple_write(pah->s_play, pulse_out, bytes, &pa_error) < 0) { - m_errorStream << "airtaudio::api::Pulse::callbackEvent: audio write error, " << pa_strerror(pa_error) << "."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Pulse::callbackEvent: audio write error, " << pa_strerror(pa_error) << "."); + return; } } if (m_stream.mode == INPUT || m_stream.mode == DUPLEX) { @@ -190,9 +189,8 @@ void airtaudio::api::Pulse::callbackEvent(void) { bytes = m_stream.nUserChannels[INPUT] * m_stream.bufferSize * formatBytes(m_stream.userFormat); } if (pa_simple_read(pah->s_rec, pulse_in, bytes, &pa_error) < 0) { - m_errorStream << "airtaudio::api::Pulse::callbackEvent: audio read error, " << pa_strerror(pa_error) << "."; - m_errorText = m_errorStream.str(); - error(airtaudio::errorWarning); + ATA_ERROR("airtaudio::api::Pulse::callbackEvent: audio read error, " << pa_strerror(pa_error) << "."); + return; } if (m_stream.doConvertBuffer[INPUT]) { convertBuffer(m_stream.userBuffer[INPUT], @@ -200,135 +198,127 @@ void airtaudio::api::Pulse::callbackEvent(void) { m_stream.convertInfo[INPUT]); } } - unlock: m_stream.mutex.unlock(); airtaudio::Api::tickStreamTime(); if (doStopStream == 1) { stopStream(); + return; } + return; } -void airtaudio::api::Pulse::startStream(void) { +enum airtaudio::errorType airtaudio::api::Pulse::startStream(void) { PulseAudioHandle *pah = static_cast(m_stream.apiHandle); if (m_stream.state == STREAM_CLOSED) { - m_errorText = "airtaudio::api::Pulse::startStream(): the stream is not open!"; - error(airtaudio::errorInvalidUse); - return; + ATA_ERROR("airtaudio::api::Pulse::startStream(): the stream is not open!"); + return airtaudio::errorInvalidUse; } if (m_stream.state == STREAM_RUNNING) { - m_errorText = "airtaudio::api::Pulse::startStream(): the stream is already running!"; - error(airtaudio::errorWarning); - return; + ATA_ERROR("airtaudio::api::Pulse::startStream(): the stream is already running!"); + return airtaudio::errorWarning; } m_stream.mutex.lock(); m_stream.state = STREAM_RUNNING; pah->runnable = true; pah->runnable_cv.notify_one(); m_stream.mutex.unlock(); + return airtaudio::errorNone; } -void airtaudio::api::Pulse::stopStream(void) { +enum airtaudio::errorType airtaudio::api::Pulse::stopStream(void) { PulseAudioHandle *pah = static_cast(m_stream.apiHandle); if (m_stream.state == STREAM_CLOSED) { - m_errorText = "airtaudio::api::Pulse::stopStream(): the stream is not open!"; - error(airtaudio::errorInvalidUse); - return; + ATA_ERROR("airtaudio::api::Pulse::stopStream(): the stream is not open!"); + return airtaudio::errorInvalidUse; } if (m_stream.state == STREAM_STOPPED) { - m_errorText = "airtaudio::api::Pulse::stopStream(): the stream is already stopped!"; - error(airtaudio::errorWarning); - return; + ATA_ERROR("airtaudio::api::Pulse::stopStream(): the stream is already stopped!"); + return airtaudio::errorWarning; } m_stream.state = STREAM_STOPPED; m_stream.mutex.lock(); if (pah && pah->s_play) { int32_t pa_error; if (pa_simple_drain(pah->s_play, &pa_error) < 0) { - m_errorStream << "airtaudio::api::Pulse::stopStream: error draining output device, " << - pa_strerror(pa_error) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Pulse::stopStream: error draining output device, " << pa_strerror(pa_error) << "."); m_stream.mutex.unlock(); - error(airtaudio::errorSystemError); - return; + return airtaudio::errorSystemError; } } m_stream.state = STREAM_STOPPED; m_stream.mutex.unlock(); + return airtaudio::errorNone; } -void airtaudio::api::Pulse::abortStream(void) { +enum airtaudio::errorType airtaudio::api::Pulse::abortStream(void) { PulseAudioHandle *pah = static_cast(m_stream.apiHandle); if (m_stream.state == STREAM_CLOSED) { - m_errorText = "airtaudio::api::Pulse::abortStream(): the stream is not open!"; - error(airtaudio::errorInvalidUse); - return; + ATA_ERROR("airtaudio::api::Pulse::abortStream(): the stream is not open!"); + return airtaudio::errorInvalidUse; } if (m_stream.state == STREAM_STOPPED) { - m_errorText = "airtaudio::api::Pulse::abortStream(): the stream is already stopped!"; - error(airtaudio::errorWarning); - return; + ATA_ERROR("airtaudio::api::Pulse::abortStream(): the stream is already stopped!"); + return airtaudio::errorWarning; } m_stream.state = STREAM_STOPPED; m_stream.mutex.lock(); if (pah && pah->s_play) { int32_t pa_error; if (pa_simple_flush(pah->s_play, &pa_error) < 0) { - m_errorStream << "airtaudio::api::Pulse::abortStream: error flushing output device, " << - pa_strerror(pa_error) << "."; - m_errorText = m_errorStream.str(); + ATA_ERROR("airtaudio::api::Pulse::abortStream: error flushing output device, " << pa_strerror(pa_error) << "."); m_stream.mutex.unlock(); - error(airtaudio::errorSystemError); - return; + return airtaudio::errorSystemError; } } m_stream.state = STREAM_STOPPED; m_stream.mutex.unlock(); + return airtaudio::errorNone; } -bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t device, - airtaudio::api::StreamMode mode, - uint32_t channels, - uint32_t firstChannel, - uint32_t sampleRate, - airtaudio::format format, - uint32_t *bufferSize, - airtaudio::StreamOptions *options) { +bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t _device, + airtaudio::api::StreamMode _mode, + uint32_t _channels, + uint32_t _firstChannel, + uint32_t _sampleRate, + airtaudio::format _format, + uint32_t *_bufferSize, + airtaudio::StreamOptions *_options) { PulseAudioHandle *pah = 0; uint64_t bufferBytes = 0; pa_sample_spec ss; - if (device != 0) { + if (_device != 0) { return false; } - if (mode != INPUT && mode != OUTPUT) { + if (_mode != INPUT && _mode != OUTPUT) { return false; } - if (channels != 1 && channels != 2) { - m_errorText = "airtaudio::api::Pulse::probeDeviceOpen: unsupported number of channels."; + if (_channels != 1 && _channels != 2) { + ATA_ERROR("airtaudio::api::Pulse::probeDeviceOpen: unsupported number of channels."); return false; } - ss.channels = channels; - if (firstChannel != 0) { + ss.channels = _channels; + if (_firstChannel != 0) { return false; } bool sr_found = false; for (const uint32_t *sr = SUPPORTED_SAMPLERATES; *sr; ++sr) { - if (sampleRate == *sr) { + if (_sampleRate == *sr) { sr_found = true; - m_stream.sampleRate = sampleRate; - ss.rate = sampleRate; + m_stream.sampleRate = _sampleRate; + ss.rate = _sampleRate; break; } } if (!sr_found) { - m_errorText = "airtaudio::api::Pulse::probeDeviceOpen: unsupported sample rate."; + ATA_ERROR("airtaudio::api::Pulse::probeDeviceOpen: unsupported sample rate."); return false; } bool sf_found = 0; for (const rtaudio_pa_format_mapping_t *sf = supported_sampleformats; sf->airtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf) { - if (format == sf->airtaudio_format) { + if (_format == sf->airtaudio_format) { sf_found = true; m_stream.userFormat = sf->airtaudio_format; ss.format = sf->pa_format; @@ -336,80 +326,77 @@ bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t device, } } if (!sf_found) { - m_errorText = "airtaudio::api::Pulse::probeDeviceOpen: unsupported sample format."; + ATA_ERROR("airtaudio::api::Pulse::probeDeviceOpen: unsupported sample format."); return false; } - // Set interleaving parameters. - if (options && options->flags & NONINTERLEAVED) { + if (_options && _options->flags & NONINTERLEAVED) { m_stream.userInterleaved = false; } else { m_stream.userInterleaved = true; } - m_stream.deviceInterleaved[mode] = true; + m_stream.deviceInterleaved[_mode] = true; m_stream.nBuffers = 1; - m_stream.doByteSwap[mode] = false; - m_stream.doConvertBuffer[mode] = channels > 1 && !m_stream.userInterleaved; - m_stream.deviceFormat[mode] = m_stream.userFormat; - m_stream.nUserChannels[mode] = channels; - m_stream.nDeviceChannels[mode] = channels + firstChannel; - m_stream.channelOffset[mode] = 0; - + m_stream.doByteSwap[_mode] = false; + m_stream.doConvertBuffer[_mode] = _channels > 1 && !m_stream.userInterleaved; + m_stream.deviceFormat[_mode] = m_stream.userFormat; + m_stream.nUserChannels[_mode] = _channels; + m_stream.nDeviceChannels[_mode] = _channels + _firstChannel; + m_stream.channelOffset[_mode] = 0; // Allocate necessary internal buffers. - bufferBytes = m_stream.nUserChannels[mode] * *bufferSize * formatBytes(m_stream.userFormat); - m_stream.userBuffer[mode] = (char *) calloc(bufferBytes, 1); - if (m_stream.userBuffer[mode] == NULL) { - m_errorText = "airtaudio::api::Pulse::probeDeviceOpen: error allocating user buffer memory."; + bufferBytes = m_stream.nUserChannels[_mode] * *_bufferSize * formatBytes(m_stream.userFormat); + m_stream.userBuffer[_mode] = (char *) calloc(bufferBytes, 1); + if (m_stream.userBuffer[_mode] == NULL) { + ATA_ERROR("airtaudio::api::Pulse::probeDeviceOpen: error allocating user buffer memory."); goto error; } - m_stream.bufferSize = *bufferSize; - - if (m_stream.doConvertBuffer[mode]) { + m_stream.bufferSize = *_bufferSize; + if (m_stream.doConvertBuffer[_mode]) { bool makeBuffer = true; - bufferBytes = m_stream.nDeviceChannels[mode] * formatBytes(m_stream.deviceFormat[mode]); - if (mode == INPUT) { + bufferBytes = m_stream.nDeviceChannels[_mode] * formatBytes(m_stream.deviceFormat[_mode]); + if (_mode == INPUT) { if (m_stream.mode == OUTPUT && m_stream.deviceBuffer) { uint64_t bytesOut = m_stream.nDeviceChannels[0] * formatBytes(m_stream.deviceFormat[0]); if (bufferBytes <= bytesOut) makeBuffer = false; } } if (makeBuffer) { - bufferBytes *= *bufferSize; + bufferBytes *= *_bufferSize; if (m_stream.deviceBuffer) free(m_stream.deviceBuffer); m_stream.deviceBuffer = (char *) calloc(bufferBytes, 1); if (m_stream.deviceBuffer == NULL) { - m_errorText = "airtaudio::api::Pulse::probeDeviceOpen: error allocating device buffer memory."; + ATA_ERROR("airtaudio::api::Pulse::probeDeviceOpen: error allocating device buffer memory."); goto error; } } } - m_stream.device[mode] = device; + m_stream.device[_mode] = _device; // Setup the buffer conversion information structure. - if (m_stream.doConvertBuffer[mode]) { - setConvertInfo(mode, firstChannel); + if (m_stream.doConvertBuffer[_mode]) { + setConvertInfo(_mode, _firstChannel); } if (!m_stream.apiHandle) { PulseAudioHandle *pah = new PulseAudioHandle; if (!pah) { - m_errorText = "airtaudio::api::Pulse::probeDeviceOpen: error allocating memory for handle."; + ATA_ERROR("airtaudio::api::Pulse::probeDeviceOpen: error allocating memory for handle."); goto error; } m_stream.apiHandle = pah; } pah = static_cast(m_stream.apiHandle); int32_t error; - switch (mode) { + switch (_mode) { case INPUT: pah->s_rec = pa_simple_new(NULL, "RtAudio", PA_STREAM_RECORD, NULL, "Record", &ss, NULL, NULL, &error); if (!pah->s_rec) { - m_errorText = "airtaudio::api::Pulse::probeDeviceOpen: error connecting input to PulseAudio server."; + ATA_ERROR("airtaudio::api::Pulse::probeDeviceOpen: error connecting input to PulseAudio server."); goto error; } break; case OUTPUT: pah->s_play = pa_simple_new(NULL, "RtAudio", PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error); if (!pah->s_play) { - m_errorText = "airtaudio::api::Pulse::probeDeviceOpen: error connecting output to PulseAudio server."; + ATA_ERROR("airtaudio::api::Pulse::probeDeviceOpen: error connecting output to PulseAudio server."); goto error; } break; @@ -417,8 +404,8 @@ bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t device, goto error; } if (m_stream.mode == UNINITIALIZED) { - m_stream.mode = mode; - } else if (m_stream.mode == mode) { + m_stream.mode = _mode; + } else if (m_stream.mode == _mode) { goto error; }else { m_stream.mode = DUPLEX; @@ -428,13 +415,12 @@ bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t device, m_stream.callbackInfo.isRunning = true; pah->thread = new std::thread(pulseaudio_callback, (void *)&m_stream.callbackInfo); if (pah->thread == NULL) { - m_errorText = "airtaudio::api::Pulse::probeDeviceOpen: error creating thread."; + ATA_ERROR("airtaudio::api::Pulse::probeDeviceOpen: error creating thread."); goto error; } } m_stream.state = STREAM_STOPPED; return true; - error: if (pah && m_stream.callbackInfo.isRunning) { delete pah; diff --git a/airtaudio/api/Pulse.h b/airtaudio/api/Pulse.h index 685e40b..dfad2a3 100644 --- a/airtaudio/api/Pulse.h +++ b/airtaudio/api/Pulse.h @@ -21,10 +21,10 @@ namespace airtaudio { } uint32_t getDeviceCount(void); airtaudio::DeviceInfo getDeviceInfo(uint32_t _device); - void closeStream(void); - void startStream(void); - void stopStream(void); - void abortStream(void); + enum airtaudio::errorType closeStream(void); + enum airtaudio::errorType startStream(void); + enum airtaudio::errorType stopStream(void); + enum airtaudio::errorType abortStream(void); // 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 diff --git a/airtaudio/base.h b/airtaudio/base.h index c999a28..877a059 100644 --- a/airtaudio/base.h +++ b/airtaudio/base.h @@ -28,6 +28,7 @@ namespace airtaudio { errorNone, //!< No error errorFail, //!< An error occure in the operation errorWarning, //!< A non-critical error. + errorInputNull, //!< null input or internal errror errorInvalidUse, //!< The function was called incorrectly. errorSystemError //!< A system error occured. }; @@ -171,14 +172,6 @@ namespace airtaudio { double _streamTime, airtaudio::streamStatus _status, void *_userData); - - /** - * @brief RtAudio error callback function prototype. - * @param _type Type of error. - * @param _errorText Error description. - */ - typedef void (*AirTAudioErrorCallback)(airtaudio::errorType _type, - const std::string &_errorText); } #include diff --git a/airtaudio/debug.cpp b/airtaudio/debug.cpp new file mode 100644 index 0000000..db189e5 --- /dev/null +++ b/airtaudio/debug.cpp @@ -0,0 +1,12 @@ +/** + * @author Edouard DUPIN + * + * @copyright 2011, Edouard DUPIN, all right reserved + * + * @license BSD v3 (see license file) + */ + +#include + +const char * airtaudioLibName = "airtaudio"; + diff --git a/airtaudio/debug.h b/airtaudio/debug.h new file mode 100644 index 0000000..0b2383d --- /dev/null +++ b/airtaudio/debug.h @@ -0,0 +1,28 @@ +/** + * @author Edouard DUPIN + * + * @copyright 2011, Edouard DUPIN, all right reserved + * + * @license BSD v3 (see license file) + */ + +#ifndef __EAUDIOFX_DEBUG_H__ +#define __EAUDIOFX_DEBUG_H__ + +#include +#include + +extern const char * airtaudioLibName; + +#define ATA_CRITICAL(data) ETK_CRITICAL(airtaudioLibName, data) +#define ATA_WARNING(data) ETK_WARNING(airtaudioLibName, data) +#define ATA_ERROR(data) ETK_ERROR(airtaudioLibName, data) +#define ATA_INFO(data) ETK_INFO(airtaudioLibName, data) +#define ATA_DEBUG(data) ETK_DEBUG(airtaudioLibName, data) +#define ATA_VERBOSE(data) ETK_VERBOSE(airtaudioLibName, data) +#define ATA_ASSERT(cond,data) ETK_ASSERT(airtaudioLibName, cond, data) +#define ATA_CHECK_INOUT(cond) ETK_CHECK_INOUT(airtaudioLibName, cond) +#define ATA_TODO(cond) ETK_TODO(airtaudioLibName, cond) + +#endif + diff --git a/lutin_airtaudio.py b/lutin_airtaudio.py index 5e7d418..90bc7d5 100644 --- a/lutin_airtaudio.py +++ b/lutin_airtaudio.py @@ -11,6 +11,7 @@ def create(target): myModule = module.Module(__file__, 'airtaudio', 'LIBRARY') myModule.add_src_file([ + 'airtaudio/debug.cpp', 'airtaudio/Interface.cpp', 'airtaudio/Api.cpp', 'airtaudio/api/Alsa.cpp', @@ -23,6 +24,7 @@ def create(target): 'airtaudio/api/Pulse.cpp' ]) + myModule.add_export_flag_CC(['-D__AIRTAUDIO_API_DUMMY_H__']) if target.name=="Windows": # ASIO API on Windows myModule.add_export_flag_CC(['__WINDOWS_ASIO__']) @@ -39,8 +41,6 @@ def create(target): myModule.add_export_flag_CC(['-D__LINUX_PULSE__']) myModule.add_export_flag_LD("-lpulse-simple") myModule.add_export_flag_LD("-lpulse") - #depending libs : - myModule.add_export_flag_LD("-lpthread") elif target.name=="MacOs": # MacOsX core myModule.add_export_flag_CC(['__MACOSX_CORE__']) @@ -53,6 +53,10 @@ def create(target): myModule.add_path(tools.get_current_path(__file__)+"/rtaudio/") myModule.add_path(tools.get_current_path(__file__)+"/rtaudio/include/") + # TODO : Remove this when debug will be unified ... + # name of the dependency + myModule.add_module_depend(['ewol']) + # add the currrent module at the return myModule