[DEV] correct IOs and MacOs audio interface (tested on MacOs but not on IOs)

This commit is contained in:
Edouard DUPIN 2015-02-12 23:19:48 +01:00
parent 4fc9a3e05f
commit 650f24c288
5 changed files with 65 additions and 87 deletions

View File

@ -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<int64_t, std::micro>(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;
}

View File

@ -346,9 +346,10 @@ airtaudio::DeviceInfo airtaudio::api::Core::getDeviceInfo(uint32_t _device) {
}
}
info.sampleRates.clear();
for (uint32_t k=0; k<MAX_SAMPLE_RATES; k++) {
if (SAMPLE_RATES[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<airtaudio::api::Core*>(_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]);
}

View File

@ -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<CorePrivate> 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);
};
};
};

View File

@ -45,14 +45,8 @@ namespace airtaudio {
public:
void callBackEvent(void* _data,
int32_t _frameRate);
private:
public:
std::unique_ptr<CoreIosPrivate> m_private;
static OSStatus playbackCallback(void *_userData,
AudioUnitRenderActionFlags* _ioActionFlags,
const AudioTimeStamp* _inTimeStamp,
uint32_t _inBusNumber,
uint32_t _inNumberFrames,
AudioBufferList* _ioData);
};
};
};

View File

@ -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,