mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
Core Audio: Fix some issues with channel maps.
This commit is contained in:
@@ -13939,6 +13939,80 @@ mal_result mal_format_from_AudioStreamBasicDescription(const AudioStreamBasicDes
|
||||
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)
|
||||
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
|
||||
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_assert(pContext != NULL);
|
||||
@@ -14271,37 +14272,6 @@ mal_result mal_get_AudioObject_channel_map(mal_context* pContext, AudioObjectID
|
||||
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_assert(pContext != NULL);
|
||||
@@ -14716,7 +14686,46 @@ mal_result mal_find_best_format__coreaudio(mal_context* pContext, AudioObjectID
|
||||
}
|
||||
#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)
|
||||
{
|
||||
@@ -15425,11 +15434,24 @@ 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)
|
||||
result = mal_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut);
|
||||
if (result != MAL_SUCCESS) {
|
||||
return result;
|
||||
#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) {
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
// Fall back to default assumptions.
|
||||
mal_get_standard_channel_map(mal_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
// TODO: Figure out how to get the channel map using AVAudioSession.
|
||||
|
||||
Reference in New Issue
Block a user