[DEV] correct IOs and MacOs audio interface (tested on MacOs but not on IOs)
This commit is contained in:
parent
4fc9a3e05f
commit
650f24c288
@ -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;
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user