mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-24 01:04:02 +02:00
Core Audio: Include format information in device enumeration.
This commit is contained in:
+231
-261
@@ -35551,118 +35551,30 @@ static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDe
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
||||||
{
|
|
||||||
#if defined(MA_APPLE_DESKTOP)
|
#if defined(MA_APPLE_DESKTOP)
|
||||||
UInt32 deviceCount;
|
static ma_bool32 ma_context_enumerate_device_by_AudioObjectID__coreaudio(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectID defaultDeviceObjectID, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData)
|
||||||
AudioObjectID* pDeviceObjectIDs;
|
|
||||||
AudioObjectID defaultDeviceObjectIDPlayback;
|
|
||||||
AudioObjectID defaultDeviceObjectIDCapture;
|
|
||||||
ma_result result;
|
|
||||||
UInt32 iDevice;
|
|
||||||
|
|
||||||
ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */
|
|
||||||
ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */
|
|
||||||
|
|
||||||
result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
|
|
||||||
if (result != MA_SUCCESS) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
|
|
||||||
AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
|
|
||||||
ma_device_info info;
|
|
||||||
|
|
||||||
MA_ZERO_OBJECT(&info);
|
|
||||||
if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
|
|
||||||
if (deviceObjectID == defaultDeviceObjectIDPlayback) {
|
|
||||||
info.isDefault = MA_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!callback(ma_device_type_playback, &info, pUserData)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
|
|
||||||
if (deviceObjectID == defaultDeviceObjectIDCapture) {
|
|
||||||
info.isDefault = MA_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!callback(ma_device_type_capture, &info, pUserData)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
|
|
||||||
#else
|
|
||||||
ma_device_info info;
|
|
||||||
NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
|
|
||||||
NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
|
|
||||||
|
|
||||||
for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
|
|
||||||
ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
|
|
||||||
if (!callback(ma_device_type_playback, &info, pUserData)) {
|
|
||||||
return MA_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
|
|
||||||
ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
|
|
||||||
if (!callback(ma_device_type_capture, &info, pUserData)) {
|
|
||||||
return MA_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return MA_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
|
||||||
{
|
{
|
||||||
ma_result result;
|
ma_device_info deviceInfo;
|
||||||
|
|
||||||
MA_ASSERT(pContext != NULL);
|
MA_ZERO_OBJECT(&deviceInfo);
|
||||||
|
|
||||||
#if defined(MA_APPLE_DESKTOP)
|
/* Default. */
|
||||||
/* Desktop */
|
if (deviceObjectID == defaultDeviceObjectID) {
|
||||||
|
deviceInfo.isDefault = MA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ID. */
|
||||||
|
if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(deviceInfo.id.coreaudio), deviceInfo.id.coreaudio) != MA_SUCCESS) {
|
||||||
|
return MA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Name. */
|
||||||
|
if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(deviceInfo.name), deviceInfo.name) != MA_SUCCESS) {
|
||||||
|
return MA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Data Format. */
|
||||||
{
|
{
|
||||||
AudioObjectID deviceObjectID;
|
|
||||||
AudioObjectID defaultDeviceObjectID;
|
|
||||||
UInt32 streamDescriptionCount;
|
|
||||||
AudioStreamRangedDescription* pStreamDescriptions;
|
|
||||||
UInt32 iStreamDescription;
|
|
||||||
UInt32 sampleRateRangeCount;
|
|
||||||
AudioValueRange* pSampleRateRanges;
|
|
||||||
|
|
||||||
ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */
|
|
||||||
|
|
||||||
result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
|
|
||||||
if (result != MA_SUCCESS) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio);
|
|
||||||
if (result != MA_SUCCESS) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name);
|
|
||||||
if (result != MA_SUCCESS) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deviceObjectID == defaultDeviceObjectID) {
|
|
||||||
pDeviceInfo->isDefault = MA_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
There could be a large number of permutations here. Fortunately there is only a single channel count
|
There could be a large number of permutations here. Fortunately there is only a single channel count
|
||||||
being reported which reduces this quite a bit. For sample rates we're only reporting those that are
|
being reported which reduces this quite a bit. For sample rates we're only reporting those that are
|
||||||
@@ -35671,200 +35583,258 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_
|
|||||||
if some driver performs software data conversion and therefore reports every possible format and
|
if some driver performs software data conversion and therefore reports every possible format and
|
||||||
sample rate.
|
sample rate.
|
||||||
*/
|
*/
|
||||||
pDeviceInfo->nativeDataFormatCount = 0;
|
ma_result result;
|
||||||
|
ma_format uniqueFormats[ma_format_count];
|
||||||
|
ma_uint32 uniqueFormatCount = 0;
|
||||||
|
ma_uint32 channels;
|
||||||
|
UInt32 streamDescriptionCount;
|
||||||
|
AudioStreamRangedDescription* pStreamDescriptions;
|
||||||
|
UInt32 iStreamDescription;
|
||||||
|
|
||||||
|
deviceInfo.nativeDataFormatCount = 0;
|
||||||
|
|
||||||
|
/* Channels. */
|
||||||
|
result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
return MA_TRUE; /* Failed to get channel count. */
|
||||||
|
}
|
||||||
|
|
||||||
/* Formats. */
|
/* Formats. */
|
||||||
{
|
result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);
|
||||||
ma_format uniqueFormats[ma_format_count];
|
if (result != MA_SUCCESS) {
|
||||||
ma_uint32 uniqueFormatCount = 0;
|
return MA_TRUE;
|
||||||
ma_uint32 channels;
|
}
|
||||||
|
|
||||||
/* Channels. */
|
for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) {
|
||||||
result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels);
|
ma_format format;
|
||||||
|
ma_bool32 hasFormatBeenHandled = MA_FALSE;
|
||||||
|
ma_uint32 iOutputFormat;
|
||||||
|
ma_uint32 iSampleRate;
|
||||||
|
UInt32 sampleRateRangeCount;
|
||||||
|
AudioValueRange* pSampleRateRanges;
|
||||||
|
|
||||||
|
result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format);
|
||||||
if (result != MA_SUCCESS) {
|
if (result != MA_SUCCESS) {
|
||||||
return result;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Formats. */
|
MA_ASSERT(format != ma_format_unknown);
|
||||||
result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);
|
|
||||||
if (result != MA_SUCCESS) {
|
/* Make sure the format isn't already in the output list. */
|
||||||
return result;
|
for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) {
|
||||||
|
if (uniqueFormats[iOutputFormat] == format) {
|
||||||
|
hasFormatBeenHandled = MA_TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) {
|
/* If we've already handled this format just skip it. */
|
||||||
ma_format format;
|
if (hasFormatBeenHandled) {
|
||||||
ma_bool32 hasFormatBeenHandled = MA_FALSE;
|
continue;
|
||||||
ma_uint32 iOutputFormat;
|
}
|
||||||
ma_uint32 iSampleRate;
|
|
||||||
|
|
||||||
result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format);
|
uniqueFormats[uniqueFormatCount] = format;
|
||||||
if (result != MA_SUCCESS) {
|
uniqueFormatCount += 1;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
MA_ASSERT(format != ma_format_unknown);
|
/* Sample Rates */
|
||||||
|
result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
return MA_TRUE; /* Failed to retrieve sample rates. */
|
||||||
|
}
|
||||||
|
|
||||||
/* Make sure the format isn't already in the output list. */
|
/*
|
||||||
for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) {
|
Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are
|
||||||
if (uniqueFormats[iOutputFormat] == format) {
|
between this range.
|
||||||
hasFormatBeenHandled = MA_TRUE;
|
*/
|
||||||
break;
|
for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) {
|
||||||
}
|
ma_uint32 iStandardSampleRate;
|
||||||
}
|
for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {
|
||||||
|
ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];
|
||||||
|
if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) {
|
||||||
|
/* We have a new data format. Add it to the list. */
|
||||||
|
deviceInfo.nativeDataFormats[deviceInfo.nativeDataFormatCount].format = format;
|
||||||
|
deviceInfo.nativeDataFormats[deviceInfo.nativeDataFormatCount].channels = channels;
|
||||||
|
deviceInfo.nativeDataFormats[deviceInfo.nativeDataFormatCount].sampleRate = standardSampleRate;
|
||||||
|
deviceInfo.nativeDataFormats[deviceInfo.nativeDataFormatCount].flags = 0;
|
||||||
|
deviceInfo.nativeDataFormatCount += 1;
|
||||||
|
|
||||||
/* If we've already handled this format just skip it. */
|
if (deviceInfo.nativeDataFormatCount >= ma_countof(deviceInfo.nativeDataFormats)) {
|
||||||
if (hasFormatBeenHandled) {
|
break; /* No more room for any more formats. */
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
uniqueFormats[uniqueFormatCount] = format;
|
|
||||||
uniqueFormatCount += 1;
|
|
||||||
|
|
||||||
/* Sample Rates */
|
|
||||||
result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
|
|
||||||
if (result != MA_SUCCESS) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are
|
|
||||||
between this range.
|
|
||||||
*/
|
|
||||||
for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) {
|
|
||||||
ma_uint32 iStandardSampleRate;
|
|
||||||
for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {
|
|
||||||
ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];
|
|
||||||
if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) {
|
|
||||||
/* We have a new data format. Add it to the list. */
|
|
||||||
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
|
|
||||||
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
|
|
||||||
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate;
|
|
||||||
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
|
|
||||||
pDeviceInfo->nativeDataFormatCount += 1;
|
|
||||||
|
|
||||||
if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
|
|
||||||
break; /* No more room for any more formats. */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
|
|
||||||
|
|
||||||
if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
|
|
||||||
break; /* No more room for any more formats. */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ma_free(pStreamDescriptions, &pContext->allocationCallbacks);
|
ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
|
||||||
|
|
||||||
|
if (deviceInfo.nativeDataFormatCount >= ma_countof(deviceInfo.nativeDataFormats)) {
|
||||||
|
break; /* No more room for any more formats. */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ma_free(pStreamDescriptions, &pContext->allocationCallbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return callback(deviceType, &deviceInfo, pUserData);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
/* Mobile */
|
static ma_bool32 ma_context_enumerate_device_by_AVAudioSessionPortDescription__coreaudio(ma_context* pContext, AVAudioSessionPortDescription* pPortDesc, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData)
|
||||||
{
|
{
|
||||||
ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext);
|
ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(pContext);
|
||||||
AudioComponentDescription desc;
|
ma_device_info deviceInfo;
|
||||||
AudioComponent component;
|
AudioComponentDescription desc;
|
||||||
AudioUnit audioUnit;
|
AudioComponent component;
|
||||||
OSStatus status;
|
AudioUnit audioUnit;
|
||||||
AudioUnitScope formatScope;
|
OSStatus status;
|
||||||
AudioUnitElement formatElement;
|
AudioUnitScope formatScope;
|
||||||
AudioStreamBasicDescription bestFormat;
|
AudioUnitElement formatElement;
|
||||||
UInt32 propSize;
|
AudioStreamBasicDescription bestFormat;
|
||||||
|
UInt32 propSize;
|
||||||
|
|
||||||
/* We want to ensure we use a consistent device name to device enumeration. */
|
MA_ZERO_OBJECT(&deviceInfo);
|
||||||
if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') {
|
|
||||||
ma_bool32 found = MA_FALSE;
|
|
||||||
if (deviceType == ma_device_type_playback) {
|
|
||||||
NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
|
|
||||||
for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
|
|
||||||
if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
|
|
||||||
ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
|
|
||||||
found = MA_TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
|
|
||||||
for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
|
|
||||||
if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
|
|
||||||
ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
|
|
||||||
found = MA_TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
/* ID and Name. */
|
||||||
return MA_DOES_NOT_EXIST;
|
ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &deviceInfo);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (deviceType == ma_device_type_playback) {
|
|
||||||
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
||||||
} else {
|
|
||||||
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Default.
|
||||||
|
|
||||||
/*
|
I'm not sure how to do this. I'm going to just assume it's the default device for now. Advice welcome!
|
||||||
Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is
|
*/
|
||||||
reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to
|
deviceInfo.isDefault = MA_TRUE;
|
||||||
retrieve from the AVAudioSession shared instance.
|
|
||||||
*/
|
|
||||||
desc.componentType = kAudioUnitType_Output;
|
|
||||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
|
||||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
|
||||||
desc.componentFlags = 0;
|
|
||||||
desc.componentFlagsMask = 0;
|
|
||||||
|
|
||||||
component = pContextStateCoreAudio->AudioComponentFindNext(NULL, &desc);
|
/* Data Format. */
|
||||||
if (component == NULL) {
|
/*
|
||||||
return MA_FAILED_TO_INIT_BACKEND;
|
Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is
|
||||||
}
|
reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to
|
||||||
|
retrieve from the AVAudioSession shared instance.
|
||||||
|
*/
|
||||||
|
desc.componentType = kAudioUnitType_Output;
|
||||||
|
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||||
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||||
|
desc.componentFlags = 0;
|
||||||
|
desc.componentFlagsMask = 0;
|
||||||
|
|
||||||
status = pContextStateCoreAudio->AudioComponentInstanceNew(component, &audioUnit);
|
component = pContextStateCoreAudio->AudioComponentFindNext(NULL, &desc);
|
||||||
if (status != noErr) {
|
if (component == NULL) {
|
||||||
return ma_result_from_OSStatus(status);
|
return MA_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
|
status = pContextStateCoreAudio->AudioComponentInstanceNew(component, &audioUnit);
|
||||||
formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
|
if (status != noErr) {
|
||||||
|
return MA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
propSize = sizeof(bestFormat);
|
formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
|
||||||
status = pContextStateCoreAudio->AudioUnitGetProperty(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
|
formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
|
||||||
if (status != noErr) {
|
|
||||||
pContextStateCoreAudio->AudioComponentInstanceDispose(audioUnit);
|
|
||||||
return ma_result_from_OSStatus(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
propSize = sizeof(bestFormat);
|
||||||
|
status = pContextStateCoreAudio->AudioUnitGetProperty(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
|
||||||
|
if (status != noErr) {
|
||||||
pContextStateCoreAudio->AudioComponentInstanceDispose(audioUnit);
|
pContextStateCoreAudio->AudioComponentInstanceDispose(audioUnit);
|
||||||
audioUnit = NULL;
|
return MA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Only a single format is being reported for iOS. */
|
pContextStateCoreAudio->AudioComponentInstanceDispose(audioUnit);
|
||||||
pDeviceInfo->nativeDataFormatCount = 1;
|
audioUnit = NULL;
|
||||||
|
|
||||||
result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format);
|
/* Only a single format is being reported for iOS. */
|
||||||
|
deviceInfo.nativeDataFormatCount = 1;
|
||||||
|
|
||||||
|
result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &deviceInfo.nativeDataFormats[0].format);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
return MA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceInfo.nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame;
|
||||||
|
|
||||||
|
/*
|
||||||
|
It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do
|
||||||
|
this we just get the shared instance and inspect.
|
||||||
|
*/
|
||||||
|
@autoreleasepool {
|
||||||
|
AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
|
||||||
|
MA_ASSERT(pAudioSession != NULL);
|
||||||
|
|
||||||
|
deviceInfo.nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback(deviceType, &deviceInfo, pUserData);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
||||||
|
{
|
||||||
|
#if defined(MA_APPLE_DESKTOP)
|
||||||
|
{
|
||||||
|
UInt32 deviceCount;
|
||||||
|
AudioObjectID* pDeviceObjectIDs;
|
||||||
|
AudioObjectID defaultDeviceObjectIDPlayback;
|
||||||
|
AudioObjectID defaultDeviceObjectIDCapture;
|
||||||
|
ma_result result;
|
||||||
|
UInt32 iDevice;
|
||||||
|
|
||||||
|
ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */
|
||||||
|
ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */
|
||||||
|
|
||||||
|
result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
|
||||||
if (result != MA_SUCCESS) {
|
if (result != MA_SUCCESS) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame;
|
for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
|
||||||
|
AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
|
||||||
|
|
||||||
/*
|
if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
|
||||||
It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do
|
ma_bool32 cbResult = ma_context_enumerate_device_by_AudioObjectID__coreaudio(pContext, deviceObjectID, defaultDeviceObjectIDPlayback, ma_device_type_playback, callback, pUserData);
|
||||||
this we just get the shared instance and inspect.
|
if (cbResult == MA_FALSE) {
|
||||||
*/
|
break;
|
||||||
@autoreleasepool {
|
}
|
||||||
AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
|
}
|
||||||
MA_ASSERT(pAudioSession != NULL);
|
|
||||||
|
|
||||||
pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate;
|
if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
|
||||||
|
ma_bool32 cbResult = ma_context_enumerate_device_by_AudioObjectID__coreaudio(pContext, deviceObjectID, defaultDeviceObjectIDCapture, ma_device_type_capture, callback, pUserData);
|
||||||
|
if (cbResult == MA_FALSE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
|
||||||
|
NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
|
||||||
|
|
||||||
|
for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
|
||||||
|
ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
|
||||||
|
|
||||||
|
/* I'm not sure how to check for default devices. I'm just going to assume the first one is the default. */
|
||||||
|
if (pPortDesc == pOutputs[0]) {
|
||||||
|
info.isDefault = MA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!callback(ma_device_type_playback, &info, pUserData)) {
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
|
||||||
|
ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
|
||||||
|
|
||||||
|
/* I'm not sure how to check for default devices. I'm just going to assume the first one is the default. */
|
||||||
|
if (pPortDesc == pInputs[0]) {
|
||||||
|
info.isDefault = MA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!callback(ma_device_type_capture, &info, pUserData)) {
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
(void)pDeviceInfo; /* Unused. */
|
|
||||||
return MA_SUCCESS;
|
return MA_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37134,7 +37104,7 @@ static ma_device_backend_vtable ma_gDeviceBackendVTable_CoreAudio =
|
|||||||
ma_context_init__coreaudio,
|
ma_context_init__coreaudio,
|
||||||
ma_context_uninit__coreaudio,
|
ma_context_uninit__coreaudio,
|
||||||
ma_context_enumerate_devices__coreaudio,
|
ma_context_enumerate_devices__coreaudio,
|
||||||
ma_context_get_device_info__coreaudio,
|
NULL,
|
||||||
ma_device_init__coreaudio,
|
ma_device_init__coreaudio,
|
||||||
ma_device_uninit__coreaudio,
|
ma_device_uninit__coreaudio,
|
||||||
ma_device_start__coreaudio,
|
ma_device_start__coreaudio,
|
||||||
|
|||||||
Reference in New Issue
Block a user