mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-23 08:44:04 +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;
|
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
|
|||||||
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
|
|||||||
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
|
|||||||
}
|
}
|
||||||
#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,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)
|
#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 (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
|
#else
|
||||||
// TODO: Figure out how to get the channel map using AVAudioSession.
|
// TODO: Figure out how to get the channel map using AVAudioSession.
|
||||||
|
|||||||
Reference in New Issue
Block a user