Commit 96e8b37d authored by David Reid's avatar David Reid

Core Audio: Fix some issues with capture mode.

parent a9ee8cf8
...@@ -22674,6 +22674,11 @@ static ma_result ma_context_init__jack(const ma_context_config* pConfig, ma_cont ...@@ -22674,6 +22674,11 @@ static ma_result ma_context_init__jack(const ma_context_config* pConfig, ma_cont
Core Audio Backend Core Audio Backend
References
==========
- Technical Note TN2091: Device input using the HAL Output Audio Unit
https://developer.apple.com/library/archive/technotes/tn2091/_index.html
******************************************************************************/ ******************************************************************************/
#ifdef MA_HAS_COREAUDIO #ifdef MA_HAS_COREAUDIO
#include <TargetConditionals.h> #include <TargetConditionals.h>
...@@ -23822,10 +23827,10 @@ static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit au ...@@ -23822,10 +23827,10 @@ static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit au
MA_ASSERT(pContext != NULL); MA_ASSERT(pContext != NULL);
if (deviceType == ma_device_type_playback) { if (deviceType == ma_device_type_playback) {
deviceScope = kAudioUnitScope_Output; deviceScope = kAudioUnitScope_Input;
deviceBus = MA_COREAUDIO_OUTPUT_BUS; deviceBus = MA_COREAUDIO_OUTPUT_BUS;
} else { } else {
deviceScope = kAudioUnitScope_Input; deviceScope = kAudioUnitScope_Output;
deviceBus = MA_COREAUDIO_INPUT_BUS; deviceBus = MA_COREAUDIO_INPUT_BUS;
} }
...@@ -24850,10 +24855,11 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev ...@@ -24850,10 +24855,11 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev
UInt32 enableIOFlag; UInt32 enableIOFlag;
AudioStreamBasicDescription bestFormat; AudioStreamBasicDescription bestFormat;
ma_uint32 actualPeriodSizeInFrames; ma_uint32 actualPeriodSizeInFrames;
ma_uint32 actualPeriodSizeInFramesSize = sizeof(actualPeriodSizeInFrames);
AURenderCallbackStruct callbackInfo; AURenderCallbackStruct callbackInfo;
#if defined(MA_APPLE_DESKTOP) #if defined(MA_APPLE_DESKTOP)
AudioObjectID deviceObjectID; AudioObjectID deviceObjectID;
#else
ma_uint32 actualPeriodSizeInFramesSize = sizeof(actualPeriodSizeInFrames);
#endif #endif
/* This API should only be used for a single device type: playback or capture. No full-duplex mode. */ /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */
...@@ -24919,7 +24925,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev ...@@ -24919,7 +24925,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev
/* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */ /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */
#if defined(MA_APPLE_DESKTOP) #if defined(MA_APPLE_DESKTOP)
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS, &deviceObjectID, sizeof(AudioDeviceID)); status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID));
if (status != noErr) { if (status != noErr) {
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
return ma_result_from_OSStatus(result); return ma_result_from_OSStatus(result);
...@@ -24961,20 +24967,22 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev ...@@ -24961,20 +24967,22 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev
On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to. On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to.
*/ */
{ {
AudioStreamBasicDescription origFormat;
UInt32 origFormatSize = sizeof(origFormat);
AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
#if defined(MA_APPLE_DESKTOP) if (deviceType == ma_device_type_playback) {
AudioStreamBasicDescription origFormat; status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize);
UInt32 origFormatSize; } else {
status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize);
origFormatSize = sizeof(origFormat); }
status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &origFormat, &origFormatSize);
if (status != noErr) { if (status != noErr) {
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
return result; return result;
} }
#if defined(MA_APPLE_DESKTOP)
result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, pData->usingDefaultFormat, pData->usingDefaultChannels, pData->usingDefaultSampleRate, &origFormat, &bestFormat); result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, pData->usingDefaultFormat, pData->usingDefaultChannels, pData->usingDefaultSampleRate, &origFormat, &bestFormat);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
...@@ -24987,11 +24995,32 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev ...@@ -24987,11 +24995,32 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev
I cannot remember where I read this in the documentation and I cannot find it again. For now I'm going to remove this I cannot remember where I read this in the documentation and I cannot find it again. For now I'm going to remove this
and see what the feedback from the community is like. If this results in issues we can add it back in again. The idea and see what the feedback from the community is like. If this results in issues we can add it back in again. The idea
is that the closest sample rate natively supported by the backend to the requested sample rate should be used if possible. is that the closest sample rate natively supported by the backend to the requested sample rate should be used if possible.
Update 2020-11-06:
I have found the documentation that talks about keeping the sample rate consistent:
Technical Note TN2091: Device input using the HAL Output Audio Unit
https://developer.apple.com/library/archive/technotes/tn2091/_index.html
This documentation says the following:
The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY
variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate
conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with
another AudioConverter.
The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We
therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it
safe and apply the same rule to output as well.
I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender()
returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but
this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format. At
this point I'm not sure how to change the sample rate on the device even if the device reports native support for it. If
anybody has any suggestions on this please let me know.
*/ */
#if 0
/* From what I can see, Apple's documentation implies that we should keep the sample rate consistent. */
bestFormat.mSampleRate = origFormat.mSampleRate; bestFormat.mSampleRate = origFormat.mSampleRate;
#endif
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
if (status != noErr) { if (status != noErr) {
...@@ -24999,12 +25028,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev ...@@ -24999,12 +25028,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev
bestFormat = origFormat; bestFormat = origFormat;
} }
#else #else
UInt32 propSize = sizeof(bestFormat); bestFormat = origFormat;
status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
if (status != noErr) {
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
return ma_result_from_OSStatus(status);
}
/* /*
Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try
...@@ -25119,7 +25143,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev ...@@ -25119,7 +25143,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev
of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice. of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice.
*/ */
{ {
/*AudioUnitScope propScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; /*AudioUnitScope propScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Output : kAudioUnitScope_Input;
AudioUnitElement propBus = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; AudioUnitElement propBus = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, propScope, propBus, &actualBufferSizeInFrames, sizeof(actualBufferSizeInFrames)); status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, propScope, propBus, &actualBufferSizeInFrames, sizeof(actualBufferSizeInFrames));
...@@ -25184,14 +25208,14 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev ...@@ -25184,14 +25208,14 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev
callbackInfo.inputProcRefCon = pDevice_DoNotReference; callbackInfo.inputProcRefCon = pDevice_DoNotReference;
if (deviceType == ma_device_type_playback) { if (deviceType == ma_device_type_playback) {
callbackInfo.inputProc = ma_on_output__coreaudio; callbackInfo.inputProc = ma_on_output__coreaudio;
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, MA_COREAUDIO_OUTPUT_BUS, &callbackInfo, sizeof(callbackInfo)); status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));
if (status != noErr) { if (status != noErr) {
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
return ma_result_from_OSStatus(status); return ma_result_from_OSStatus(status);
} }
} else { } else {
callbackInfo.inputProc = ma_on_input__coreaudio; callbackInfo.inputProc = ma_on_input__coreaudio;
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, MA_COREAUDIO_INPUT_BUS, &callbackInfo, sizeof(callbackInfo)); status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));
if (status != noErr) { if (status != noErr) {
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
return ma_result_from_OSStatus(status); return ma_result_from_OSStatus(status);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment