diff --git a/airtaudio/Api.cpp b/airtaudio/Api.cpp index 75b5505..044e7e6 100644 --- a/airtaudio/Api.cpp +++ b/airtaudio/Api.cpp @@ -49,7 +49,6 @@ airtaudio::Api::Api() : m_device[1] = 11111; m_state = airtaudio::state_closed; m_mode = airtaudio::mode_unknow; - m_apiHandle = 0; } airtaudio::Api::~Api() { diff --git a/airtaudio/Api.h b/airtaudio/Api.h index 49ae05e..e5fc721 100644 --- a/airtaudio/Api.h +++ b/airtaudio/Api.h @@ -38,13 +38,13 @@ namespace airtaudio { virtual airtaudio::DeviceInfo getDeviceInfo(uint32_t _device) = 0; virtual uint32_t getDefaultInputDevice(); virtual uint32_t getDefaultOutputDevice(); - enum airtaudio::error openStream(airtaudio::StreamParameters *_outputParameters, - airtaudio::StreamParameters *_inputParameters, + enum airtaudio::error openStream(airtaudio::StreamParameters* _outputParameters, + airtaudio::StreamParameters* _inputParameters, audio::format _format, uint32_t _sampleRate, - uint32_t *_bufferFrames, + uint32_t* _bufferFrames, airtaudio::AirTAudioCallback _callback, - airtaudio::StreamOptions *_options); + airtaudio::StreamOptions* _options); virtual enum airtaudio::error closeStream(); virtual enum airtaudio::error startStream() = 0; virtual enum airtaudio::error stopStream() = 0; @@ -62,8 +62,6 @@ namespace airtaudio { protected: mutable std::mutex m_mutex; uint32_t m_device[2]; // Playback and record, respectively. - // TODO : Remove this use derivative property of the c++ class ... - void *m_apiHandle; // void pointer for API specific stream handle information enum airtaudio::mode m_mode; // airtaudio::mode_output, airtaudio::mode_input, or airtaudio::mode_duplex. enum airtaudio::state m_state; // STOPPED, RUNNING, or CLOSED std::vector m_userBuffer[2]; // Playback and record, respectively. diff --git a/airtaudio/api/Alsa.cpp b/airtaudio/api/Alsa.cpp index 0b81679..0e7b5e4 100644 --- a/airtaudio/api/Alsa.cpp +++ b/airtaudio/api/Alsa.cpp @@ -21,29 +21,30 @@ airtaudio::Api* airtaudio::api::Alsa::Create() { return new airtaudio::api::Alsa(); } - -// A structure to hold various information related to the ALSA API -// implementation. -struct AlsaHandle { - snd_pcm_t *handles[2]; - bool synchronized; - bool xrun[2]; - std::condition_variable runnable_cv; - bool runnable; - - AlsaHandle() : - synchronized(false), - runnable(false) { - handles[0] = nullptr; - handles[1] = nullptr; - xrun[0] = false; - xrun[1] = false; - } +namespace airtaudio { + namespace api { + class AlsaPrivate { + public: + snd_pcm_t *handles[2]; + bool synchronized; + bool xrun[2]; + std::condition_variable runnable_cv; + bool runnable; + + AlsaPrivate() : + synchronized(false), + runnable(false) { + handles[0] = nullptr; + handles[1] = nullptr; + xrun[0] = false; + xrun[1] = false; + } + }; + }; }; - - -airtaudio::api::Alsa::Alsa() { +airtaudio::api::Alsa::Alsa() : + m_private(new airtaudio::api::AlsaPrivate()) { // Nothing to do here. } @@ -631,19 +632,7 @@ foundDevice: && m_nUserChannels[modeToIdTable(_mode)] > 1) { m_doConvertBuffer[modeToIdTable(_mode)] = true; } - // Allocate the ApiHandle if necessary and then save. - AlsaHandle *apiInfo = nullptr; - if (m_apiHandle == nullptr) { - apiInfo = (AlsaHandle *) new AlsaHandle; - if (apiInfo == nullptr) { - ATA_ERROR("error allocating AlsaHandle memory."); - goto error; - } - m_apiHandle = (void *) apiInfo; - } else { - apiInfo = (AlsaHandle *) m_apiHandle; - } - apiInfo->handles[modeToIdTable(_mode)] = phandle; + m_private->handles[modeToIdTable(_mode)] = phandle; phandle = 0; // Allocate necessary internal buffers. uint64_t bufferBytes; @@ -692,9 +681,9 @@ foundDevice: // We had already set up an output stream. m_mode = airtaudio::mode_duplex; // Link the streams if possible. - apiInfo->synchronized = false; - if (snd_pcm_link(apiInfo->handles[0], apiInfo->handles[1]) == 0) { - apiInfo->synchronized = true; + m_private->synchronized = false; + if (snd_pcm_link(m_private->handles[0], m_private->handles[1]) == 0) { + m_private->synchronized = true; } else { ATA_ERROR("unable to synchronize input and output devices."); // TODO : airtaudio::error_warning; @@ -712,16 +701,13 @@ foundDevice: } return true; error: - if (apiInfo != nullptr) { - if (apiInfo->handles[0]) { - snd_pcm_close(apiInfo->handles[0]); - } - if (apiInfo->handles[1]) { - snd_pcm_close(apiInfo->handles[1]); - } - delete apiInfo; - apiInfo = nullptr; - m_apiHandle = 0; + if (m_private->handles[0]) { + snd_pcm_close(m_private->handles[0]); + m_private->handles[0] = nullptr; + } + if (m_private->handles[1]) { + snd_pcm_close(m_private->handles[1]); + m_private->handles[1] = nullptr; } if (phandle) { snd_pcm_close(phandle); @@ -742,12 +728,11 @@ enum airtaudio::error airtaudio::api::Alsa::closeStream() { ATA_ERROR("no open stream to close!"); return airtaudio::error_warning; } - AlsaHandle *apiInfo = (AlsaHandle *) m_apiHandle; m_callbackInfo.isRunning = false; m_mutex.lock(); if (m_state == airtaudio::state_stopped) { - apiInfo->runnable = true; - apiInfo->runnable_cv.notify_one(); + m_private->runnable = true; + m_private->runnable_cv.notify_one(); } m_mutex.unlock(); if (m_callbackInfo.thread != nullptr) { @@ -757,23 +742,21 @@ enum airtaudio::error airtaudio::api::Alsa::closeStream() { m_state = airtaudio::state_stopped; if ( m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { - snd_pcm_drop(apiInfo->handles[0]); + snd_pcm_drop(m_private->handles[0]); } if ( m_mode == airtaudio::mode_input || m_mode == airtaudio::mode_duplex) { - snd_pcm_drop(apiInfo->handles[1]); + snd_pcm_drop(m_private->handles[1]); } } - if (apiInfo != nullptr) { - if (apiInfo->handles[0]) { - snd_pcm_close(apiInfo->handles[0]); - } - if (apiInfo->handles[1]) { - snd_pcm_close(apiInfo->handles[1]); - } - delete apiInfo; - apiInfo = nullptr; - m_apiHandle = 0; + // close all stream : + if (m_private->handles[0]) { + snd_pcm_close(m_private->handles[0]); + m_private->handles[0] = nullptr; + } + if (m_private->handles[1]) { + snd_pcm_close(m_private->handles[1]); + m_private->handles[1] = nullptr; } for (int32_t iii=0; iii<2; ++iii) { m_userBuffer[iii].clear(); @@ -799,8 +782,7 @@ enum airtaudio::error airtaudio::api::Alsa::startStream() { std::unique_lock lck(m_mutex); int32_t result = 0; snd_pcm_state_t state; - AlsaHandle *apiInfo = (AlsaHandle *) m_apiHandle; - snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; + snd_pcm_t **handle = (snd_pcm_t **) m_private->handles; if ( m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { if (handle[0] == nullptr) { @@ -820,7 +802,7 @@ enum airtaudio::error airtaudio::api::Alsa::startStream() { } if ( ( m_mode == airtaudio::mode_input || m_mode == airtaudio::mode_duplex) - && !apiInfo->synchronized) { + && !m_private->synchronized) { if (handle[1] == nullptr) { ATA_ERROR("send nullptr to alsa ..."); if (handle[0] != nullptr) { @@ -838,8 +820,8 @@ enum airtaudio::error airtaudio::api::Alsa::startStream() { } m_state = airtaudio::state_running; unlock: - apiInfo->runnable = true; - apiInfo->runnable_cv.notify_one(); + m_private->runnable = true; + m_private->runnable_cv.notify_one(); if (result >= 0) { return airtaudio::error_none; } @@ -857,11 +839,10 @@ enum airtaudio::error airtaudio::api::Alsa::stopStream() { m_state = airtaudio::state_stopped; std::unique_lock lck(m_mutex); int32_t result = 0; - AlsaHandle *apiInfo = (AlsaHandle *) m_apiHandle; - snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; + snd_pcm_t **handle = (snd_pcm_t **) m_private->handles; if ( m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { - if (apiInfo->synchronized) { + if (m_private->synchronized) { result = snd_pcm_drop(handle[0]); } else { result = snd_pcm_drain(handle[0]); @@ -873,7 +854,7 @@ enum airtaudio::error airtaudio::api::Alsa::stopStream() { } if ( ( m_mode == airtaudio::mode_input || m_mode == airtaudio::mode_duplex) - && !apiInfo->synchronized) { + && !m_private->synchronized) { result = snd_pcm_drop(handle[1]); if (result < 0) { ATA_ERROR("error stopping input pcm device, " << snd_strerror(result) << "."); @@ -898,8 +879,7 @@ enum airtaudio::error airtaudio::api::Alsa::abortStream() { m_state = airtaudio::state_stopped; std::unique_lock lck(m_mutex); int32_t result = 0; - AlsaHandle *apiInfo = (AlsaHandle *) m_apiHandle; - snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; + snd_pcm_t **handle = (snd_pcm_t **) m_private->handles; if ( m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { result = snd_pcm_drop(handle[0]); @@ -910,7 +890,7 @@ enum airtaudio::error airtaudio::api::Alsa::abortStream() { } if ( ( m_mode == airtaudio::mode_input || m_mode == airtaudio::mode_duplex) - && !apiInfo->synchronized) { + && !m_private->synchronized) { result = snd_pcm_drop(handle[1]); if (result < 0) { ATA_ERROR("error aborting input pcm device, " << snd_strerror(result) << "."); @@ -937,13 +917,12 @@ void airtaudio::api::Alsa::callbackEvent() { } void airtaudio::api::Alsa::callbackEventOneCycle() { - AlsaHandle *apiInfo = (AlsaHandle *) m_apiHandle; if (m_state == airtaudio::state_stopped) { std::unique_lock lck(m_mutex); // TODO : Set this back .... /* - while (!apiInfo->runnable) { - apiInfo->runnable_cv.wait(lck); + while (!m_private->runnable) { + m_private->runnable_cv.wait(lck); } */ if (m_state != airtaudio::state_running) { @@ -957,13 +936,13 @@ void airtaudio::api::Alsa::callbackEventOneCycle() { int32_t doStopStream = 0; double streamTime = getStreamTime(); enum airtaudio::status status = airtaudio::status_ok; - if (m_mode != airtaudio::mode_input && apiInfo->xrun[0] == true) { + if (m_mode != airtaudio::mode_input && m_private->xrun[0] == true) { status = airtaudio::status_underflow; - apiInfo->xrun[0] = false; + m_private->xrun[0] = false; } - if (m_mode != airtaudio::mode_output && apiInfo->xrun[1] == true) { + if (m_mode != airtaudio::mode_output && m_private->xrun[1] == true) { status = airtaudio::status_overflow; - apiInfo->xrun[1] = false; + m_private->xrun[1] = false; } doStopStream = m_callbackInfo.callback(&m_userBuffer[0][0], &m_userBuffer[1][0], @@ -985,7 +964,7 @@ void airtaudio::api::Alsa::callbackEventOneCycle() { snd_pcm_t **handle; snd_pcm_sframes_t frames; audio::format format; - handle = (snd_pcm_t **) apiInfo->handles; + handle = (snd_pcm_t **) m_private->handles; if ( m_mode == airtaudio::mode_input || m_mode == airtaudio::mode_duplex) { // Setup parameters. @@ -1013,7 +992,7 @@ void airtaudio::api::Alsa::callbackEventOneCycle() { if (result == -EPIPE) { snd_pcm_state_t state = snd_pcm_state(handle[1]); if (state == SND_PCM_STATE_XRUN) { - apiInfo->xrun[1] = true; + m_private->xrun[1] = true; result = snd_pcm_prepare(handle[1]); if (result < 0) { ATA_ERROR("error preparing device after overrun, " << snd_strerror(result) << "."); @@ -1076,7 +1055,7 @@ tryOutput: if (result == -EPIPE) { snd_pcm_state_t state = snd_pcm_state(handle[0]); if (state == SND_PCM_STATE_XRUN) { - apiInfo->xrun[0] = true; + m_private->xrun[0] = true; result = snd_pcm_prepare(handle[0]); if (result < 0) { ATA_ERROR("error preparing device after underrun, " << snd_strerror(result) << "."); diff --git a/airtaudio/api/Alsa.h b/airtaudio/api/Alsa.h index b99f88f..efceac2 100644 --- a/airtaudio/api/Alsa.h +++ b/airtaudio/api/Alsa.h @@ -10,6 +10,7 @@ namespace airtaudio { namespace api { + class AlsaPrivate; class Alsa: public airtaudio::Api { public: static airtaudio::Api* Create(); @@ -34,6 +35,7 @@ namespace airtaudio { private: static void alsaCallbackEvent(void* _userData); private: + std::unique_ptr m_private; std::vector m_devices; void saveDeviceInfo(); bool probeDeviceOpen(uint32_t _device, diff --git a/airtaudio/api/Asio.cpp b/airtaudio/api/Asio.cpp index ebb2ae0..34bc9e7 100644 --- a/airtaudio/api/Asio.cpp +++ b/airtaudio/api/Asio.cpp @@ -47,25 +47,31 @@ static ASIODriverInfo driverInfo; static CallbackInfo *asioCallbackInfo; static bool asioXRun; -struct AsioHandle { - int32_t drainCounter; // Tracks callback counts when draining - bool internalDrain; // Indicates if stop is initiated from callback or not. - ASIOBufferInfo *bufferInfos; - HANDLE condition; - AsioHandle() : - drainCounter(0), - internalDrain(false), - bufferInfos(0) { - +namespace airtaudio { + namespace api { + class AsioPrivate { + public: + int32_t drainCounter; // Tracks callback counts when draining + bool internalDrain; // Indicates if stop is initiated from callback or not. + ASIOBufferInfo *bufferInfos; + HANDLE condition; + AsioPrivate() : + drainCounter(0), + internalDrain(false), + bufferInfos(0) { + + } + }; } -}; +} // Function declarations (definitions at end of section) static const char* getAsioErrorString(ASIOError _result); static void sampleRateChanged(ASIOSampleRate _sRate); static long asioMessages(long _selector, long _value, void* _message, double* _opt); -airtaudio::api::Asio::Asio() { +airtaudio::api::Asio::Asio() : + m_private(new airtaudio::api::AsioPrivate()) { // ASIO cannot run on a multi-threaded appartment. You can call // CoInitialize beforehand, but it must be for appartment threading // (in which case, CoInitilialize will return S_FALSE here). @@ -403,23 +409,12 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device, m_nBuffers = 2; // ASIO always uses non-interleaved buffers. m_deviceInterleaved[modeToIdTable(_mode)] = false; - // Allocate, if necessary, our AsioHandle structure for the stream. - AsioHandle *handle = (AsioHandle *) m_apiHandle; - if (handle == nullptr) { - handle = new AsioHandle; - if (handle == nullptr) { - drivers.removeCurrentDriver(); - ATA_ERROR("error allocating AsioHandle memory."); - return false; - } - handle->bufferInfos = 0; - // Create a manual-reset event. - handle->condition = CreateEvent(nullptr, // no security - TRUE, // manual-reset - FALSE, // non-signaled initially - nullptr); // unnamed - m_apiHandle = (void *) handle; - } + m_private->bufferInfos = 0; + // Create a manual-reset event. + m_private->condition = CreateEvent(nullptr, // no security + TRUE, // manual-reset + FALSE, // non-signaled initially + nullptr); // unnamed // Create the ASIO internal buffers. Since RtAudio sets up input // and output separately, we'll have to dispose of previously // created output buffers for a duplex stream. @@ -427,21 +422,21 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device, if ( _mode == airtaudio::mode_input && m_mode == airtaudio::mode_output) { ASIODisposeBuffers(); - if (handle->bufferInfos == nullptr) { - free(handle->bufferInfos); - handle->bufferInfos = nullptr; + if (m_private->bufferInfos == nullptr) { + free(m_private->bufferInfos); + m_private->bufferInfos = nullptr; } } // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure. bool buffersAllocated = false; uint32_t i, nChannels = m_nDeviceChannels[0] + m_nDeviceChannels[1]; - handle->bufferInfos = (ASIOBufferInfo *) malloc(nChannels * sizeof(ASIOBufferInfo)); - if (handle->bufferInfos == nullptr) { + m_private->bufferInfos = (ASIOBufferInfo *) malloc(nChannels * sizeof(ASIOBufferInfo)); + if (m_private->bufferInfos == nullptr) { ATA_ERROR("error allocating bufferInfo memory for driver (" << driverName << ")."); goto error; } ASIOBufferInfo *infos; - infos = handle->bufferInfos; + infos = m_private->bufferInfos; for (i=0; iisInput = ASIOFalse; infos->channelNum = i + m_channelOffset[0]; @@ -457,7 +452,7 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device, asioCallbacks.sampleRateDidChange = &sampleRateChanged; asioCallbacks.asioMessage = &asioMessages; asioCallbacks.bufferSwitchTimeInfo = nullptr; - result = ASIOCreateBuffers(handle->bufferInfos, nChannels, m_bufferSize, &asioCallbacks); + result = ASIOCreateBuffers(m_private->bufferInfos, nChannels, m_bufferSize, &asioCallbacks); if (result != ASE_OK) { ATA_ERROR("driver (" << driverName << ") error (" << getAsioErrorString(result) << ") creating buffers."); goto error; @@ -536,15 +531,10 @@ error: ASIODisposeBuffers(); } drivers.removeCurrentDriver(); - if (handle) { - CloseHandle(handle->condition); - if (handle->bufferInfos) { - free(handle->bufferInfos); - handle->bufferInfos = nullptr; - } - delete handle; - handle = nullptr; - m_apiHandle = 0; + CloseHandle(m_private->condition); + if (m_private->bufferInfos != nullptr) { + free(m_private->bufferInfos); + m_private->bufferInfos = nullptr; } for (int32_t i=0; i<2; i++) { if (m_userBuffer[i]) { @@ -570,14 +560,9 @@ enum airtaudio::error airtaudio::api::Asio::closeStream() { } ASIODisposeBuffers(); drivers.removeCurrentDriver(); - AsioHandle *handle = (AsioHandle *) m_apiHandle; - if (handle) { - CloseHandle(handle->condition); - if (handle->bufferInfos) { - free(handle->bufferInfos); - } - delete handle; - m_apiHandle = 0; + CloseHandle(m_private->condition); + if (m_private->bufferInfos) { + free(m_private->bufferInfos); } for (int32_t i=0; i<2; i++) { if (m_userBuffer[i]) { @@ -604,15 +589,14 @@ enum airtaudio::error airtaudio::api::Asio::startStream() { ATA_ERROR("the stream is already running!"); return airtaudio::error_warning; } - AsioHandle *handle = (AsioHandle *) m_apiHandle; ASIOError result = ASIOStart(); if (result != ASE_OK) { ATA_ERROR("error (" << getAsioErrorString(result) << ") starting device."); goto unlock; } - handle->drainCounter = 0; - handle->internalDrain = false; - ResetEvent(handle->condition); + m_private->drainCounter = 0; + m_private->internalDrain = false; + ResetEvent(m_private->condition); m_state = airtaudio::state_running; asioXRun = false; unlock: @@ -631,11 +615,10 @@ enum airtaudio::error airtaudio::api::Asio::stopStream() { ATA_ERROR("the stream is already stopped!"); return airtaudio::error_warning; } - AsioHandle *handle = (AsioHandle *) m_apiHandle; if (m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { - if (handle->drainCounter == 0) { - handle->drainCounter = 2; - WaitForSingleObject(handle->condition, INFINITE); // block until signaled + if (m_private->drainCounter == 0) { + m_private->drainCounter = 2; + WaitForSingleObject(m_private->condition, INFINITE); // block until signaled } } m_state = airtaudio::state_stopped; @@ -663,7 +646,6 @@ enum airtaudio::error airtaudio::api::Asio::abortStream() { // noted where the device buffers need to be zeroed to avoid // continuing sound, even when the device buffers are completely // disposed. So now, calling abort is the same as calling stop. - // AsioHandle *handle = (AsioHandle *) m_apiHandle; // handle->drainCounter = 2; return stopStream(); } @@ -691,22 +673,25 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) { return false; } CallbackInfo *info = (CallbackInfo *) &m_callbackInfo; - AsioHandle *handle = (AsioHandle *) m_apiHandle; // Check if we were draining the stream and signal if finished. - if (handle->drainCounter > 3) { + if (m_private->drainCounter > 3) { m_state = airtaudio::state_stopping; - if (handle->internalDrain == false) { - SetEvent(handle->condition); + if (m_private->internalDrain == false) { + SetEvent(m_private->condition); } else { // spawn a thread to stop the stream unsigned threadId; - m_callbackInfo.thread = _beginthreadex(nullptr, 0, &asioStopStream, - &m_callbackInfo, 0, &threadId); + m_callbackInfo.thread = _beginthreadex(nullptr, + 0, + &asioStopStream, + &m_callbackInfo, + 0, + &threadId); } return true; } // Invoke user callback to get fresh output data UNLESS we are // draining stream. - if (handle->drainCounter == 0) { + if (m_private->drainCounter == 0) { double streamTime = getStreamTime(); rtaudio::streamStatus status = 0; if (m_mode != airtaudio::mode_input && asioXRun == true) { @@ -724,7 +709,7 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) { status); if (cbReturnValue == 2) { m_state = airtaudio::state_stopping; - handle->drainCounter = 2; + m_private->drainCounter = 2; unsigned threadId; m_callbackInfo.thread = _beginthreadex(nullptr, 0, @@ -734,8 +719,8 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) { &threadId); return true; } else if (cbReturnValue == 1) { - handle->drainCounter = 1; - handle->internalDrain = true; + m_private->drainCounter = 1; + m_private->internalDrain = true; } } uint32_t nChannels, bufferBytes, i, j; @@ -743,10 +728,10 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) { if ( m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { bufferBytes = m_bufferSize * audio::getFormatBytes(m_deviceFormat[0]); - if (handle->drainCounter > 1) { // write zeros to the output stream + if (m_private->drainCounter > 1) { // write zeros to the output stream for (i=0, j=0; ibufferInfos[i].isInput != ASIOTrue) { - memset(handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes); + if (m_private->bufferInfos[i].isInput != ASIOTrue) { + memset(m_private->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes); } } } else if (m_doConvertBuffer[0]) { @@ -757,8 +742,8 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) { m_deviceFormat[0]); } for (i=0, j=0; ibufferInfos[i].isInput != ASIOTrue) { - memcpy(handle->bufferInfos[i].buffers[bufferIndex], + if (m_private->bufferInfos[i].isInput != ASIOTrue) { + memcpy(m_private->bufferInfos[i].buffers[bufferIndex], &m_deviceBuffer[j++*bufferBytes], bufferBytes); } @@ -770,15 +755,15 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) { m_userFormat); } for (i=0, j=0; ibufferInfos[i].isInput != ASIOTrue) { - memcpy(handle->bufferInfos[i].buffers[bufferIndex], + if (m_private->bufferInfos[i].isInput != ASIOTrue) { + memcpy(m_private->bufferInfos[i].buffers[bufferIndex], &m_userBuffer[0][bufferBytes*j++], bufferBytes); } } } - if (handle->drainCounter) { - handle->drainCounter++; + if (m_private->drainCounter) { + m_private->drainCounter++; goto unlock; } } @@ -788,9 +773,9 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) { if (m_doConvertBuffer[1]) { // Always interleave ASIO input data. for (i=0, j=0; ibufferInfos[i].isInput == ASIOTrue) { + if (m_private->bufferInfos[i].isInput == ASIOTrue) { memcpy(&m_deviceBuffer[j++*bufferBytes], - handle->bufferInfos[i].buffers[bufferIndex], + m_private->bufferInfos[i].buffers[bufferIndex], bufferBytes); } } @@ -804,9 +789,9 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) { m_convertInfo[1]); } else { for (i=0, j=0; ibufferInfos[i].isInput == ASIOTrue) { + if (m_private->bufferInfos[i].isInput == ASIOTrue) { memcpy(&m_userBuffer[1][bufferBytes*j++], - handle->bufferInfos[i].buffers[bufferIndex], + m_private->bufferInfos[i].buffers[bufferIndex], bufferBytes); } } diff --git a/airtaudio/api/Asio.h b/airtaudio/api/Asio.h index e1ff933..06e7f34 100644 --- a/airtaudio/api/Asio.h +++ b/airtaudio/api/Asio.h @@ -10,6 +10,7 @@ namespace airtaudio { namespace api { + class AsioPrivate: class Asio: public airtaudio::Api { public: static airtaudio::Api* Create(); @@ -32,6 +33,7 @@ namespace airtaudio { // will most likely produce highly undesireable results! bool callbackEvent(long _bufferIndex); private: + std::unique_ptr m_private; std::vector m_devices; void saveDeviceInfo(); bool m_coInitialized; diff --git a/airtaudio/api/Core.cpp b/airtaudio/api/Core.cpp index d110adb..bf6fdc5 100644 --- a/airtaudio/api/Core.cpp +++ b/airtaudio/api/Core.cpp @@ -24,49 +24,38 @@ airtaudio::Api* airtaudio::api::Core::Create() { #undef __class__ #define __class__ "api::Core" -// The OS X CoreAudio API is designed to use a separate callback -// procedure for each of its audio devices. A single RtAudio duplex -// stream using two different devices is supported here, though it -// cannot be guaranteed to always behave correctly because we cannot -// synchronize these two callbacks. -// -// A property listener is installed for over/underrun information. -// However, no functionality is currently provided to allow property -// listeners to trigger user handlers because it is unclear what could -// be done if a critical stream parameter (buffer size, sample rate, -// device disconnect) notification arrived. The listeners entail -// quite a bit of extra code and most likely, a user program wouldn't -// be prepared for the result anyway. However, we do provide a flag -// to the client callback function to inform of an over/underrun. - -// A structure to hold various information related to the CoreAudio API -// implementation. -struct CoreHandle { - 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_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; - std::condition_variable 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; +namespace airtaudio { + namespace api { + class CorePrivate { + public: + 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_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; + std::condition_variable condition; + int32_t drainCounter; // Tracks callback counts when draining + bool internalDrain; // Indicates if stop is initiated from callback or not. + CorePrivate() : + 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() : + m_private(new airtaudio::api::CorePrivate()) { #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 @@ -402,14 +391,14 @@ OSStatus airtaudio::api::Core::callbackEvent(AudioDeviceID _inDevice, static OSStatus xrunListener(AudioObjectID _inDevice, uint32_t _nAddresses, const AudioObjectPropertyAddress _properties[], - void* _handlePointer) { - CoreHandle* handle = (CoreHandle*)_handlePointer; + void* _userData) { + airtaudio::api::Core* myClass = reinterpret_cast(_userData); for (uint32_t i=0; i<_nAddresses; i++) { if (_properties[i].mSelector == kAudioDeviceProcessorOverload) { if (_properties[i].mScope == kAudioDevicePropertyScopeInput) { - handle->xrun[1] = true; + myClass->m_private->xrun[1] = true; } else { - handle->xrun[0] = true; + myClass->m_private->xrun[0] = true; } } } @@ -803,21 +792,9 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device, } else if (monoMode) { m_doConvertBuffer[modeToIdTable(_mode)] = true; } - // Allocate our CoreHandle structure for the stream. - CoreHandle *handle = 0; - if (m_apiHandle == 0) { - handle = new CoreHandle; - if (handle == nullptr) { - ATA_ERROR("error allocating CoreHandle memory."); - return false; - } - m_apiHandle = (void *) handle; - } else { - handle = (CoreHandle *) m_apiHandle; - } - handle->iStream[modeToIdTable(_mode)] = firstStream; - handle->nStreams[modeToIdTable(_mode)] = streamCount; - handle->id[modeToIdTable(_mode)] = id; + m_private->iStream[modeToIdTable(_mode)] = firstStream; + m_private->nStreams[modeToIdTable(_mode)] = streamCount; + m_private->id[modeToIdTable(_mode)] = id; // Allocate necessary internal buffers. uint64_t bufferBytes; bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_userFormat); @@ -832,7 +809,7 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device, // "device buffers". However, we can't do this if using multiple // streams. if ( m_doConvertBuffer[modeToIdTable(_mode)] - && handle->nStreams[modeToIdTable(_mode)] > 1) { + && m_private->nStreams[modeToIdTable(_mode)] > 1) { bool makeBuffer = true; bufferBytes = m_nDeviceChannels[modeToIdTable(_mode)] * audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]); if (_mode == airtaudio::mode_input) { @@ -876,7 +853,7 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device, m_mode = airtaudio::mode_duplex; } else { #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) - result = AudioDeviceCreateIOProcID(id, &airtaudio::api::Core::callbackEvent, (void *) &m_callbackInfo, &handle->procId[modeToIdTable(_mode)]); + result = AudioDeviceCreateIOProcID(id, &airtaudio::api::Core::callbackEvent, (void *) &m_callbackInfo, &m_private->procId[modeToIdTable(_mode)]); #else // deprecated in favor of AudioDeviceCreateIOProcID() result = AudioDeviceAddIOProc(id, &airtaudio::api::Core::callbackEvent, (void *) &m_callbackInfo); @@ -894,13 +871,9 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device, } // Setup the device property listener for over/underload. property.mSelector = kAudioDeviceProcessorOverload; - result = AudioObjectAddPropertyListener(id, &property, xrunListener, (void *) handle); + result = AudioObjectAddPropertyListener(id, &property, xrunListener, this); return true; error: - if (handle) { - delete handle; - m_apiHandle = 0; - } for (int32_t i=0; i<2; i++) { if (m_userBuffer[i]) { free(m_userBuffer[i]); @@ -920,30 +893,29 @@ enum airtaudio::error airtaudio::api::Core::closeStream() { ATA_ERROR("no open stream to close!"); return airtaudio::error_warning; } - CoreHandle *handle = (CoreHandle *) m_apiHandle; if ( m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { if (m_state == airtaudio::state_running) { - AudioDeviceStop(handle->id[0], &airtaudio::api::Core::callbackEvent); + AudioDeviceStop(m_private->id[0], &airtaudio::api::Core::callbackEvent); } #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]); + AudioDeviceDestroyIOProcID(m_private->id[0], m_private->procId[0]); #else // deprecated in favor of AudioDeviceDestroyIOProcID() - AudioDeviceRemoveIOProc(handle->id[0], &airtaudio::api::Core::callbackEvent); + AudioDeviceRemoveIOProc(m_private->id[0], &airtaudio::api::Core::callbackEvent); #endif } if ( m_mode == airtaudio::mode_input || ( m_mode == airtaudio::mode_duplex && m_device[0] != m_device[1])) { if (m_state == airtaudio::state_running) { - AudioDeviceStop(handle->id[1], &airtaudio::api::Core::callbackEvent); + AudioDeviceStop(m_private->id[1], &airtaudio::api::Core::callbackEvent); } #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]); + AudioDeviceDestroyIOProcID(m_private->id[1], m_private->procId[1]); #else // deprecated in favor of AudioDeviceDestroyIOProcID() - AudioDeviceRemoveIOProc(handle->id[1], &airtaudio::api::Core::callbackEvent); + AudioDeviceRemoveIOProc(m_private->id[1], &airtaudio::api::Core::callbackEvent); #endif } for (int32_t i=0; i<2; i++) { @@ -956,8 +928,6 @@ enum airtaudio::error airtaudio::api::Core::closeStream() { free(m_deviceBuffer); m_deviceBuffer = nullptr; } - delete handle; - m_apiHandle = 0; m_mode = airtaudio::mode_unknow; m_state = airtaudio::state_closed; return airtaudio::error_none; @@ -972,10 +942,9 @@ enum airtaudio::error airtaudio::api::Core::startStream() { return airtaudio::error_warning; } OSStatus result = noErr; - CoreHandle *handle = (CoreHandle *) m_apiHandle; if ( m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { - result = AudioDeviceStart(handle->id[0], &airtaudio::api::Core::callbackEvent); + result = AudioDeviceStart(m_private->id[0], &airtaudio::api::Core::callbackEvent); if (result != noErr) { ATA_ERROR("system error (" << getErrorCode(result) << ") starting callback procedure on device (" << m_device[0] << ")."); goto unlock; @@ -984,14 +953,14 @@ enum airtaudio::error airtaudio::api::Core::startStream() { if ( m_mode == airtaudio::mode_input || ( m_mode == airtaudio::mode_duplex && m_device[0] != m_device[1])) { - result = AudioDeviceStart(handle->id[1], &airtaudio::api::Core::callbackEvent); + result = AudioDeviceStart(m_private->id[1], &airtaudio::api::Core::callbackEvent); if (result != noErr) { ATA_ERROR("system error starting input callback procedure on device (" << m_device[1] << ")."); goto unlock; } } - handle->drainCounter = 0; - handle->internalDrain = false; + m_private->drainCounter = 0; + m_private->internalDrain = false; m_state = airtaudio::state_running; unlock: if (result == noErr) { @@ -1009,15 +978,14 @@ enum airtaudio::error airtaudio::api::Core::stopStream() { return airtaudio::error_warning; } OSStatus result = noErr; - CoreHandle *handle = (CoreHandle *) m_apiHandle; if ( m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { - if (handle->drainCounter == 0) { + if (m_private->drainCounter == 0) { std::unique_lock lck(m_mutex); - handle->drainCounter = 2; - handle->condition.wait(lck); + m_private->drainCounter = 2; + m_private->condition.wait(lck); } - result = AudioDeviceStop(handle->id[0], &airtaudio::api::Core::callbackEvent); + result = AudioDeviceStop(m_private->id[0], &airtaudio::api::Core::callbackEvent); if (result != noErr) { ATA_ERROR("system error (" << getErrorCode(result) << ") stopping callback procedure on device (" << m_device[0] << ")."); goto unlock; @@ -1026,7 +994,7 @@ enum airtaudio::error airtaudio::api::Core::stopStream() { if ( m_mode == airtaudio::mode_input || ( m_mode == airtaudio::mode_duplex && m_device[0] != m_device[1])) { - result = AudioDeviceStop(handle->id[1], &airtaudio::api::Core::callbackEvent); + result = AudioDeviceStop(m_private->id[1], &airtaudio::api::Core::callbackEvent); if (result != noErr) { ATA_ERROR("system error (" << getErrorCode(result) << ") stopping input callback procedure on device (" << m_device[1] << ")."); goto unlock; @@ -1048,8 +1016,7 @@ enum airtaudio::error airtaudio::api::Core::abortStream() { ATA_ERROR("the stream is already stopped!"); return airtaudio::error_warning; } - CoreHandle* handle = (CoreHandle*)m_apiHandle; - handle->drainCounter = 2; + m_private->drainCounter = 2; return stopStream(); } @@ -1075,34 +1042,33 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId, return false; } CallbackInfo *info = (CallbackInfo *) &m_callbackInfo; - CoreHandle *handle = (CoreHandle *) m_apiHandle; // Check if we were draining the stream and signal is finished. - if (handle->drainCounter > 3) { + if (m_private->drainCounter > 3) { m_state = airtaudio::state_stopping; - if (handle->internalDrain == true) { + if (m_private->internalDrain == true) { new std::thread(&airtaudio::api::Core::coreStopStream, this); } else { // external call to stopStream() - handle->condition.notify_one(); + m_private->condition.notify_one(); } return true; } - AudioDeviceID outputDevice = handle->id[0]; + AudioDeviceID outputDevice = m_private->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_mode != airtaudio::mode_duplex || _deviceId == outputDevice)) { + if (m_private->drainCounter == 0 && (m_mode != airtaudio::mode_duplex || _deviceId == outputDevice)) { double streamTime = getStreamTime(); enum airtaudio::status status = airtaudio::status_ok; if ( m_mode != airtaudio::mode_input - && handle->xrun[0] == true) { + && m_private->xrun[0] == true) { status |= airtaudio::status_underflow; - handle->xrun[0] = false; + m_private->xrun[0] = false; } if ( m_mode != airtaudio::mode_output - && handle->xrun[1] == true) { + && m_private->xrun[1] == true) { status |= airtaudio::mode_input_OVERFLOW; - handle->xrun[1] = false; + m_private->xrun[1] = false; } int32_t cbReturnValue = info->callback(m_userBuffer[0], m_userBuffer[1], @@ -1111,42 +1077,42 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId, status); if (cbReturnValue == 2) { m_state = airtaudio::state_stopping; - handle->drainCounter = 2; + m_private->drainCounter = 2; abortStream(); return true; } else if (cbReturnValue == 1) { - handle->drainCounter = 1; - handle->internalDrain = true; + m_private->drainCounter = 1; + m_private->internalDrain = true; } } if ( m_mode == airtaudio::mode_output || ( m_mode == airtaudio::mode_duplex && _deviceId == outputDevice)) { - if (handle->drainCounter > 1) { + if (m_private->drainCounter > 1) { // write zeros to the output stream - if (handle->nStreams[0] == 1) { - memset(_outBufferList->mBuffers[handle->iStream[0]].mData, + if (m_private->nStreams[0] == 1) { + memset(_outBufferList->mBuffers[m_private->iStream[0]].mData, 0, - _outBufferList->mBuffers[handle->iStream[0]].mDataByteSize); + _outBufferList->mBuffers[m_private->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, + for (uint32_t i=0; inStreams[0]; i++) { + memset(_outBufferList->mBuffers[m_private->iStream[0]+i].mData, 0, - _outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize); + _outBufferList->mBuffers[m_private->iStream[0]+i].mDataByteSize); } } - } else if (handle->nStreams[0] == 1) { + } else if (m_private->nStreams[0] == 1) { if (m_doConvertBuffer[0]) { // convert directly to CoreAudio stream buffer - convertBuffer((char*)_outBufferList->mBuffers[handle->iStream[0]].mData, + convertBuffer((char*)_outBufferList->mBuffers[m_private->iStream[0]].mData, m_userBuffer[0], m_convertInfo[0]); } else { // copy from user buffer - memcpy(_outBufferList->mBuffers[handle->iStream[0]].mData, + memcpy(_outBufferList->mBuffers[m_private->iStream[0]].mData, m_userBuffer[0], - _outBufferList->mBuffers[handle->iStream[0]].mDataByteSize); + _outBufferList->mBuffers[m_private->iStream[0]].mDataByteSize); } } else { // fill multiple streams @@ -1156,9 +1122,9 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId, inBuffer = (float *) m_deviceBuffer; } if (m_deviceInterleaved[0] == false) { // mono mode - uint32_t bufferBytes = _outBufferList->mBuffers[handle->iStream[0]].mDataByteSize; + uint32_t bufferBytes = _outBufferList->mBuffers[m_private->iStream[0]].mDataByteSize; for (uint32_t i=0; imBuffers[handle->iStream[0]+i].mData, + memcpy(_outBufferList->mBuffers[m_private->iStream[0]+i].mData, (void *)&inBuffer[i*m_bufferSize], bufferBytes); } @@ -1178,10 +1144,10 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId, inOffset = m_bufferSize; } channelsLeft = inChannels; - for (uint32_t i=0; inStreams[0]; i++) { + for (uint32_t i=0; inStreams[0]; i++) { in = inBuffer; - out = (float *) _outBufferList->mBuffers[handle->iStream[0]+i].mData; - streamChannels = _outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels; + out = (float *) _outBufferList->mBuffers[m_private->iStream[0]+i].mData; + streamChannels = _outBufferList->mBuffers[m_private->iStream[0]+i].mNumberChannels; outJump = 0; // Account for possible channel offset in first stream if (i == 0 && m_channelOffset[0] > 0) { @@ -1213,26 +1179,26 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId, } } } - if (handle->drainCounter) { - handle->drainCounter++; + if (m_private->drainCounter) { + m_private->drainCounter++; goto unlock; } } AudioDeviceID inputDevice; - inputDevice = handle->id[1]; + inputDevice = m_private->id[1]; if ( m_mode == airtaudio::mode_input || ( m_mode == airtaudio::mode_duplex && _deviceId == inputDevice)) { - if (handle->nStreams[1] == 1) { + if (m_private->nStreams[1] == 1) { if (m_doConvertBuffer[1]) { // convert directly from CoreAudio stream buffer convertBuffer(m_userBuffer[1], - (char *) _inBufferList->mBuffers[handle->iStream[1]].mData, + (char *) _inBufferList->mBuffers[m_private->iStream[1]].mData, m_convertInfo[1]); } else { // copy to user buffer memcpy(m_userBuffer[1], - _inBufferList->mBuffers[handle->iStream[1]].mData, - _inBufferList->mBuffers[handle->iStream[1]].mDataByteSize); + _inBufferList->mBuffers[m_private->iStream[1]].mData, + _inBufferList->mBuffers[m_private->iStream[1]].mDataByteSize); } } else { // read from multiple streams float *outBuffer = (float *) m_userBuffer[1]; @@ -1241,10 +1207,10 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId, } if (m_deviceInterleaved[1] == false) { // mono mode - uint32_t bufferBytes = _inBufferList->mBuffers[handle->iStream[1]].mDataByteSize; + uint32_t bufferBytes = _inBufferList->mBuffers[m_private->iStream[1]].mDataByteSize; for (uint32_t i=0; imBuffers[handle->iStream[1]+i].mData, + _inBufferList->mBuffers[m_private->iStream[1]+i].mData, bufferBytes); } } else { @@ -1263,10 +1229,10 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId, outOffset = m_bufferSize; } channelsLeft = outChannels; - for (uint32_t i=0; inStreams[1]; i++) { + for (uint32_t i=0; inStreams[1]; i++) { out = outBuffer; - in = (float *) _inBufferList->mBuffers[handle->iStream[1]+i].mData; - streamChannels = _inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels; + in = (float *) _inBufferList->mBuffers[m_private->iStream[1]+i].mData; + streamChannels = _inBufferList->mBuffers[m_private->iStream[1]+i].mNumberChannels; inJump = 0; // Account for possible channel offset in first stream if (i == 0 && m_channelOffset[1] > 0) { diff --git a/airtaudio/api/Core.h b/airtaudio/api/Core.h index b9c9752..7158f94 100644 --- a/airtaudio/api/Core.h +++ b/airtaudio/api/Core.h @@ -12,6 +12,7 @@ namespace airtaudio { namespace api { + class CorePrivate; class Core: public airtaudio::Api { public: static airtaudio::Api* Create(); @@ -46,6 +47,7 @@ namespace airtaudio { void* _infoPointer); void coreStopStream(void *_userData); private: + std::unique_ptr m_private; bool probeDeviceOpen(uint32_t _device, airtaudio::mode _mode, uint32_t _channels, diff --git a/airtaudio/api/CoreIos.h b/airtaudio/api/CoreIos.h index 4669fc7..ac34ff6 100644 --- a/airtaudio/api/CoreIos.h +++ b/airtaudio/api/CoreIos.h @@ -46,7 +46,7 @@ namespace airtaudio { void callBackEvent(void* _data, int32_t _frameRate); private: - CoreIosPrivate* m_private; + std::unique_ptr m_private; static OSStatus playbackCallback(void *_userData, AudioUnitRenderActionFlags* _ioActionFlags, const AudioTimeStamp* _inTimeStamp, diff --git a/airtaudio/api/CoreIos.mm b/airtaudio/api/CoreIos.mm index a471991..e63eef1 100644 --- a/airtaudio/api/CoreIos.mm +++ b/airtaudio/api/CoreIos.mm @@ -38,7 +38,7 @@ namespace airtaudio { airtaudio::api::CoreIos::CoreIos(void) : - m_private(new airtaudio::api::CoreIosPrivate) { + m_private(new airtaudio::api::CoreIosPrivate()) { ATA_INFO("new CoreIos"); int32_t deviceCount = 2; ATA_ERROR("Get count devices : " << 2); diff --git a/airtaudio/api/Ds.cpp b/airtaudio/api/Ds.cpp index 281dee3..3abed2c 100644 --- a/airtaudio/api/Ds.cpp +++ b/airtaudio/api/Ds.cpp @@ -56,32 +56,36 @@ static inline DWORD dsPointerBetween(DWORD _pointer, DWORD _laterPointer, DWORD return _pointer >= _earlierPointer && _pointer < _laterPointer; } -// A structure to hold various information related to the DirectSound -// API implementation. -struct DsHandle { - uint32_t drainCounter; // Tracks callback counts when draining - bool internalDrain; // Indicates if stop is initiated from callback or not. - void *id[2]; - void *buffer[2]; - bool xrun[2]; - UINT bufferPointer[2]; - DWORD dsBufferSize[2]; - DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by. - HANDLE condition; - DsHandle() : - drainCounter(0), - internalDrain(false) { - id[0] = 0; - id[1] = 0; - buffer[0] = 0; - buffer[1] = 0; - xrun[0] = false; - xrun[1] = false; - bufferPointer[0] = 0; - bufferPointer[1] = 0; +namespace airtaudio { + namespace api { + class DsPrivate { + public: + uint32_t drainCounter; // Tracks callback counts when draining + bool internalDrain; // Indicates if stop is initiated from callback or not. + void *id[2]; + void *buffer[2]; + bool xrun[2]; + UINT bufferPointer[2]; + DWORD dsBufferSize[2]; + DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by. + HANDLE condition; + + DsPrivate() : + drainCounter(0), + internalDrain(false) { + id[0] = 0; + id[1] = 0; + buffer[0] = 0; + buffer[1] = 0; + xrun[0] = false; + xrun[1] = false; + bufferPointer[0] = 0; + bufferPointer[1] = 0; + } + }; } -}; +} // Declarations for utility functions, callbacks, and structures // specific to the DirectSound implementation. @@ -111,7 +115,8 @@ struct DsProbeData { std::vector* dsDevices; }; -airtaudio::api::Ds::Ds() { +airtaudio::api::Ds::Ds() : + m_private(new airtaudio::api::DsPrivate()) { // Dsound will run both-threaded. If CoInitialize fails, then just // accept whatever the mainline chose for a threading model. m_coInitialized = false; @@ -755,26 +760,15 @@ bool airtaudio::api::Ds::probeDeviceOpen(uint32_t _device, } } } - // Allocate our DsHandle structures for the stream. - if (m_apiHandle == 0) { - handle = new DsHandle; - if (handle == nullptr) { - ATA_ERROR("error allocating AsioHandle memory."); - goto error; - } - // Create a manual-reset event. - handle->condition = CreateEvent(nullptr, // no security - TRUE, // manual-reset - FALSE, // non-signaled initially - nullptr); // unnamed - m_apiHandle = (void *) handle; - } else { - handle = (DsHandle *) m_apiHandle; - } - handle->id[modeToIdTable(_mode)] = ohandle; - handle->buffer[modeToIdTable(_mode)] = bhandle; - handle->dsBufferSize[modeToIdTable(_mode)] = dsBufferSize; - handle->dsPointerLeadTime[modeToIdTable(_mode)] = dsPointerLeadTime; + // Create a manual-reset event. + m_private->condition = CreateEvent(nullptr, // no security + TRUE, // manual-reset + FALSE, // non-signaled initially + nullptr); // unnamed + m_private->id[modeToIdTable(_mode)] = ohandle; + m_private->buffer[modeToIdTable(_mode)] = bhandle; + m_private->dsBufferSize[modeToIdTable(_mode)] = dsBufferSize; + m_private->dsPointerLeadTime[modeToIdTable(_mode)] = dsPointerLeadTime; m_device[modeToIdTable(_mode)] = _device; m_state = airtaudio::state_stopped; if ( m_mode == airtaudio::mode_output @@ -810,27 +804,23 @@ bool airtaudio::api::Ds::probeDeviceOpen(uint32_t _device, } return true; error: - if (handle) { - if (handle->buffer[0]) { - // the object pointer can be nullptr and valid - LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0]; - LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; - if (buffer) { - buffer->Release(); - } - object->Release(); + if (m_private->buffer[0]) { + // the object pointer can be nullptr and valid + LPDIRECTSOUND object = (LPDIRECTSOUND) m_private->id[0]; + LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0]; + if (buffer) { + buffer->Release(); } - if (handle->buffer[1]) { - LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1]; - LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; - if (buffer != nullptr) { - buffer->Release(); - } - } - CloseHandle(handle->condition); - delete handle; - m_apiHandle = 0; + object->Release(); } + if (m_private->buffer[1]) { + LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) m_private->id[1]; + LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) m_private->buffer[1]; + if (buffer != nullptr) { + buffer->Release(); + } + } + CloseHandle(m_private->condition); for (int32_t i=0; i<2; i++) { if (m_userBuffer[i]) { free(m_userBuffer[i]); @@ -854,30 +844,25 @@ enum airtaudio::error airtaudio::api::Ds::closeStream() { m_callbackInfo.isRunning = false; WaitForSingleObject((HANDLE) m_callbackInfo.thread, INFINITE); CloseHandle((HANDLE) m_callbackInfo.thread); - DsHandle *handle = (DsHandle *) m_apiHandle; - if (handle) { - if (handle->buffer[0]) { // the object pointer can be nullptr and valid - LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0]; - LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; - if (buffer) { - buffer->Stop(); - buffer->Release(); - } - object->Release(); + if (m_private->buffer[0]) { // the object pointer can be nullptr and valid + LPDIRECTSOUND object = (LPDIRECTSOUND) m_private->id[0]; + LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0]; + if (buffer) { + buffer->Stop(); + buffer->Release(); } - if (handle->buffer[1]) { - LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1]; - LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; - if (buffer) { - buffer->Stop(); - buffer->Release(); - } - object->Release(); - } - CloseHandle(handle->condition); - delete handle; - m_apiHandle = 0; + object->Release(); } + if (m_private->buffer[1]) { + LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) m_private->id[1]; + LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) m_private->buffer[1]; + if (buffer) { + buffer->Stop(); + buffer->Release(); + } + object->Release(); + } + CloseHandle(m_private->condition); for (int32_t i=0; i<2; i++) { if (m_userBuffer[i]) { free(m_userBuffer[i]); @@ -900,7 +885,6 @@ enum airtaudio::error airtaudio::api::Ds::startStream() { ATA_ERROR("the stream is already running!"); return airtaudio::error_warning; } - DsHandle *handle = (DsHandle *) m_apiHandle; // Increase scheduler frequency on lesser windows (a side-effect of // increasing timer accuracy). On greater windows (Win2K or later), // this is already in effect. @@ -914,7 +898,7 @@ enum airtaudio::error airtaudio::api::Ds::startStream() { HRESULT result = 0; if ( m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { - LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; + LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0]; result = buffer->Play(0, 0, DSBPLAY_LOOPING); if (FAILED(result)) { ATA_ERROR("error (" << getErrorString(result) << ") starting output buffer!"); @@ -923,16 +907,16 @@ enum airtaudio::error airtaudio::api::Ds::startStream() { } if ( m_mode == airtaudio::mode_input || m_mode == airtaudio::mode_duplex) { - LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; + LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) m_private->buffer[1]; result = buffer->Start(DSCBSTART_LOOPING); if (FAILED(result)) { ATA_ERROR("error (" << getErrorString(result) << ") starting input buffer!"); goto unlock; } } - handle->drainCounter = 0; - handle->internalDrain = false; - ResetEvent(handle->condition); + m_private->drainCounter = 0; + m_private->internalDrain = false; + ResetEvent(m_private->condition); m_state = airtaudio::state_running; unlock: if (FAILED(result)) { @@ -952,16 +936,15 @@ enum airtaudio::error airtaudio::api::Ds::stopStream() { HRESULT result = 0; LPVOID audioPtr; DWORD dataLen; - DsHandle *handle = (DsHandle *) m_apiHandle; if ( m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { - if (handle->drainCounter == 0) { - handle->drainCounter = 2; - WaitForSingleObject(handle->condition, INFINITE); // block until signaled + if (m_private->drainCounter == 0) { + m_private->drainCounter = 2; + WaitForSingleObject(m_private->condition, INFINITE); // block until signaled } m_state = airtaudio::state_stopped; // Stop the buffer and clear memory - LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; + LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0]; result = buffer->Stop(); if (FAILED(result)) { ATA_ERROR("error (" << getErrorString(result) << ") stopping output buffer!"); @@ -969,7 +952,7 @@ enum airtaudio::error airtaudio::api::Ds::stopStream() { } // Lock the buffer and clear it so that if we start to play again, // we won't have old data playing. - result = buffer->Lock(0, handle->dsBufferSize[0], &audioPtr, &dataLen, nullptr, nullptr, 0); + result = buffer->Lock(0, m_private->dsBufferSize[0], &audioPtr, &dataLen, nullptr, nullptr, 0); if (FAILED(result)) { ATA_ERROR("error (" << getErrorString(result) << ") locking output buffer!"); goto unlock; @@ -983,11 +966,11 @@ enum airtaudio::error airtaudio::api::Ds::stopStream() { goto unlock; } // If we start playing again, we must begin at beginning of buffer. - handle->bufferPointer[0] = 0; + m_private->bufferPointer[0] = 0; } if ( m_mode == airtaudio::mode_input || m_mode == airtaudio::mode_duplex) { - LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; + LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) m_private->buffer[1]; audioPtr = nullptr; dataLen = 0; m_state = airtaudio::state_stopped; @@ -998,7 +981,7 @@ enum airtaudio::error airtaudio::api::Ds::stopStream() { } // Lock the buffer and clear it so that if we start to play again, // we won't have old data playing. - result = buffer->Lock(0, handle->dsBufferSize[1], &audioPtr, &dataLen, nullptr, nullptr, 0); + result = buffer->Lock(0, m_private->dsBufferSize[1], &audioPtr, &dataLen, nullptr, nullptr, 0); if (FAILED(result)) { ATA_ERROR("error (" << getErrorString(result) << ") locking input buffer!"); goto unlock; @@ -1012,7 +995,7 @@ enum airtaudio::error airtaudio::api::Ds::stopStream() { goto unlock; } // If we start recording again, we must begin at beginning of buffer. - handle->bufferPointer[1] = 0; + m_private->bufferPointer[1] = 0; } unlock: timeEndPeriod(1); // revert to normal scheduler frequency on lesser windows. @@ -1030,8 +1013,7 @@ enum airtaudio::error airtaudio::api::Ds::abortStream() { ATA_ERROR("the stream is already stopped!"); return airtaudio::error_warning; } - DsHandle *handle = (DsHandle *) m_apiHandle; - handle->drainCounter = 2; + m_private->drainCounter = 2; return stopStream(); } @@ -1045,12 +1027,11 @@ void airtaudio::api::Ds::callbackEvent() { return; } CallbackInfo *info = (CallbackInfo *) &m_callbackInfo; - DsHandle *handle = (DsHandle *) m_apiHandle; // Check if we were draining the stream and signal is finished. - if (handle->drainCounter > m_nBuffers + 2) { + if (m_private->drainCounter > m_nBuffers + 2) { m_state = airtaudio::state_stopping; - if (handle->internalDrain == false) { - SetEvent(handle->condition); + if (m_private->internalDrain == false) { + SetEvent(m_private->condition); } else { stopStream(); } @@ -1058,18 +1039,18 @@ void airtaudio::api::Ds::callbackEvent() { } // Invoke user callback to get fresh output data UNLESS we are // draining stream. - if (handle->drainCounter == 0) { + if (m_private->drainCounter == 0) { double streamTime = getStreamTime(); rtaudio::streamStatus status = 0; if ( m_mode != airtaudio::mode_input - && handle->xrun[0] == true) { + && m_private->xrun[0] == true) { status |= RTAUDIO_airtaudio::status_underflow; - handle->xrun[0] = false; + m_private->xrun[0] = false; } if ( m_mode != airtaudio::mode_output - && handle->xrun[1] == true) { + && m_private->xrun[1] == true) { status |= RTAUDIO_airtaudio::mode_input_OVERFLOW; - handle->xrun[1] = false; + m_private->xrun[1] = false; } int32_t cbReturnValue = info->callback(m_userBuffer[0], m_userBuffer[1], @@ -1078,12 +1059,12 @@ void airtaudio::api::Ds::callbackEvent() { status); if (cbReturnValue == 2) { m_state = airtaudio::state_stopping; - handle->drainCounter = 2; + m_private->drainCounter = 2; abortStream(); return; } else if (cbReturnValue == 1) { - handle->drainCounter = 1; - handle->internalDrain = true; + m_private->drainCounter = 1; + m_private->internalDrain = true; } } HRESULT result; @@ -1098,7 +1079,7 @@ void airtaudio::api::Ds::callbackEvent() { long bufferBytes; if (m_buffersRolling == false) { if (m_mode == airtaudio::mode_duplex) { - //assert(handle->dsBufferSize[0] == handle->dsBufferSize[1]); + //assert(m_private->dsBufferSize[0] == m_private->dsBufferSize[1]); // It takes a while for the devices to get rolling. As a result, // there's no guarantee that the capture and write device pointers // will move in lockstep. Wait here for both devices to start @@ -1111,8 +1092,8 @@ void airtaudio::api::Ds::callbackEvent() { // Realtime priority, maybe; but I'm not sure what priority the // DirectSound service threads run at. We *should* be roughly // within a ms or so of correct. - LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; - LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; + LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0]; + LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) m_private->buffer[1]; DWORD startSafeWritePointer, startSafeReadPointer; result = dsWriteBuffer->GetCurrentPosition(nullptr, &startSafeWritePointer); if (FAILED(result)) { @@ -1141,31 +1122,31 @@ void airtaudio::api::Ds::callbackEvent() { } Sleep(1); } - //assert(handle->dsBufferSize[0] == handle->dsBufferSize[1]); - handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0]; - if (handle->bufferPointer[0] >= handle->dsBufferSize[0]) { - handle->bufferPointer[0] -= handle->dsBufferSize[0]; + //assert(m_private->dsBufferSize[0] == m_private->dsBufferSize[1]); + m_private->bufferPointer[0] = safeWritePointer + m_private->dsPointerLeadTime[0]; + if (m_private->bufferPointer[0] >= m_private->dsBufferSize[0]) { + m_private->bufferPointer[0] -= m_private->dsBufferSize[0]; } - handle->bufferPointer[1] = safeReadPointer; + m_private->bufferPointer[1] = safeReadPointer; } else if (m_mode == airtaudio::mode_output) { // Set the proper nextWritePosition after initial startup. - LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; + LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0]; result = dsWriteBuffer->GetCurrentPosition(¤tWritePointer, &safeWritePointer); if (FAILED(result)) { ATA_ERROR("error (" << getErrorString(result) << ") getting current write position!"); return; } - handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0]; - if (handle->bufferPointer[0] >= handle->dsBufferSize[0]) { - handle->bufferPointer[0] -= handle->dsBufferSize[0]; + m_private->bufferPointer[0] = safeWritePointer + m_private->dsPointerLeadTime[0]; + if (m_private->bufferPointer[0] >= m_private->dsBufferSize[0]) { + m_private->bufferPointer[0] -= m_private->dsBufferSize[0]; } } m_buffersRolling = true; } if ( m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { - LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; - if (handle->drainCounter > 1) { // write zeros to the output stream + LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0]; + if (m_private->drainCounter > 1) { // write zeros to the output stream bufferBytes = m_bufferSize * m_nUserChannels[0]; bufferBytes *= audio::getFormatBytes(m_userFormat); memset(m_userBuffer[0], 0, bufferBytes); @@ -1190,8 +1171,8 @@ void airtaudio::api::Ds::callbackEvent() { buffer[i] = (unsigned char) (buffer[i] + 128); } } - DWORD dsBufferSize = handle->dsBufferSize[0]; - nextWritePointer = handle->bufferPointer[0]; + DWORD dsBufferSize = m_private->dsBufferSize[0]; + nextWritePointer = m_private->bufferPointer[0]; DWORD endWrite, leadPointer; while (true) { // Find out where the read and "safe write" pointers are. @@ -1203,7 +1184,7 @@ void airtaudio::api::Ds::callbackEvent() { // We will copy our output buffer into the region between // safeWritePointer and leadPointer. If leadPointer is not // beyond the next endWrite position, wait until it is. - leadPointer = safeWritePointer + handle->dsPointerLeadTime[0]; + leadPointer = safeWritePointer + m_private->dsPointerLeadTime[0]; //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl; if (leadPointer > dsBufferSize) { leadPointer -= dsBufferSize; @@ -1229,12 +1210,12 @@ void airtaudio::api::Ds::callbackEvent() { if ( dsPointerBetween(nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize) || dsPointerBetween(endWrite, safeWritePointer, currentWritePointer, dsBufferSize)) { // We've strayed into the forbidden zone ... resync the read pointer. - handle->xrun[0] = true; - nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes; + m_private->xrun[0] = true; + nextWritePointer = safeWritePointer + m_private->dsPointerLeadTime[0] - bufferBytes; if (nextWritePointer >= dsBufferSize) { nextWritePointer -= dsBufferSize; } - handle->bufferPointer[0] = nextWritePointer; + m_private->bufferPointer[0] = nextWritePointer; endWrite = nextWritePointer + bufferBytes; } // Lock free space in the buffer @@ -1261,9 +1242,9 @@ void airtaudio::api::Ds::callbackEvent() { return; } nextWritePointer = (nextWritePointer + bufferSize1 + bufferSize2) % dsBufferSize; - handle->bufferPointer[0] = nextWritePointer; - if (handle->drainCounter) { - handle->drainCounter++; + m_private->bufferPointer[0] = nextWritePointer; + if (m_private->drainCounter) { + m_private->drainCounter++; goto unlock; } } @@ -1279,9 +1260,9 @@ void airtaudio::api::Ds::callbackEvent() { bufferBytes = m_bufferSize * m_nUserChannels[1]; bufferBytes *= audio::getFormatBytes(m_userFormat); } - LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; - long nextReadPointer = handle->bufferPointer[1]; - DWORD dsBufferSize = handle->dsBufferSize[1]; + LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) m_private->buffer[1]; + long nextReadPointer = m_private->bufferPointer[1]; + DWORD dsBufferSize = m_private->dsBufferSize[1]; // Find out where the write and "safe read" pointers are. result = dsBuffer->GetCurrentPosition(¤tReadPointer, &safeReadPointer); if (FAILED(result)) { @@ -1310,7 +1291,7 @@ void airtaudio::api::Ds::callbackEvent() { if (m_duplexPrerollBytes <= 0) { // Pre-roll time over. Be more agressive. int32_t adjustment = endRead-safeReadPointer; - handle->xrun[1] = true; + m_private->xrun[1] = true; // Two cases: // - large adjustments: we've probably run out of CPU cycles, so just resync exactly, // and perform fine adjustments later. @@ -1386,7 +1367,7 @@ void airtaudio::api::Ds::callbackEvent() { ATA_ERROR("error (" << getErrorString(result) << ") unlocking capture buffer!"); return; } - handle->bufferPointer[1] = nextReadPointer; + m_private->bufferPointer[1] = nextReadPointer; // No byte swapping necessary in DirectSound implementation. // If necessary, convert 8-bit data from unsigned to signed. if (m_deviceFormat[1] == RTAUDIO_SINT8) { diff --git a/airtaudio/api/Ds.h b/airtaudio/api/Ds.h index f839702..690daf2 100644 --- a/airtaudio/api/Ds.h +++ b/airtaudio/api/Ds.h @@ -10,6 +10,7 @@ namespace airtaudio { namespace api { + class DsPrivate; class Ds: public airtaudio::Api { public: static airtaudio::Api* Create(); @@ -34,6 +35,7 @@ namespace airtaudio { // will most likely produce highly undesireable results! void callbackEvent(); private: + std::unique_ptr m_private; bool m_coInitialized; bool m_buffersRolling; long m_duplexPrerollBytes; diff --git a/airtaudio/api/Jack.cpp b/airtaudio/api/Jack.cpp index 4e16f32..682aa34 100644 --- a/airtaudio/api/Jack.cpp +++ b/airtaudio/api/Jack.cpp @@ -56,29 +56,34 @@ airtaudio::Api* airtaudio::api::Jack::Create() { #include #include -// A structure to hold various information related to the Jack API -// implementation. -struct JackHandle { - jack_client_t *client; - jack_port_t **ports[2]; - std::string deviceName[2]; - bool xrun[2]; - std::condition_variable condition; - int32_t drainCounter; // Tracks callback counts when draining - bool internalDrain; // Indicates if stop is initiated from callback or not. - JackHandle() : - client(0), - drainCounter(0), - internalDrain(false) { - ports[0] = 0; - ports[1] = 0; - xrun[0] = false; - xrun[1] = false; +namespace airtaudio { + namespace api { + class JackPrivate { + public: + jack_client_t *client; + jack_port_t **ports[2]; + std::string deviceName[2]; + bool xrun[2]; + std::condition_variable condition; + int32_t drainCounter; // Tracks callback counts when draining + bool internalDrain; // Indicates if stop is initiated from callback or not. + + JackPrivate() : + client(0), + drainCounter(0), + internalDrain(false) { + ports[0] = 0; + ports[1] = 0; + xrun[0] = false; + xrun[1] = false; + } + }; } -}; +} -airtaudio::api::Jack::Jack() { +airtaudio::api::Jack::Jack() : + m_private(new airtaudio::api::JackPrivate()) { // Nothing to do here. } @@ -246,12 +251,11 @@ void airtaudio::api::Jack::jackShutdown(void* _userData) { int32_t airtaudio::api::Jack::jackXrun(void* _userData) { airtaudio::api::Jack* myClass = reinterpret_cast(_userData); - JackHandle* handle = (JackHandle*)myClass->m_apiHandle; - if (handle->ports[0]) { - handle->xrun[0] = true; + if (myClass->m_private->ports[0]) { + myClass->m_private->xrun[0] = true; } - if (handle->ports[1]) { - handle->xrun[1] = true; + if (myClass->m_private->ports[1]) { + myClass->m_private->xrun[1] = true; } return 0; } @@ -264,7 +268,6 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device, audio::format _format, uint32_t* _bufferSize, airtaudio::StreamOptions* _options) { - JackHandle *handle = (JackHandle *) m_apiHandle; // Look for jack server and try to become a client (only do once per stream). jack_client_t *client = 0; if ( _mode == airtaudio::mode_output @@ -283,7 +286,7 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device, } } else { // The handle must have been created on an earlier pass. - client = handle->client; + client = m_private->client; } const char **ports; std::string port, previousPort, deviceName; @@ -376,16 +379,8 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device, m_doConvertBuffer[modeToIdTable(_mode)] = true; } // Allocate our JackHandle structure for the stream. - if (handle == 0) { - handle = new JackHandle; - if (handle == nullptr) { - ATA_ERROR("error allocating JackHandle memory."); - goto error; - } - m_apiHandle = (void *) handle; - handle->client = client; - } - handle->deviceName[modeToIdTable(_mode)] = deviceName; + m_private->client = client; + m_private->deviceName[modeToIdTable(_mode)] = deviceName; // Allocate necessary internal buffers. uint64_t bufferBytes; bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]); @@ -419,8 +414,8 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device, } } // Allocate memory for the Jack ports (channels) identifiers. - handle->ports[modeToIdTable(_mode)] = (jack_port_t **) malloc (sizeof (jack_port_t *) * _channels); - if (handle->ports[modeToIdTable(_mode)] == nullptr) { + m_private->ports[modeToIdTable(_mode)] = (jack_port_t **) malloc (sizeof (jack_port_t *) * _channels); + if (m_private->ports[modeToIdTable(_mode)] == nullptr) { ATA_ERROR("error allocating port memory."); goto error; } @@ -433,29 +428,29 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device, m_mode = airtaudio::mode_duplex; } else { m_mode = _mode; - jack_set_process_callback(handle->client, &airtaudio::api::Jack::jackCallbackHandler, this); - jack_set_xrun_callback(handle->client, &airtaudio::api::Jack::jackXrun, this); - jack_on_shutdown(handle->client, &airtaudio::api::Jack::jackShutdown, this); + jack_set_process_callback(m_private->client, &airtaudio::api::Jack::jackCallbackHandler, this); + jack_set_xrun_callback(m_private->client, &airtaudio::api::Jack::jackXrun, this); + jack_on_shutdown(m_private->client, &airtaudio::api::Jack::jackShutdown, this); } // Register our ports. char label[64]; if (_mode == airtaudio::mode_output) { for (uint32_t i=0; iports[0][i] = jack_port_register(handle->client, - (const char *)label, - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsOutput, - 0); + m_private->ports[0][i] = jack_port_register(m_private->client, + (const char *)label, + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, + 0); } } else { for (uint32_t i=0; iports[1][i] = jack_port_register(handle->client, - (const char *)label, - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsInput, - 0); + m_private->ports[1][i] = jack_port_register(m_private->client, + (const char *)label, + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, + 0); } } // Setup the buffer conversion information structure. We don't use @@ -466,16 +461,14 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device, } return true; error: - if (handle) { - jack_client_close(handle->client); - if (handle->ports[0]) { - free(handle->ports[0]); - } - if (handle->ports[1]) { - free(handle->ports[1]); - } - delete handle; - m_apiHandle = nullptr; + jack_client_close(m_private->client); + if (m_private->ports[0] != nullptr) { + free(m_private->ports[0]); + m_private->ports[0] = nullptr; + } + if (m_private->ports[1] != nullptr) { + free(m_private->ports[1]); + m_private->ports[1] = nullptr; } for (int32_t iii=0; iii<2; ++iii) { m_userBuffer[iii].clear(); @@ -492,22 +485,19 @@ enum airtaudio::error airtaudio::api::Jack::closeStream() { ATA_ERROR("no open stream to close!"); return airtaudio::error_warning; } - JackHandle *handle = (JackHandle *) m_apiHandle; - if (handle != nullptr) { + if (m_private != nullptr) { if (m_state == airtaudio::state_running) { - jack_deactivate(handle->client); + jack_deactivate(m_private->client); } - jack_client_close(handle->client); + jack_client_close(m_private->client); } - if (handle != nullptr) { - if (handle->ports[0]) { - free(handle->ports[0]); - } - if (handle->ports[1]) { - free(handle->ports[1]); - } - delete handle; - m_apiHandle = nullptr; + if (m_private->ports[0] != nullptr) { + free(m_private->ports[0]); + m_private->ports[0] = nullptr; + } + if (m_private->ports[1] != nullptr) { + free(m_private->ports[1]); + m_private->ports[1] = nullptr; } for (int32_t i=0; i<2; i++) { m_userBuffer[i].clear(); @@ -529,8 +519,7 @@ enum airtaudio::error airtaudio::api::Jack::startStream() { ATA_ERROR("the stream is already running!"); return airtaudio::error_warning; } - JackHandle *handle = (JackHandle *) m_apiHandle; - int32_t result = jack_activate(handle->client); + int32_t result = jack_activate(m_private->client); if (result) { ATA_ERROR("unable to activate JACK client!"); goto unlock; @@ -540,7 +529,7 @@ enum airtaudio::error airtaudio::api::Jack::startStream() { if ( m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { result = 1; - ports = jack_get_ports(handle->client, handle->deviceName[0].c_str(), nullptr, JackPortIsInput); + ports = jack_get_ports(m_private->client, m_private->deviceName[0].c_str(), nullptr, JackPortIsInput); if (ports == nullptr) { ATA_ERROR("error determining available JACK input ports!"); goto unlock; @@ -551,7 +540,7 @@ enum airtaudio::error airtaudio::api::Jack::startStream() { for (uint32_t i=0; iclient, jack_port_name(handle->ports[0][i]), ports[ m_channelOffset[0] + i ]); + result = jack_connect(m_private->client, jack_port_name(m_private->ports[0][i]), ports[ m_channelOffset[0] + i ]); if (result) { free(ports); ATA_ERROR("error connecting output ports!"); @@ -563,7 +552,7 @@ enum airtaudio::error airtaudio::api::Jack::startStream() { if ( m_mode == airtaudio::mode_input || m_mode == airtaudio::mode_duplex) { result = 1; - ports = jack_get_ports(handle->client, handle->deviceName[1].c_str(), nullptr, JackPortIsOutput); + ports = jack_get_ports(m_private->client, m_private->deviceName[1].c_str(), nullptr, JackPortIsOutput); if (ports == nullptr) { ATA_ERROR("error determining available JACK output ports!"); goto unlock; @@ -572,7 +561,7 @@ enum airtaudio::error airtaudio::api::Jack::startStream() { for (uint32_t i=0; iclient, ports[ m_channelOffset[1] + i ], jack_port_name(handle->ports[1][i])); + result = jack_connect(m_private->client, ports[ m_channelOffset[1] + i ], jack_port_name(m_private->ports[1][i])); } if (result) { free(ports); @@ -582,8 +571,8 @@ enum airtaudio::error airtaudio::api::Jack::startStream() { } free(ports); } - handle->drainCounter = 0; - handle->internalDrain = false; + m_private->drainCounter = 0; + m_private->internalDrain = false; m_state = airtaudio::state_running; unlock: if (result == 0) { @@ -600,16 +589,15 @@ enum airtaudio::error airtaudio::api::Jack::stopStream() { ATA_ERROR("the stream is already stopped!"); return airtaudio::error_warning; } - JackHandle *handle = (JackHandle *) m_apiHandle; if ( m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { - if (handle->drainCounter == 0) { - handle->drainCounter = 2; + if (m_private->drainCounter == 0) { + m_private->drainCounter = 2; std::unique_lock lck(m_mutex); - handle->condition.wait(lck); + m_private->condition.wait(lck); } } - jack_deactivate(handle->client); + jack_deactivate(m_private->client); m_state = airtaudio::state_stopped; return airtaudio::error_none; } @@ -622,8 +610,7 @@ enum airtaudio::error airtaudio::api::Jack::abortStream() { ATA_ERROR("the stream is already stopped!"); return airtaudio::error_warning; } - JackHandle *handle = (JackHandle *) m_apiHandle; - handle->drainCounter = 2; + m_private->drainCounter = 2; return stopStream(); } @@ -650,28 +637,27 @@ bool airtaudio::api::Jack::callbackEvent(uint64_t _nframes) { ATA_ERROR("the JACK buffer size has changed ... cannot process!"); return false; } - JackHandle *handle = (JackHandle *) m_apiHandle; // Check if we were draining the stream and signal is finished. - if (handle->drainCounter > 3) { + if (m_private->drainCounter > 3) { m_state = airtaudio::state_stopping; - if (handle->internalDrain == true) { + if (m_private->internalDrain == true) { new std::thread(jackStopStream, this); } else { - handle->condition.notify_one(); + m_private->condition.notify_one(); } return true; } // Invoke user callback first, to get fresh output data. - if (handle->drainCounter == 0) { + if (m_private->drainCounter == 0) { double streamTime = getStreamTime(); enum airtaudio::status status = airtaudio::status_ok; - if (m_mode != airtaudio::mode_input && handle->xrun[0] == true) { + if (m_mode != airtaudio::mode_input && m_private->xrun[0] == true) { status = airtaudio::status_underflow; - handle->xrun[0] = false; + m_private->xrun[0] = false; } - if (m_mode != airtaudio::mode_output && handle->xrun[1] == true) { + if (m_mode != airtaudio::mode_output && m_private->xrun[1] == true) { status = airtaudio::status_overflow; - handle->xrun[1] = false; + m_private->xrun[1] = false; } int32_t cbReturnValue = m_callbackInfo.callback(&m_userBuffer[0][0], &m_userBuffer[1][0], @@ -680,38 +666,38 @@ bool airtaudio::api::Jack::callbackEvent(uint64_t _nframes) { status); if (cbReturnValue == 2) { m_state = airtaudio::state_stopping; - handle->drainCounter = 2; + m_private->drainCounter = 2; new std::thread(jackStopStream, this); return true; } else if (cbReturnValue == 1) { - handle->drainCounter = 1; - handle->internalDrain = true; + m_private->drainCounter = 1; + m_private->internalDrain = true; } } jack_default_audio_sample_t *jackbuffer; uint64_t bufferBytes = _nframes * sizeof(jack_default_audio_sample_t); if ( m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { - if (handle->drainCounter > 1) { // write zeros to the output stream + if (m_private->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(m_private->ports[0][i], (jack_nframes_t) _nframes); memset(jackbuffer, 0, bufferBytes); } } else if (m_doConvertBuffer[0]) { convertBuffer(m_deviceBuffer, &m_userBuffer[0][0], m_convertInfo[0]); for (uint32_t i=0; iports[0][i], (jack_nframes_t) _nframes); + jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(m_private->ports[0][i], (jack_nframes_t) _nframes); memcpy(jackbuffer, &m_deviceBuffer[i*bufferBytes], bufferBytes); } } 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(m_private->ports[0][i], (jack_nframes_t) _nframes); memcpy(jackbuffer, &m_userBuffer[0][i*bufferBytes], bufferBytes); } } - if (handle->drainCounter) { - handle->drainCounter++; + if (m_private->drainCounter) { + m_private->drainCounter++; goto unlock; } } @@ -719,14 +705,14 @@ bool airtaudio::api::Jack::callbackEvent(uint64_t _nframes) { || m_mode == airtaudio::mode_duplex) { if (m_doConvertBuffer[1]) { for (uint32_t i=0; iports[1][i], (jack_nframes_t) _nframes); + jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(m_private->ports[1][i], (jack_nframes_t) _nframes); memcpy(&m_deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes); } convertBuffer(&m_userBuffer[1][0], m_deviceBuffer, m_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(m_private->ports[1][i], (jack_nframes_t) _nframes); memcpy(&m_userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes); } } diff --git a/airtaudio/api/Jack.h b/airtaudio/api/Jack.h index ab93a3c..ead1a85 100644 --- a/airtaudio/api/Jack.h +++ b/airtaudio/api/Jack.h @@ -11,6 +11,7 @@ #include namespace airtaudio { namespace api { + class JackPrivate; class Jack: public airtaudio::Api { public: static airtaudio::Api* Create(); @@ -38,6 +39,7 @@ namespace airtaudio { static void jackShutdown(void* _userData); static int32_t jackCallbackHandler(jack_nframes_t _nframes, void* _userData); private: + std::unique_ptr m_private; bool probeDeviceOpen(uint32_t _device, airtaudio::mode _mode, uint32_t _channels, diff --git a/airtaudio/api/Oss.cpp b/airtaudio/api/Oss.cpp index 71e0157..7031ab4 100644 --- a/airtaudio/api/Oss.cpp +++ b/airtaudio/api/Oss.cpp @@ -25,25 +25,30 @@ airtaudio::Api* airtaudio::api::Oss::Create() { return new airtaudio::api::Oss(); } -static void *ossCallbackHandler(void* _ptr); +static void *ossCallbackHandler(void* _userData); -// A structure to hold various information related to the OSS API -// implementation. -struct OssHandle { - int32_t id[2]; // device ids - bool xrun[2]; - bool triggered; - std::condition_variable runnable; - OssHandle(): - triggered(false) { - id[0] = 0; - id[1] = 0; - xrun[0] = false; - xrun[1] = false; + +namespace airtaudio { + namespace api { + class OssPrivate { + public: + int32_t id[2]; // device ids + bool xrun[2]; + bool triggered; + std::condition_variable runnable; + OssPrivate(): + triggered(false) { + id[0] = 0; + id[1] = 0; + xrun[0] = false; + xrun[1] = false; + } + }; } -}; +} -airtaudio::api::Oss::Oss() { +airtaudio::api::Oss::Oss() : + m_private(new airtaudio::api::OssPrivate()) { // Nothing to do here. } @@ -226,15 +231,14 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device, return false; } int32_t flags = 0; - OssHandle *handle = (OssHandle *) m_apiHandle; if (_mode == airtaudio::mode_output) { flags |= O_WRONLY; } else { // _mode == airtaudio::mode_input if ( m_mode == airtaudio::mode_output && m_device[0] == _device) { // We just set the same device for playback ... close and reopen for duplex (OSS only). - close(handle->id[0]); - handle->id[0] = 0; + close(m_private->id[0]); + m_private->id[0] = 0; if (!(ainfo.caps & PCM_CAP_airtaudio::mode_duplex)) { ATA_ERROR("device (" << ainfo.name << ") does not support duplex mode."); return false; @@ -450,18 +454,7 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device, && m_nUserChannels[modeToIdTable(_mode)] > 1) { m_doConvertBuffer[modeToIdTable(_mode)] = true; } - // Allocate the stream handles if necessary and then save. - if (m_apiHandle == 0) { - handle = new OssHandle; - if handle == nullptr) { - ATA_ERROR("error allocating OssHandle memory."); - goto error; - } - m_apiHandle = (void *) handle; - } else { - handle = (OssHandle *) m_apiHandle; - } - handle->id[modeToIdTable(_mode)] = fd; + m_private->id[modeToIdTable(_mode)] = fd; // Allocate necessary internal buffers. uint64_t bufferBytes; bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_userFormat); @@ -505,14 +498,13 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device, // We had already set up an output stream. m_mode = airtaudio::mode_duplex; if (m_device[0] == _device) { - handle->id[0] = fd; + m_private->id[0] = fd; } } else { m_mode = _mode; // Setup callback thread. - m_callbackInfo.object = (void *) this; m_callbackInfo.isRunning = true; - m_callbackInfo.thread = new std::thread(ossCallbackHandler, &m_callbackInfo); + m_callbackInfo.thread = new std::thread(ossCallbackHandler, this); if (m_callbackInfo.thread == nullptr) { m_callbackInfo.isRunning = false; ATA_ERROR("creating callback thread!"); @@ -521,15 +513,13 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device, } return true; error: - if (handle) { - if (handle->id[0]) { - close(handle->id[0]); - } - if (handle->id[1]) { - close(handle->id[1]); - } - delete handle; - m_apiHandle = 0; + if (m_private->id[0] != nullptr) { + close(m_private->id[0]); + m_private->id[0] = nullptr; + } + if (m_private->id[1] != nullptr) { + close(m_private->id[1]); + m_private->id[1] = nullptr; } for (int32_t i=0; i<2; i++) { if (m_userBuffer[i]) { @@ -549,31 +539,28 @@ enum airtaudio::error airtaudio::api::Oss::closeStream() { ATA_ERROR("no open stream to close!"); return airtaudio::error_warning; } - OssHandle *handle = (OssHandle *) m_apiHandle; m_callbackInfo.isRunning = false; m_mutex.lock(); if (m_state == airtaudio::state_stopped) { - handle->runnable.notify_one(); + m_private->runnable.notify_one(); } m_mutex.unlock(); m_callbackInfo.thread->join(); if (m_state == airtaudio::state_running) { if (m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { - ioctl(handle->id[0], SNDCTL_DSP_HALT, 0); + ioctl(m_private->id[0], SNDCTL_DSP_HALT, 0); } else { - ioctl(handle->id[1], SNDCTL_DSP_HALT, 0); + ioctl(m_private->id[1], SNDCTL_DSP_HALT, 0); } m_state = airtaudio::state_stopped; } - if (handle) { - if (handle->id[0]) { - close(handle->id[0]); - } - if (handle->id[1]) { - close(handle->id[1]); - } - delete handle; - m_apiHandle = 0; + if (m_private->id[0] != nullptr) { + close(m_private->id[0]); + m_private->id[0] = nullptr; + } + if (m_private->id[1] != nullptr) { + close(m_private->id[1]); + m_private->id[1] = nullptr; } for (int32_t i=0; i<2; i++) { if (m_userBuffer[i]) { @@ -603,8 +590,7 @@ enum airtaudio::error airtaudio::api::Oss::startStream() { // No need to do anything else here ... OSS automatically starts // when fed samples. m_mutex.unlock(); - OssHandle *handle = (OssHandle *) m_apiHandle; - handle->runnable.notify_one(); + m_private->runnable.notify_one(); } enum airtaudio::error airtaudio::api::Oss::stopStream() { @@ -622,7 +608,6 @@ enum airtaudio::error airtaudio::api::Oss::stopStream() { return; } int32_t result = 0; - OssHandle *handle = (OssHandle *) m_apiHandle; if ( m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { // Flush the output with zeros a few times. @@ -640,23 +625,23 @@ enum airtaudio::error airtaudio::api::Oss::stopStream() { } memset(buffer, 0, samples * audio::getFormatBytes(format)); for (uint32_t i=0; iid[0], buffer, samples * audio::getFormatBytes(format)); + result = write(m_private->id[0], buffer, samples * audio::getFormatBytes(format)); if (result == -1) { ATA_ERROR("audio write error."); return airtaudio::error_warning; } } - result = ioctl(handle->id[0], SNDCTL_DSP_HALT, 0); + result = ioctl(m_private->id[0], SNDCTL_DSP_HALT, 0); if (result == -1) { ATA_ERROR("system error stopping callback procedure on device (" << m_device[0] << ")."); goto unlock; } - handle->triggered = false; + m_private->triggered = false; } if ( m_mode == airtaudio::mode_input || ( m_mode == airtaudio::mode_duplex - && handle->id[0] != handle->id[1])) { - result = ioctl(handle->id[1], SNDCTL_DSP_HALT, 0); + && m_private->id[0] != m_private->id[1])) { + result = ioctl(m_private->id[1], SNDCTL_DSP_HALT, 0); if (result == -1) { ATA_ERROR("system error stopping input callback procedure on device (" << m_device[0] << ")."); goto unlock; @@ -686,17 +671,16 @@ enum airtaudio::error airtaudio::api::Oss::abortStream() { return; } int32_t result = 0; - OssHandle *handle = (OssHandle *) m_apiHandle; if (m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { - result = ioctl(handle->id[0], SNDCTL_DSP_HALT, 0); + result = ioctl(m_private->id[0], SNDCTL_DSP_HALT, 0); if (result == -1) { ATA_ERROR("system error stopping callback procedure on device (" << m_device[0] << ")."); goto unlock; } - handle->triggered = false; + m_private->triggered = false; } - if (m_mode == airtaudio::mode_input || (m_mode == airtaudio::mode_duplex && handle->id[0] != handle->id[1])) { - result = ioctl(handle->id[1], SNDCTL_DSP_HALT, 0); + if (m_mode == airtaudio::mode_input || (m_mode == airtaudio::mode_duplex && m_private->id[0] != m_private->id[1])) { + result = ioctl(m_private->id[1], SNDCTL_DSP_HALT, 0); if (result == -1) { ATA_ERROR("system error stopping input callback procedure on device (" << m_device[0] << ")."); goto unlock; @@ -712,10 +696,9 @@ unlock: } void airtaudio::api::Oss::callbackEvent() { - OssHandle *handle = (OssHandle *) m_apiHandle; if (m_state == airtaudio::state_stopped) { std::unique_lock lck(m_mutex); - handle->runnable.wait(lck); + m_private->runnable.wait(lck); if (m_state != airtaudio::state_running) { return; } @@ -729,14 +712,14 @@ void airtaudio::api::Oss::callbackEvent() { double streamTime = getStreamTime(); rtaudio::streamStatus status = 0; if ( m_mode != airtaudio::mode_input - && handle->xrun[0] == true) { + && m_private->xrun[0] == true) { status |= RTAUDIO_airtaudio::status_underflow; - handle->xrun[0] = false; + m_private->xrun[0] = false; } if ( m_mode != airtaudio::mode_output - && handle->xrun[1] == true) { + && m_private->xrun[1] == true) { status |= RTAUDIO_airtaudio::mode_input_OVERFLOW; - handle->xrun[1] = false; + m_private->xrun[1] = false; } doStopStream = m_callbackInfo.callback(m_userBuffer[0], m_userBuffer[1], @@ -774,21 +757,21 @@ void airtaudio::api::Oss::callbackEvent() { byteSwapBuffer(buffer, samples, format); } if ( m_mode == airtaudio::mode_duplex - && handle->triggered == false) { + && m_private->triggered == false) { int32_t trig = 0; - ioctl(handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig); - result = write(handle->id[0], buffer, samples * audio::getFormatBytes(format)); + ioctl(m_private->id[0], SNDCTL_DSP_SETTRIGGER, &trig); + result = write(m_private->id[0], buffer, samples * audio::getFormatBytes(format)); trig = PCM_ENABLE_airtaudio::mode_input|PCM_ENABLE_airtaudio::mode_output; - ioctl(handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig); - handle->triggered = true; + ioctl(m_private->id[0], SNDCTL_DSP_SETTRIGGER, &trig); + m_private->triggered = true; } else { // Write samples to device. - result = write(handle->id[0], buffer, samples * audio::getFormatBytes(format)); + result = write(m_private->id[0], buffer, samples * audio::getFormatBytes(format)); } if (result == -1) { // We'll assume this is an underrun, though there isn't a // specific means for determining that. - handle->xrun[0] = true; + m_private->xrun[0] = true; ATA_ERROR("audio write error."); //error(airtaudio::error_warning); // Continue on to input section. @@ -807,11 +790,11 @@ void airtaudio::api::Oss::callbackEvent() { format = m_userFormat; } // Read samples from device. - result = read(handle->id[1], buffer, samples * audio::getFormatBytes(format)); + result = read(m_private->id[1], buffer, samples * audio::getFormatBytes(format)); if (result == -1) { // We'll assume this is an overrun, though there isn't a // specific means for determining that. - handle->xrun[1] = true; + m_private->xrun[1] = true; ATA_ERROR("audio read error."); goto unlock; } @@ -832,12 +815,10 @@ unlock: } } -static void ossCallbackHandler(void* _ptr) { - CallbackInfo* info = (CallbackInfo*)_ptr; - RtApiOss* object = (RtApiOss*)info->object; - bool *isRunning = &info->isRunning; - while (*isRunning == true) { - object->callbackEvent(); +static void ossCallbackHandler(void* _userData) { + airtaudio::api::Alsa* myClass = reinterpret_cast(_userData); + while (myClass->m_callbackInfo->isRunning == true) { + myClass->callbackEvent(); } } diff --git a/airtaudio/api/Oss.h b/airtaudio/api/Oss.h index 95f7ec1..979e822 100644 --- a/airtaudio/api/Oss.h +++ b/airtaudio/api/Oss.h @@ -10,6 +10,7 @@ namespace airtaudio { namespace api { + class OssPrivate; class Oss: public airtaudio::Api { public: static airtaudio::Api* Create(); @@ -31,6 +32,7 @@ namespace airtaudio { // will most likely produce highly undesireable results! void callbackEvent(); private: + std::unique_ptr m_private; bool probeDeviceOpen(uint32_t _device, airtaudio::mode _mode, uint32_t _channels, diff --git a/airtaudio/api/Pulse.cpp b/airtaudio/api/Pulse.cpp index ad0a2d5..155fc71 100644 --- a/airtaudio/api/Pulse.cpp +++ b/airtaudio/api/Pulse.cpp @@ -49,19 +49,29 @@ static const rtaudio_pa_format_mapping_t supported_sampleformats[] = { {audio::format_float, PA_SAMPLE_FLOAT32LE}, {audio::format_unknow, PA_SAMPLE_INVALID}}; -struct PulseAudioHandle { - pa_simple *s_play; - pa_simple *s_rec; - std::thread* thread; - std::condition_variable runnable_cv; - bool runnable; - PulseAudioHandle() : - s_play(0), - s_rec(0), - runnable(false) { - + +namespace airtaudio { + namespace api { + class PulsePrivate { + public: + pa_simple *s_play; + pa_simple *s_rec; + std::thread* thread; + std::condition_variable runnable_cv; + bool runnable; + PulsePrivate() : + s_play(0), + s_rec(0), + runnable(false) { + + } + }; } -}; +} +airtaudio::api::Pulse::Pulse() : + m_private(new airtaudio::api::PulsePrivate()) { + +} airtaudio::api::Pulse::~Pulse() { if (m_state != airtaudio::state_closed) { @@ -103,25 +113,20 @@ void airtaudio::api::Pulse::callbackEvent() { } enum airtaudio::error airtaudio::api::Pulse::closeStream() { - PulseAudioHandle *pah = static_cast(m_apiHandle); m_callbackInfo.isRunning = false; - if (pah) { - m_mutex.lock(); - if (m_state == airtaudio::state_stopped) { - pah->runnable = true; - pah->runnable_cv.notify_one();; - } - m_mutex.unlock(); - pah->thread->join(); - if (pah->s_play) { - pa_simple_flush(pah->s_play, nullptr); - pa_simple_free(pah->s_play); - } - if (pah->s_rec) { - pa_simple_free(pah->s_rec); - } - delete pah; - m_apiHandle = nullptr; + m_mutex.lock(); + if (m_state == airtaudio::state_stopped) { + m_private->runnable = true; + m_private->runnable_cv.notify_one();; + } + m_mutex.unlock(); + m_private->thread->join(); + if (m_private->s_play) { + pa_simple_flush(m_private->s_play, nullptr); + pa_simple_free(m_private->s_play); + } + if (m_private->s_rec) { + pa_simple_free(m_private->s_rec); } m_userBuffer[0].clear(); m_userBuffer[1].clear(); @@ -131,11 +136,10 @@ enum airtaudio::error airtaudio::api::Pulse::closeStream() { } void airtaudio::api::Pulse::callbackEventOneCycle() { - PulseAudioHandle *pah = static_cast(m_apiHandle); if (m_state == airtaudio::state_stopped) { std::unique_lock lck(m_mutex); - while (!pah->runnable) { - pah->runnable_cv.wait(lck); + while (!m_private->runnable) { + m_private->runnable_cv.wait(lck); } if (m_state != airtaudio::state_running) { m_mutex.unlock(); @@ -175,7 +179,7 @@ void airtaudio::api::Pulse::callbackEventOneCycle() { } else { bytes = m_nUserChannels[airtaudio::modeToIdTable(airtaudio::mode_output)] * m_bufferSize * audio::getFormatBytes(m_userFormat); } - if (pa_simple_write(pah->s_play, pulse_out, bytes, &pa_error) < 0) { + if (pa_simple_write(m_private->s_play, pulse_out, bytes, &pa_error) < 0) { ATA_ERROR("audio write error, " << pa_strerror(pa_error) << "."); return; } @@ -186,7 +190,7 @@ void airtaudio::api::Pulse::callbackEventOneCycle() { } else { bytes = m_nUserChannels[airtaudio::modeToIdTable(airtaudio::mode_input)] * m_bufferSize * audio::getFormatBytes(m_userFormat); } - if (pa_simple_read(pah->s_rec, pulse_in, bytes, &pa_error) < 0) { + if (pa_simple_read(m_private->s_rec, pulse_in, bytes, &pa_error) < 0) { ATA_ERROR("audio read error, " << pa_strerror(pa_error) << "."); return; } @@ -207,7 +211,6 @@ unlock: } enum airtaudio::error airtaudio::api::Pulse::startStream() { - PulseAudioHandle *pah = static_cast(m_apiHandle); if (m_state == airtaudio::state_closed) { ATA_ERROR("the stream is not open!"); return airtaudio::error_invalidUse; @@ -218,14 +221,13 @@ enum airtaudio::error airtaudio::api::Pulse::startStream() { } m_mutex.lock(); m_state = airtaudio::state_running; - pah->runnable = true; - pah->runnable_cv.notify_one(); + m_private->runnable = true; + m_private->runnable_cv.notify_one(); m_mutex.unlock(); return airtaudio::error_none; } enum airtaudio::error airtaudio::api::Pulse::stopStream() { - PulseAudioHandle *pah = static_cast(m_apiHandle); if (m_state == airtaudio::state_closed) { ATA_ERROR("the stream is not open!"); return airtaudio::error_invalidUse; @@ -236,9 +238,9 @@ enum airtaudio::error airtaudio::api::Pulse::stopStream() { } m_state = airtaudio::state_stopped; m_mutex.lock(); - if (pah && pah->s_play) { + if (m_private->s_play) { int32_t pa_error; - if (pa_simple_drain(pah->s_play, &pa_error) < 0) { + if (pa_simple_drain(m_private->s_play, &pa_error) < 0) { ATA_ERROR("error draining output device, " << pa_strerror(pa_error) << "."); m_mutex.unlock(); return airtaudio::error_systemError; @@ -250,7 +252,6 @@ enum airtaudio::error airtaudio::api::Pulse::stopStream() { } enum airtaudio::error airtaudio::api::Pulse::abortStream() { - PulseAudioHandle *pah = static_cast(m_apiHandle); if (m_state == airtaudio::state_closed) { ATA_ERROR("the stream is not open!"); return airtaudio::error_invalidUse; @@ -261,9 +262,9 @@ enum airtaudio::error airtaudio::api::Pulse::abortStream() { } m_state = airtaudio::state_stopped; m_mutex.lock(); - if (pah && pah->s_play) { + if (m_private && m_private->s_play) { int32_t pa_error; - if (pa_simple_flush(pah->s_play, &pa_error) < 0) { + if (pa_simple_flush(m_private->s_play, &pa_error) < 0) { ATA_ERROR("error flushing output device, " << pa_strerror(pa_error) << "."); m_mutex.unlock(); return airtaudio::error_systemError; @@ -282,7 +283,6 @@ bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t _device, audio::format _format, uint32_t *_bufferSize, airtaudio::StreamOptions *_options) { - PulseAudioHandle *pah = 0; uint64_t bufferBytes = 0; pa_sample_spec ss; if (_device != 0) { @@ -367,27 +367,18 @@ bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t _device, if (m_doConvertBuffer[modeToIdTable(_mode)]) { setConvertInfo(_mode, _firstChannel); } - if (!m_apiHandle) { - PulseAudioHandle *pah = new PulseAudioHandle; - if (!pah) { - ATA_ERROR("error allocating memory for handle."); - goto error; - } - m_apiHandle = pah; - } - pah = static_cast(m_apiHandle); int32_t error; switch (_mode) { case airtaudio::mode_input: - pah->s_rec = pa_simple_new(nullptr, "airtAudio", PA_STREAM_RECORD, nullptr, "Record", &ss, nullptr, nullptr, &error); - if (!pah->s_rec) { + m_private->s_rec = pa_simple_new(nullptr, "airtAudio", PA_STREAM_RECORD, nullptr, "Record", &ss, nullptr, nullptr, &error); + if (!m_private->s_rec) { ATA_ERROR("error connecting input to PulseAudio server."); goto error; } break; case airtaudio::mode_output: - pah->s_play = pa_simple_new(nullptr, "airtAudio", PA_STREAM_PLAYBACK, nullptr, "Playback", &ss, nullptr, nullptr, &error); - if (!pah->s_play) { + m_private->s_play = pa_simple_new(nullptr, "airtAudio", PA_STREAM_PLAYBACK, nullptr, "Playback", &ss, nullptr, nullptr, &error); + if (!m_private->s_play) { ATA_ERROR("error connecting output to PulseAudio server."); goto error; } @@ -404,8 +395,8 @@ bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t _device, } if (!m_callbackInfo.isRunning) { m_callbackInfo.isRunning = true; - pah->thread = new std::thread(pulseaudio_callback, this); - if (pah->thread == nullptr) { + m_private->thread = new std::thread(pulseaudio_callback, this); + if (m_private->thread == nullptr) { ATA_ERROR("error creating thread."); goto error; } @@ -413,10 +404,6 @@ bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t _device, m_state = airtaudio::state_stopped; return true; error: - if (pah && m_callbackInfo.isRunning) { - delete pah; - m_apiHandle = 0; - } for (int32_t i=0; i<2; i++) { m_userBuffer[i].clear(); } diff --git a/airtaudio/api/Pulse.h b/airtaudio/api/Pulse.h index 436d3ec..bd1d7c7 100644 --- a/airtaudio/api/Pulse.h +++ b/airtaudio/api/Pulse.h @@ -10,10 +10,12 @@ namespace airtaudio { namespace api { + class PulsePrivate; class Pulse: public airtaudio::Api { public: static airtaudio::Api* Create(); public: + Pulse(); virtual ~Pulse(); enum airtaudio::type getCurrentApi() { return airtaudio::type_pulse; @@ -31,6 +33,7 @@ namespace airtaudio { void callbackEventOneCycle(); void callbackEvent(); private: + std::unique_ptr m_private; std::vector m_devices; void saveDeviceInfo(); bool probeDeviceOpen(uint32_t _device,