Commit c3fcb643 authored by David Reid's avatar David Reid

Core Audio: Fix some issues with channel maps.

parent 20820048
...@@ -13939,6 +13939,80 @@ mal_result mal_format_from_AudioStreamBasicDescription(const AudioStreamBasicDes ...@@ -13939,6 +13939,80 @@ mal_result mal_format_from_AudioStreamBasicDescription(const AudioStreamBasicDes
return MAL_FORMAT_NOT_SUPPORTED; return MAL_FORMAT_NOT_SUPPORTED;
} }
mal_result mal_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, mal_channel channelMap[MAL_MAX_CHANNELS])
{
mal_assert(pChannelLayout != NULL);
if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
for (UInt32 iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions; ++iChannel) {
channelMap[iChannel] = mal_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);
}
} else
#if 0
if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
// This is the same kind of system that's used by Windows audio APIs.
UInt32 iChannel = 0;
AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap;
for (UInt32 iBit = 0; iBit < 32; ++iBit) {
AudioChannelBitmap bit = bitmap & (1 << iBit);
if (bit != 0) {
channelMap[iChannel++] = mal_channel_from_AudioChannelBit(bit);
}
}
} else
#endif
{
// Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should
// be updated to determine the mapping based on the tag.
UInt32 channelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
switch (pChannelLayout->mChannelLayoutTag)
{
case kAudioChannelLayoutTag_Mono:
case kAudioChannelLayoutTag_Stereo:
case kAudioChannelLayoutTag_StereoHeadphones:
case kAudioChannelLayoutTag_MatrixStereo:
case kAudioChannelLayoutTag_MidSide:
case kAudioChannelLayoutTag_XY:
case kAudioChannelLayoutTag_Binaural:
case kAudioChannelLayoutTag_Ambisonic_B_Format:
{
mal_get_standard_channel_map(mal_standard_channel_map_default, channelCount, channelMap);
} break;
case kAudioChannelLayoutTag_Octagonal:
{
channelMap[7] = MAL_CHANNEL_SIDE_RIGHT;
channelMap[6] = MAL_CHANNEL_SIDE_LEFT;
} // Intentional fallthrough.
case kAudioChannelLayoutTag_Hexagonal:
{
channelMap[5] = MAL_CHANNEL_BACK_CENTER;
} // Intentional fallthrough.
case kAudioChannelLayoutTag_Pentagonal:
{
channelMap[4] = MAL_CHANNEL_FRONT_CENTER;
} // Intentional fallghrough.
case kAudioChannelLayoutTag_Quadraphonic:
{
channelMap[3] = MAL_CHANNEL_BACK_RIGHT;
channelMap[2] = MAL_CHANNEL_BACK_LEFT;
channelMap[1] = MAL_CHANNEL_RIGHT;
channelMap[0] = MAL_CHANNEL_LEFT;
} break;
// TODO: Add support for more tags here.
default:
{
mal_get_standard_channel_map(mal_standard_channel_map_default, channelCount, channelMap);
} break;
}
}
return MAL_SUCCESS;
}
#if defined(MAL_APPLE_DESKTOP) #if defined(MAL_APPLE_DESKTOP)
mal_result mal_get_device_object_ids__coreaudio(mal_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) // NOTE: Free the returned buffer with mal_free(). mal_result mal_get_device_object_ids__coreaudio(mal_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) // NOTE: Free the returned buffer with mal_free().
{ {
...@@ -14178,79 +14252,6 @@ mal_result mal_get_AudioObject_channel_count(mal_context* pContext, AudioObjectI ...@@ -14178,79 +14252,6 @@ mal_result mal_get_AudioObject_channel_count(mal_context* pContext, AudioObjectI
return MAL_SUCCESS; return MAL_SUCCESS;
} }
mal_result mal_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, mal_channel channelMap[MAL_MAX_CHANNELS])
{
mal_assert(pChannelLayout != NULL);
if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
for (UInt32 iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions; ++iChannel) {
channelMap[iChannel] = mal_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);
}
} else
#if 0
if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
// This is the same kind of system that's used by Windows audio APIs.
UInt32 iChannel = 0;
AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap;
for (UInt32 iBit = 0; iBit < 32; ++iBit) {
AudioChannelBitmap bit = bitmap & (1 << iBit);
if (bit != 0) {
channelMap[iChannel++] = mal_channel_from_AudioChannelBit(bit);
}
}
} else
#endif
{
// Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should
// be updated to determine the mapping based on the tag.
UInt32 channelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
switch (pChannelLayout->mChannelLayoutTag)
{
case kAudioChannelLayoutTag_Mono:
case kAudioChannelLayoutTag_Stereo:
case kAudioChannelLayoutTag_StereoHeadphones:
case kAudioChannelLayoutTag_MatrixStereo:
case kAudioChannelLayoutTag_MidSide:
case kAudioChannelLayoutTag_XY:
case kAudioChannelLayoutTag_Binaural:
case kAudioChannelLayoutTag_Ambisonic_B_Format:
{
mal_get_standard_channel_map(mal_standard_channel_map_default, channelCount, channelMap);
} break;
case kAudioChannelLayoutTag_Octagonal:
{
channelMap[7] = MAL_CHANNEL_SIDE_RIGHT;
channelMap[6] = MAL_CHANNEL_SIDE_LEFT;
} // Intentional fallthrough.
case kAudioChannelLayoutTag_Hexagonal:
{
channelMap[5] = MAL_CHANNEL_BACK_CENTER;
} // Intentional fallthrough.
case kAudioChannelLayoutTag_Pentagonal:
{
channelMap[4] = MAL_CHANNEL_FRONT_CENTER;
} // Intentional fallghrough.
case kAudioChannelLayoutTag_Quadraphonic:
{
channelMap[3] = MAL_CHANNEL_BACK_RIGHT;
channelMap[2] = MAL_CHANNEL_BACK_LEFT;
channelMap[1] = MAL_CHANNEL_RIGHT;
channelMap[0] = MAL_CHANNEL_LEFT;
} break;
// TODO: Add support for more tags here.
default:
{
mal_get_standard_channel_map(mal_standard_channel_map_default, channelCount, channelMap);
} break;
}
}
return MAL_SUCCESS;
}
mal_result mal_get_AudioObject_channel_map(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, mal_channel channelMap[MAL_MAX_CHANNELS]) mal_result mal_get_AudioObject_channel_map(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, mal_channel channelMap[MAL_MAX_CHANNELS])
{ {
mal_assert(pContext != NULL); mal_assert(pContext != NULL);
...@@ -14271,37 +14272,6 @@ mal_result mal_get_AudioObject_channel_map(mal_context* pContext, AudioObjectID ...@@ -14271,37 +14272,6 @@ mal_result mal_get_AudioObject_channel_map(mal_context* pContext, AudioObjectID
return result; return result;
} }
mal_result mal_get_AudioUnit_channel_map(mal_context* pContext, AudioUnit audioUnit, mal_device_type deviceType, mal_channel channelMap[MAL_MAX_CHANNELS])
{
mal_assert(pContext != NULL);
UInt32 channelLayoutSize;
OSStatus status = ((mal_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Output, MAL_COREAUDIO_OUTPUT_BUS, &channelLayoutSize, NULL);
if (status != noErr) {
return mal_result_from_OSStatus(status);
}
AudioChannelLayout* pChannelLayout = (AudioChannelLayout*)mal_malloc(channelLayoutSize);
if (pChannelLayout == NULL) {
return MAL_OUT_OF_MEMORY;
}
status = ((mal_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Output, MAL_COREAUDIO_OUTPUT_BUS, pChannelLayout, &channelLayoutSize);
if (status != noErr) {
mal_free(pChannelLayout);
return mal_result_from_OSStatus(status);
}
mal_result result = mal_get_channel_map_from_AudioChannelLayout(pChannelLayout, channelMap);
if (result != MAL_SUCCESS) {
mal_free(pChannelLayout);
return result;
}
mal_free(pChannelLayout);
return MAL_SUCCESS;
}
mal_result mal_get_AudioObject_sample_rates(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges) // NOTE: Free the returned pointer with mal_free(). mal_result mal_get_AudioObject_sample_rates(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges) // NOTE: Free the returned pointer with mal_free().
{ {
mal_assert(pContext != NULL); mal_assert(pContext != NULL);
...@@ -14716,7 +14686,46 @@ mal_result mal_find_best_format__coreaudio(mal_context* pContext, AudioObjectID ...@@ -14716,7 +14686,46 @@ mal_result mal_find_best_format__coreaudio(mal_context* pContext, AudioObjectID
} }
#endif #endif
mal_result mal_get_AudioUnit_channel_map(mal_context* pContext, AudioUnit audioUnit, mal_device_type deviceType, mal_channel channelMap[MAL_MAX_CHANNELS])
{
mal_assert(pContext != NULL);
AudioUnitScope deviceScope;
AudioUnitElement deviceBus;
if (deviceType == mal_device_type_playback) {
deviceScope = kAudioUnitScope_Output;
deviceBus = MAL_COREAUDIO_OUTPUT_BUS;
} else {
deviceScope = kAudioUnitScope_Input;
deviceBus = MAL_COREAUDIO_INPUT_BUS;
}
UInt32 channelLayoutSize;
OSStatus status = ((mal_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL);
if (status != noErr) {
return mal_result_from_OSStatus(status);
}
AudioChannelLayout* pChannelLayout = (AudioChannelLayout*)mal_malloc(channelLayoutSize);
if (pChannelLayout == NULL) {
return MAL_OUT_OF_MEMORY;
}
status = ((mal_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize);
if (status != noErr) {
mal_free(pChannelLayout);
return mal_result_from_OSStatus(status);
}
mal_result result = mal_get_channel_map_from_AudioChannelLayout(pChannelLayout, channelMap);
if (result != MAL_SUCCESS) {
mal_free(pChannelLayout);
return result;
}
mal_free(pChannelLayout);
return MAL_SUCCESS;
}
mal_bool32 mal_context_is_device_id_equal__coreaudio(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) mal_bool32 mal_context_is_device_id_equal__coreaudio(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1)
{ {
...@@ -15425,12 +15434,25 @@ mal_result mal_device_init_internal__coreaudio(mal_context* pContext, mal_device ...@@ -15425,12 +15434,25 @@ mal_result mal_device_init_internal__coreaudio(mal_context* pContext, mal_device
} }
// Internal channel map. // Internal channel map. This is weird in my testing. If I use the AudioObject to get the
// channel map, the channel descriptions are set to "Unknown" for some reason. To work around
// this it looks like retrieving it from the AudioUnit will work. However, and this is where
// it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore
// I'm going to fall back to a default assumption in these cases.
#if defined(MAL_APPLE_DESKTOP) #if defined(MAL_APPLE_DESKTOP)
result = mal_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut); result = mal_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut);
if (result != MAL_SUCCESS) {
#if 0
// Try falling back to the channel map from the AudioObject.
result = mal_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut);
if (result != MAL_SUCCESS) { if (result != MAL_SUCCESS) {
return result; return result;
} }
#else
// Fall back to default assumptions.
mal_get_standard_channel_map(mal_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
#endif
}
#else #else
// TODO: Figure out how to get the channel map using AVAudioSession. // TODO: Figure out how to get the channel map using AVAudioSession.
mal_get_standard_channel_map(mal_standard_channel_map_default, pData->channelsOut, pData->channelMapOut); mal_get_standard_channel_map(mal_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
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