From 650f24c288d08db41ff8d4e4c2c64ccec7cde674 Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Thu, 12 Feb 2015 23:19:48 +0100 Subject: [PATCH] [DEV] correct IOs and MacOs audio interface (tested on MacOs but not on IOs) --- airtaudio/Api.cpp | 2 + airtaudio/api/Core.cpp | 106 ++++++++++++++++----------------------- airtaudio/api/Core.h | 7 ++- airtaudio/api/CoreIos.h | 8 +-- airtaudio/api/CoreIos.mm | 29 ++++++----- 5 files changed, 65 insertions(+), 87 deletions(-) diff --git a/airtaudio/Api.cpp b/airtaudio/Api.cpp index fac07f9..0ba0e8e 100644 --- a/airtaudio/Api.cpp +++ b/airtaudio/Api.cpp @@ -55,6 +55,7 @@ airtaudio::Api::~Api() { } enum airtaudio::error airtaudio::Api::startStream() { + ATA_VERBOSE("Start Stream"); m_startTime = std::chrono::system_clock::now(); m_duration = std::chrono::duration(0); return airtaudio::error_none; @@ -156,6 +157,7 @@ uint32_t airtaudio::Api::getDefaultOutputDevice() { } enum airtaudio::error airtaudio::Api::closeStream() { + ATA_VERBOSE("Close Stream"); // MUST be implemented in subclasses! return airtaudio::error_none; } diff --git a/airtaudio/api/Core.cpp b/airtaudio/api/Core.cpp index 73dfc4b..711653d 100644 --- a/airtaudio/api/Core.cpp +++ b/airtaudio/api/Core.cpp @@ -346,9 +346,10 @@ airtaudio::DeviceInfo airtaudio::api::Core::getDeviceInfo(uint32_t _device) { } } info.sampleRates.clear(); - for (uint32_t k=0; k= (uint32_t) minimumRate && SAMPLE_RATES[k] <= (uint32_t) maximumRate) { - info.sampleRates.push_back(SAMPLE_RATES[k]); + for (auto &it : airtaudio::genericSampleRate()) { + if ( it >= minimumRate + && it <= maximumRate) { + info.sampleRates.push_back(it); } } if (info.sampleRates.size() == 0) { @@ -388,10 +389,10 @@ OSStatus airtaudio::api::Core::callbackEvent(AudioDeviceID _inDevice, } } -static OSStatus xrunListener(AudioObjectID _inDevice, - uint32_t _nAddresses, - const AudioObjectPropertyAddress _properties[], - void* _userData) { +OSStatus airtaudio::api::Core::xrunListener(AudioObjectID _inDevice, + uint32_t _nAddresses, + const AudioObjectPropertyAddress _properties[], + void* _userData) { airtaudio::api::Core* myClass = reinterpret_cast(_userData); for (uint32_t i=0; i<_nAddresses; i++) { if (_properties[i].mSelector == kAudioDeviceProcessorOverload) { @@ -406,9 +407,9 @@ static OSStatus xrunListener(AudioObjectID _inDevice, } static OSStatus rateListener(AudioObjectID _inDevice, - uint32_t _nAddresses, - const AudioObjectPropertyAddress _properties[], - void* _ratePointer) { + UInt32 _nAddresses, + const AudioObjectPropertyAddress _properties[], + void* _ratePointer) { double *rate = (double*)_ratePointer; uint32_t dataSize = sizeof(double); AudioObjectPropertyAddress property = { @@ -593,26 +594,6 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device, } m_bufferSize = *_bufferSize; m_nBuffers = 1; - // Try to set "hog" mode ... it's not clear to me this is working. - if ( _options != nullptr - && _options->flags & HOG_DEVICE) { - pid_t hog_pid; - dataSize = sizeof(hog_pid); - property.mSelector = kAudioDevicePropertyHogMode; - result = AudioObjectGetPropertyData(id, &property, 0, nullptr, &dataSize, &hog_pid); - if (result != noErr) { - ATA_ERROR("system error (" << getErrorCode(result) << ") getting 'hog' state!"); - return false; - } - if (hog_pid != getpid()) { - hog_pid = getpid(); - result = AudioObjectSetPropertyData(id, &property, 0, nullptr, dataSize, &hog_pid); - if (result != noErr) { - ATA_ERROR("system error (" << getErrorCode(result) << ") setting 'hog' state!"); - return false; - } - } - } // Check and if necessary, change the sample rate for the device. double nominalRate; dataSize = sizeof(double); @@ -627,7 +608,7 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device, // Set a property listener for the sample rate change double reportedRate = 0.0; AudioObjectPropertyAddress tmp = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; - result = AudioObjectAddPropertyListener(id, &tmp, rateListener, (void *) &reportedRate); + result = AudioObjectAddPropertyListener(id, &tmp, &rateListener, (void *) &reportedRate); if (result != noErr) { ATA_ERROR("system error (" << getErrorCode(result) << ") setting sample rate property listener for device (" << _device << ")."); return false; @@ -648,7 +629,7 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device, usleep(5000); } // Remove the property listener. - AudioObjectRemovePropertyListener(id, &tmp, rateListener, (void *) &reportedRate); + AudioObjectRemovePropertyListener(id, &tmp, &rateListener, (void *) &reportedRate); if (microCounter > 5000000) { ATA_ERROR("timeout waiting for sample rate update for device (" << _device << ")."); return false; @@ -763,7 +744,7 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device, // From the CoreAudio documentation, PCM data must be supplied as // 32-bit floats. m_userFormat = _format; - m_deviceFormat[modeToIdTable(_mode)] = FLOAT32; + m_deviceFormat[modeToIdTable(_mode)] = audio::format_float; if (streamCount == 1) { m_nDeviceChannels[modeToIdTable(_mode)] = description.mChannelsPerFrame; } else { @@ -799,9 +780,8 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device, uint64_t bufferBytes; bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_userFormat); // m_userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1); - m_userBuffer[modeToIdTable(_mode)] = (char *) malloc(bufferBytes * sizeof(char)); - memset(m_userBuffer[modeToIdTable(_mode)], 0, bufferBytes * sizeof(char)); - if (m_userBuffer[modeToIdTable(_mode)] == nullptr) { + m_userBuffer[modeToIdTable(_mode)].resize(bufferBytes, 0); + if (m_userBuffer[modeToIdTable(_mode)].size() == 0) { ATA_ERROR("error allocating user buffer memory."); goto error; } @@ -837,7 +817,7 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device, m_sampleRate = _sampleRate; m_device[modeToIdTable(_mode)] = _device; m_state = airtaudio::state_stopped; - m_callbackInfo.object = (void *) this; + ATA_VERBOSE("Set state as stopped"); // Setup the buffer conversion information structure. if (m_doConvertBuffer[modeToIdTable(_mode)]) { if (streamCount > 1) { @@ -853,10 +833,10 @@ 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, &m_private->procId[modeToIdTable(_mode)]); + result = AudioDeviceCreateIOProcID(id, &airtaudio::api::Core::callbackEvent, this, &m_private->procId[modeToIdTable(_mode)]); #else // deprecated in favor of AudioDeviceCreateIOProcID() - result = AudioDeviceAddIOProc(id, &airtaudio::api::Core::callbackEvent, (void *) &m_callbackInfo); + result = AudioDeviceAddIOProc(id, &airtaudio::api::Core::callbackEvent, this); #endif if (result != noErr) { ATA_ERROR("system error setting callback for device (" << _device << ")."); @@ -871,20 +851,17 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device, } // Setup the device property listener for over/underload. property.mSelector = kAudioDeviceProcessorOverload; - result = AudioObjectAddPropertyListener(id, &property, xrunListener, this); + result = AudioObjectAddPropertyListener(id, &property, &airtaudio::api::Core::xrunListener, this); return true; error: - for (int32_t i=0; i<2; i++) { - if (m_userBuffer[i]) { - free(m_userBuffer[i]); - m_userBuffer[i] = 0; - } - } + m_userBuffer[0].clear(); + m_userBuffer[1].clear(); if (m_deviceBuffer) { free(m_deviceBuffer); m_deviceBuffer = 0; } m_state = airtaudio::state_closed; + ATA_VERBOSE("Set state as closed"); return false; } @@ -918,18 +895,15 @@ enum airtaudio::error airtaudio::api::Core::closeStream() { AudioDeviceRemoveIOProc(m_private->id[1], &airtaudio::api::Core::callbackEvent); #endif } - for (int32_t i=0; i<2; i++) { - if (m_userBuffer[i]) { - free(m_userBuffer[i]); - m_userBuffer[i] = nullptr; - } - } + m_userBuffer[0].clear(); + m_userBuffer[1].clear(); if (m_deviceBuffer) { free(m_deviceBuffer); m_deviceBuffer = nullptr; } m_mode = airtaudio::mode_unknow; m_state = airtaudio::state_closed; + ATA_VERBOSE("Set state as closed"); return airtaudio::error_none; } @@ -964,6 +938,7 @@ enum airtaudio::error airtaudio::api::Core::startStream() { m_private->drainCounter = 0; m_private->internalDrain = false; m_state = airtaudio::state_running; + ATA_VERBOSE("Set state as running"); unlock: if (result == noErr) { return airtaudio::error_none; @@ -1003,6 +978,7 @@ enum airtaudio::error airtaudio::api::Core::stopStream() { } } m_state = airtaudio::state_stopped; + ATA_VERBOSE("Set state as stopped"); unlock: if (result == noErr) { return airtaudio::error_none; @@ -1047,6 +1023,7 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId, // Check if we were draining the stream and signal is finished. if (m_private->drainCounter > 3) { m_state = airtaudio::state_stopping; + ATA_VERBOSE("Set state as stopping"); if (m_private->internalDrain == true) { new std::thread(&airtaudio::api::Core::coreStopStream, this); } else { @@ -1064,21 +1041,22 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId, enum airtaudio::status status = airtaudio::status_ok; if ( m_mode != airtaudio::mode_input && m_private->xrun[0] == true) { - status |= airtaudio::status_underflow; + status = airtaudio::status_underflow; m_private->xrun[0] = false; } if ( m_mode != airtaudio::mode_output && m_private->xrun[1] == true) { - status |= airtaudio::mode_input_OVERFLOW; + status = airtaudio::status_overflow; m_private->xrun[1] = false; } - int32_t cbReturnValue = info->callback(m_userBuffer[0], - m_userBuffer[1], + int32_t cbReturnValue = info->callback(&m_userBuffer[0][0], + &m_userBuffer[1][0], m_bufferSize, streamTime, status); if (cbReturnValue == 2) { m_state = airtaudio::state_stopping; + ATA_VERBOSE("Set state as stopping"); m_private->drainCounter = 2; abortStream(); return true; @@ -1108,19 +1086,19 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId, if (m_doConvertBuffer[0]) { // convert directly to CoreAudio stream buffer convertBuffer((char*)_outBufferList->mBuffers[m_private->iStream[0]].mData, - m_userBuffer[0], + &m_userBuffer[0][0], m_convertInfo[0]); } else { // copy from user buffer memcpy(_outBufferList->mBuffers[m_private->iStream[0]].mData, - m_userBuffer[0], + &m_userBuffer[0][0], _outBufferList->mBuffers[m_private->iStream[0]].mDataByteSize); } } else { // fill multiple streams - float *inBuffer = (float *) m_userBuffer[0]; + float *inBuffer = (float *) &m_userBuffer[0][0]; if (m_doConvertBuffer[0]) { - convertBuffer(m_deviceBuffer, m_userBuffer[0], m_convertInfo[0]); + convertBuffer(m_deviceBuffer, &m_userBuffer[0][0], m_convertInfo[0]); inBuffer = (float *) m_deviceBuffer; } if (m_deviceInterleaved[0] == false) { // mono mode @@ -1194,16 +1172,16 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId, if (m_private->nStreams[1] == 1) { if (m_doConvertBuffer[1]) { // convert directly from CoreAudio stream buffer - convertBuffer(m_userBuffer[1], + convertBuffer(&m_userBuffer[1][0], (char *) _inBufferList->mBuffers[m_private->iStream[1]].mData, m_convertInfo[1]); } else { // copy to user buffer - memcpy(m_userBuffer[1], + memcpy(&m_userBuffer[1][0], _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]; + float *outBuffer = (float *) &m_userBuffer[1][0]; if (m_doConvertBuffer[1]) { outBuffer = (float *) m_deviceBuffer; } @@ -1266,7 +1244,7 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId, } } if (m_doConvertBuffer[1]) { // convert from our internal "device" buffer - convertBuffer(m_userBuffer[1], + convertBuffer(&m_userBuffer[1][0], m_deviceBuffer, m_convertInfo[1]); } diff --git a/airtaudio/api/Core.h b/airtaudio/api/Core.h index 7158f94..1fc1b6a 100644 --- a/airtaudio/api/Core.h +++ b/airtaudio/api/Core.h @@ -45,7 +45,7 @@ namespace airtaudio { AudioBufferList* _outOutputData, const AudioTimeStamp* _inOutputTime, void* _infoPointer); - void coreStopStream(void *_userData); + static void coreStopStream(void *_userData); private: std::unique_ptr m_private; bool probeDeviceOpen(uint32_t _device, @@ -57,6 +57,11 @@ namespace airtaudio { uint32_t *_bufferSize, airtaudio::StreamOptions *_options); static const char* getErrorCode(OSStatus _code); + + static OSStatus xrunListener(AudioObjectID _inDevice, + uint32_t _nAddresses, + const AudioObjectPropertyAddress _properties[], + void* _userData); }; }; }; diff --git a/airtaudio/api/CoreIos.h b/airtaudio/api/CoreIos.h index ac34ff6..4cfd9a4 100644 --- a/airtaudio/api/CoreIos.h +++ b/airtaudio/api/CoreIos.h @@ -45,14 +45,8 @@ namespace airtaudio { public: void callBackEvent(void* _data, int32_t _frameRate); - private: + public: std::unique_ptr m_private; - static OSStatus playbackCallback(void *_userData, - AudioUnitRenderActionFlags* _ioActionFlags, - const AudioTimeStamp* _inTimeStamp, - uint32_t _inBusNumber, - uint32_t _inNumberFrames, - AudioBufferList* _ioData); }; }; }; diff --git a/airtaudio/api/CoreIos.mm b/airtaudio/api/CoreIos.mm index 8410734..d89e4dd 100644 --- a/airtaudio/api/CoreIos.mm +++ b/airtaudio/api/CoreIos.mm @@ -69,8 +69,6 @@ airtaudio::api::CoreIos::CoreIos(void) : airtaudio::api::CoreIos::~CoreIos(void) { ATA_INFO("Destroy CoreIOs interface"); AudioUnitUninitialize(m_private->audioUnit); - delete m_private; - m_private = nullptr; } uint32_t airtaudio::api::CoreIos::getDeviceCount(void) { @@ -131,13 +129,13 @@ void airtaudio::api::CoreIos::callBackEvent(void* _data, int32_t doStopStream = 0; std::chrono::system_clock::time_point streamTime = getStreamTime(); enum airtaudio::status status = airtaudio::status_ok; - if (m_doConvertBuffer[airtaudio::mode_output] == true) { - doStopStream = m_callbackInfo.callback(m_userBuffer[airtaudio::mode_output], + if (m_doConvertBuffer[modeToIdTable(airtaudio::mode_output)] == true) { + doStopStream = m_callbackInfo.callback(&m_userBuffer[modeToIdTable(airtaudio::mode_output)][0], nullptr, _frameRate, streamTime, status); - convertBuffer((char*)_data, (char*)m_userBuffer[airtaudio::mode_output], m_convertInfo[airtaudio::mode_output]); + convertBuffer((char*)_data, &m_userBuffer[modeToIdTable(airtaudio::mode_output)][0], m_convertInfo[modeToIdTable(airtaudio::mode_output)]); } else { doStopStream = m_callbackInfo.callback(_data, nullptr, @@ -152,12 +150,13 @@ void airtaudio::api::CoreIos::callBackEvent(void* _data, airtaudio::Api::tickStreamTime(); } -OSStatus airtaudio::api::CoreIos::playbackCallback(void *_userData, - AudioUnitRenderActionFlags* _ioActionFlags, - const AudioTimeStamp* _inTimeStamp, - uint32_t _inBusNumber, - uint32_t _inNumberFrames, - AudioBufferList* _ioData) { + +static OSStatus playbackCallback(void *_userData, + AudioUnitRenderActionFlags* _ioActionFlags, + const AudioTimeStamp* _inTimeStamp, + uint32_t _inBusNumber, + uint32_t _inNumberFrames, + AudioBufferList* _ioData) { if (_userData == nullptr) { ATA_ERROR("callback event ... nullptr pointer"); return -1; @@ -197,7 +196,7 @@ bool airtaudio::api::CoreIos::probeDeviceOpen(uint32_t _device, m_doByteSwap[modeToIdTable(_mode)] = false; // for endienness ... // TODO : For now, we write it in hard ==> to be update later ... - m_deviceFormat[modeToIdTable(_mode)] = SINT16; + m_deviceFormat[modeToIdTable(_mode)] = audio::format_int16; m_nDeviceChannels[modeToIdTable(_mode)] = 2; m_deviceInterleaved[modeToIdTable(_mode)] = true; @@ -215,8 +214,8 @@ bool airtaudio::api::CoreIos::probeDeviceOpen(uint32_t _device, if (m_doConvertBuffer[modeToIdTable(_mode)] == true) { // Allocate necessary internal buffers. uint64_t bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * m_bufferSize * audio::getFormatBytes(m_userFormat); - m_userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1); - if (m_userBuffer[modeToIdTable(_mode)] == nullptr) { + m_userBuffer[modeToIdTable(_mode)].resize(bufferBytes, 0); + if (m_userBuffer[modeToIdTable(_mode)].size() == 0) { ATA_ERROR("error allocating user buffer memory."); } setConvertInfo(_mode, _firstChannel); @@ -285,7 +284,7 @@ bool airtaudio::api::CoreIos::probeDeviceOpen(uint32_t _device, // Set output callback AURenderCallbackStruct callbackStruct; - callbackStruct.inputProc = &airtaudio::api::CoreIos::playbackCallback; + callbackStruct.inputProc = &playbackCallback; callbackStruct.inputProcRefCon = this; status = AudioUnitSetProperty(m_private->audioUnit, kAudioUnitProperty_SetRenderCallback,