diff options
Diffstat (limited to 'src/external/mini_al.h')
| -rw-r--r-- | src/external/mini_al.h | 354 |
1 files changed, 282 insertions, 72 deletions
diff --git a/src/external/mini_al.h b/src/external/mini_al.h index e8d41239..58cf034d 100644 --- a/src/external/mini_al.h +++ b/src/external/mini_al.h @@ -1,5 +1,5 @@ // Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file. -// mini_al - v0.x - 2018-xx-xx +// mini_al - v0.8 - 2018-07-05 // // David Reid - [email protected] @@ -52,10 +52,11 @@ // The Windows build should compile clean on all popular compilers without the need to configure any include paths // nor link to any libraries. // -// Building for macOS -// ------------------ +// Building for macOS and iOS +// -------------------------- // The macOS build should compile clean without the need to download any dependencies or link to any libraries or -// frameworks. +// frameworks. The iOS build needs to be compiled as Objective-C (sorry) and will need to link the relevant frameworks +// but should Just Work with Xcode. // // Building for Linux // ------------------ @@ -356,6 +357,10 @@ extern "C" { #endif #endif +#if !defined(MAL_HAS_STDINT) && (defined(__GNUC__) || defined(__clang__)) // Assume support for stdint.h on GCC and Clang. + #define MAL_HAS_STDINT +#endif + #ifndef MAL_HAS_STDINT typedef signed char mal_int8; typedef unsigned char mal_uint8; @@ -418,6 +423,13 @@ typedef mal_uint16 wchar_t; #define NULL 0 #endif +#if defined(SIZE_MAX) + #define MAL_SIZE_MAX SIZE_MAX +#else + #define MAL_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */ +#endif + + #ifdef _MSC_VER #define MAL_INLINE __forceinline #else @@ -1241,7 +1253,7 @@ struct mal_context mal_proc AudioObjectGetPropertyDataSize; mal_proc AudioObjectSetPropertyData; - mal_handle hAudioToolbox; + mal_handle hAudioUnit; // Could possibly be set to AudioToolbox on later versions of macOS. mal_proc AudioComponentFindNext; mal_proc AudioComponentInstanceDispose; mal_proc AudioComponentInstanceNew; @@ -3580,8 +3592,8 @@ double mal_timer_get_time_in_seconds(mal_timer* pTimer) struct timespec newTime; clock_gettime(MAL_CLOCK_ID, &newTime); - uint64_t newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; - uint64_t oldTimeCounter = pTimer->counter; + mal_uint64 newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; + mal_uint64 oldTimeCounter = pTimer->counter; return (newTimeCounter - oldTimeCounter) / 1000000000.0; } @@ -8236,7 +8248,7 @@ mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type type, m } // Backend tax. Need to fiddle with this. - float fBackend = 12.0; + float fBackend = 10.0; pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fType*fBackend); } @@ -8774,8 +8786,8 @@ static struct const char* name; float scale; } g_malDefaultBufferSizeScalesALSA[] = { - {"bcm2835 IEC958/HDMI", 8.0f}, - {"bcm2835 ALSA", 8.0f} + {"bcm2835 IEC958/HDMI", 6.0f}, + {"bcm2835 ALSA", 6.0f} }; float mal_find_default_buffer_size_scale__alsa(const char* deviceName) @@ -9967,6 +9979,8 @@ mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, co } + + // Hardware parameters. mal_snd_pcm_hw_params_t* pHWParams = (mal_snd_pcm_hw_params_t*)alloca(((mal_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)()); mal_zero_memory(pHWParams, ((mal_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)()); @@ -12558,23 +12572,31 @@ mal_result mal_device__stop_backend__jack(mal_device* pDevice) // /////////////////////////////////////////////////////////////////////////////// #ifdef MAL_HAS_COREAUDIO -#include <CoreAudio/CoreAudio.h> -#include <AudioToolbox/AudioToolbox.h> - #include <TargetConditionals.h> -#if defined(TARGET_OS_OSX) - #define MAL_APPLE_DESKTOP -#elif defined(TARGET_OS_IPHONE) + +#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1 #define MAL_APPLE_MOBILE +#else + #define MAL_APPLE_DESKTOP #endif +#if defined(MAL_APPLE_DESKTOP) +#include <CoreAudio/CoreAudio.h> +#else +#include <AVFoundation/AVFoundation.h> +#endif + +#include <AudioToolbox/AudioToolbox.h> + // CoreFoundation typedef Boolean (* mal_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding); // CoreAudio +#if defined(MAL_APPLE_DESKTOP) typedef OSStatus (* mal_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData); typedef OSStatus (* mal_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); typedef OSStatus (* mal_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData); +#endif // AudioToolbox typedef AudioComponent (* mal_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc); @@ -12628,7 +12650,8 @@ mal_result mal_result_from_OSStatus(OSStatus status) { switch (status) { - case kAudioHardwareNoError: return MAL_SUCCESS; + case noErr: return MAL_SUCCESS; + #if defined(MAL_APPLE_DESKTOP) case kAudioHardwareNotRunningError: return MAL_DEVICE_NOT_STARTED; case kAudioHardwareUnspecifiedError: return MAL_ERROR; case kAudioHardwareUnknownPropertyError: return MAL_INVALID_ARGS; @@ -12640,11 +12663,13 @@ mal_result mal_result_from_OSStatus(OSStatus status) case kAudioHardwareUnsupportedOperationError: return MAL_INVALID_OPERATION; case kAudioDeviceUnsupportedFormatError: return MAL_FORMAT_NOT_SUPPORTED; case kAudioDevicePermissionsError: return MAL_ACCESS_DENIED; + #endif default: return MAL_ERROR; } } -mal_channel mal_channel_from_AudioChannelBit(AudioChannelBitmap bit) +#if 0 +mal_channel mal_channel_from_AudioChannelBitmap(AudioChannelBitmap bit) { switch (bit) { @@ -12669,6 +12694,7 @@ mal_channel mal_channel_from_AudioChannelBit(AudioChannelBitmap bit) default: return MAL_CHANNEL_NONE; } } +#endif mal_channel mal_channel_from_AudioChannelLabel(AudioChannelLabel label) { @@ -12738,6 +12764,8 @@ mal_channel mal_channel_from_AudioChannelLabel(AudioChannelLabel label) case kAudioChannelLabel_Discrete_14: return MAL_CHANNEL_AUX_14; case kAudioChannelLabel_Discrete_15: return MAL_CHANNEL_AUX_15; case kAudioChannelLabel_Discrete_65535: return MAL_CHANNEL_NONE; + + #if 0 // Introduced in a later version of macOS. case kAudioChannelLabel_HOA_ACN: return MAL_CHANNEL_NONE; case kAudioChannelLabel_HOA_ACN_0: return MAL_CHANNEL_AUX_0; case kAudioChannelLabel_HOA_ACN_1: return MAL_CHANNEL_AUX_1; @@ -12756,6 +12784,8 @@ mal_channel mal_channel_from_AudioChannelLabel(AudioChannelLabel label) case kAudioChannelLabel_HOA_ACN_14: return MAL_CHANNEL_AUX_14; case kAudioChannelLabel_HOA_ACN_15: return MAL_CHANNEL_AUX_15; case kAudioChannelLabel_HOA_ACN_65024: return MAL_CHANNEL_NONE; + #endif + default: return MAL_CHANNEL_NONE; } } @@ -12825,6 +12855,7 @@ mal_result mal_format_from_AudioStreamBasicDescription(const AudioStreamBasicDes return MAL_FORMAT_NOT_SUPPORTED; } +#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_assert(pContext != NULL); @@ -13072,7 +13103,9 @@ mal_result mal_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChan for (UInt32 iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions; ++iChannel) { channelMap[iChannel] = mal_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel); } - } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { + } 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; @@ -13082,7 +13115,9 @@ mal_result mal_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChan channelMap[iChannel++] = mal_channel_from_AudioChannelBit(bit); } } - } else { + } 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); @@ -13567,7 +13602,7 @@ mal_result mal_device_find_best_format__coreaudio(const mal_device* pDevice, Aud *pFormat = bestDeviceFormatSoFar; return MAL_SUCCESS; } - +#endif @@ -13586,6 +13621,7 @@ mal_result mal_context_enumerate_devices__coreaudio(mal_context* pContext, mal_e mal_assert(pContext != NULL); mal_assert(callback != NULL); +#if defined(MAL_APPLE_DESKTOP) UInt32 deviceCount; AudioObjectID* pDeviceObjectIDs; mal_result result = mal_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); @@ -13618,6 +13654,23 @@ mal_result mal_context_enumerate_devices__coreaudio(mal_context* pContext, mal_e } mal_free(pDeviceObjectIDs); +#else + // Only supporting default devices on non-Desktop platforms. + mal_device_info info; + + mal_zero_object(&info); + mal_strncpy_s(info.name, sizeof(info.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + if (!callback(pContext, mal_device_type_playback, &info, pUserData)) { + return MAL_SUCCESS; + } + + mal_zero_object(&info); + mal_strncpy_s(info.name, sizeof(info.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + if (!callback(pContext, mal_device_type_capture, &info, pUserData)) { + return MAL_SUCCESS; + } +#endif + return MAL_SUCCESS; } @@ -13627,6 +13680,9 @@ mal_result mal_context_get_device_info__coreaudio(mal_context* pContext, mal_dev (void)shareMode; (void)pDeviceInfo; +#if defined(MAL_APPLE_DESKTOP) + // Desktop + // ======= AudioObjectID deviceObjectID; mal_result result = mal_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); if (result != MAL_SUCCESS) { @@ -13705,6 +13761,70 @@ mal_result mal_context_get_device_info__coreaudio(mal_context* pContext, mal_dev } } } +#else + // Mobile + // ====== + if (deviceType == mal_device_type_playback) { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + // 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. + AudioComponentDescription desc; + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_RemoteIO; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + AudioComponent component = ((mal_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); + if (component == NULL) { + return MAL_FAILED_TO_INIT_BACKEND; + } + + AudioUnit audioUnit; + OSStatus status = ((mal_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit); + if (status != noErr) { + return mal_result_from_OSStatus(status); + } + + AudioUnitScope formatScope = (deviceType == mal_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; + AudioUnitElement formatElement = (deviceType == mal_device_type_playback) ? MAL_COREAUDIO_OUTPUT_BUS : MAL_COREAUDIO_INPUT_BUS; + + AudioStreamBasicDescription bestFormat; + UInt32 propSize = sizeof(bestFormat); + status = ((mal_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); + return mal_result_from_OSStatus(status); + } + + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); + audioUnit = NULL; + + + pDeviceInfo->minChannels = bestFormat.mChannelsPerFrame; + pDeviceInfo->maxChannels = bestFormat.mChannelsPerFrame; + + pDeviceInfo->formatCount = 1; + mal_result result = mal_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->formats[0]); + if (result != MAL_SUCCESS) { + return result; + } + + // 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]; + mal_assert(pAudioSession != NULL); + + pDeviceInfo->minSampleRate = (mal_uint32)pAudioSession.sampleRate; + pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate; + } +#endif return MAL_SUCCESS; } @@ -13713,7 +13833,7 @@ mal_result mal_context_init__coreaudio(mal_context* pContext) { mal_assert(pContext != NULL); -#ifndef MAL_NO_RUNTIME_LINKING +#if !defined(MAL_NO_RUNTIME_LINKING) && !defined(MAL_APPLE_MOBILE) pContext->coreaudio.hCoreFoundation = mal_dlopen("CoreFoundation.framework/CoreFoundation"); if (pContext->coreaudio.hCoreFoundation == NULL) { return MAL_API_NOT_FOUND; @@ -13733,40 +13853,57 @@ mal_result mal_context_init__coreaudio(mal_context* pContext) pContext->coreaudio.AudioObjectSetPropertyData = mal_dlsym(pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); - pContext->coreaudio.hAudioToolbox = mal_dlopen("AudioToolbox.framework/AudioToolbox"); - if (pContext->coreaudio.hAudioToolbox == NULL) { + // It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still + // defined in AudioUnit, but just in case they decided to remove them from there entirely I'm going to do implement a fallback. + // The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to + // AudioToolbox. + pContext->coreaudio.hAudioUnit = mal_dlopen("AudioUnit.framework/AudioUnit"); + if (pContext->coreaudio.hAudioUnit == NULL) { mal_dlclose(pContext->coreaudio.hCoreAudio); mal_dlclose(pContext->coreaudio.hCoreFoundation); return MAL_API_NOT_FOUND; } - pContext->coreaudio.AudioComponentFindNext = mal_dlsym(pContext->coreaudio.hAudioToolbox, "AudioComponentFindNext"); - pContext->coreaudio.AudioComponentInstanceDispose = mal_dlsym(pContext->coreaudio.hAudioToolbox, "AudioComponentInstanceDispose"); - pContext->coreaudio.AudioComponentInstanceNew = mal_dlsym(pContext->coreaudio.hAudioToolbox, "AudioComponentInstanceNew"); - pContext->coreaudio.AudioOutputUnitStart = mal_dlsym(pContext->coreaudio.hAudioToolbox, "AudioOutputUnitStart"); - pContext->coreaudio.AudioOutputUnitStop = mal_dlsym(pContext->coreaudio.hAudioToolbox, "AudioOutputUnitStop"); - pContext->coreaudio.AudioUnitAddPropertyListener = mal_dlsym(pContext->coreaudio.hAudioToolbox, "AudioUnitAddPropertyListener"); - pContext->coreaudio.AudioUnitGetProperty = mal_dlsym(pContext->coreaudio.hAudioToolbox, "AudioUnitGetProperty"); - pContext->coreaudio.AudioUnitSetProperty = mal_dlsym(pContext->coreaudio.hAudioToolbox, "AudioUnitSetProperty"); - pContext->coreaudio.AudioUnitInitialize = mal_dlsym(pContext->coreaudio.hAudioToolbox, "AudioUnitInitialize"); - pContext->coreaudio.AudioUnitRender = mal_dlsym(pContext->coreaudio.hAudioToolbox, "AudioUnitRender"); + if (mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { + // Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. + mal_dlclose(pContext->coreaudio.hAudioUnit); + pContext->coreaudio.hAudioUnit = mal_dlopen("AudioToolbox.framework/AudioToolbox"); + if (pContext->coreaudio.hAudioUnit == NULL) { + mal_dlclose(pContext->coreaudio.hCoreAudio); + mal_dlclose(pContext->coreaudio.hCoreFoundation); + return MAL_API_NOT_FOUND; + } + } + + pContext->coreaudio.AudioComponentFindNext = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); + pContext->coreaudio.AudioComponentInstanceDispose = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); + pContext->coreaudio.AudioComponentInstanceNew = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); + pContext->coreaudio.AudioOutputUnitStart = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); + pContext->coreaudio.AudioOutputUnitStop = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); + pContext->coreaudio.AudioUnitAddPropertyListener = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); + pContext->coreaudio.AudioUnitGetProperty = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); + pContext->coreaudio.AudioUnitSetProperty = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); + pContext->coreaudio.AudioUnitInitialize = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); + pContext->coreaudio.AudioUnitRender = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitRender"); #else - pContext->coreaudio.CFStringGetCString = CFStringGetCString; + pContext->coreaudio.CFStringGetCString = (mal_proc)CFStringGetCString; - pContext->coreaudio.AudioObjectGetPropertyData = AudioObjectGetPropertyData; - pContext->coreaudio.AudioObjectGetPropertyDataSize = AudioObjectGetPropertyDataSize; - pContext->coreaudio.AudioObjectSetPropertyData = AudioObjectSetPropertyData; + #if defined(MAL_APPLE_DESKTOP) + pContext->coreaudio.AudioObjectGetPropertyData = (mal_proc)AudioObjectGetPropertyData; + pContext->coreaudio.AudioObjectGetPropertyDataSize = (mal_proc)AudioObjectGetPropertyDataSize; + pContext->coreaudio.AudioObjectSetPropertyData = (mal_proc)AudioObjectSetPropertyData; + #endif - pContext->coreaudio.AudioComponentFindNext = AudioComponentFindNext; - pContext->coreaudio.AudioComponentInstanceDispose = AudioComponentInstanceDispose; - pContext->coreaudio.AudioComponentInstanceNew = AudioComponentInstanceNew; - pContext->coreaudio.AudioOutputUnitStart = AudioOutputUnitStart; - pContext->coreaudio.AudioOutputUnitStop = AudioOutputUnitStop; - pContext->coreaudio.AudioUnitAddPropertyListener = AudioUnitAddPropertyListener; - pContext->coreaudio.AudioUnitGetProperty = AudioUnitGetProperty; - pContext->coreaudio.AudioUnitSetProperty = AudioUnitSetProperty; - pContext->coreaudio.AudioUnitInitialize = AudioUnitInitialize; - pContext->coreaudio.AudioUnitRender = AudioUnitRender; + pContext->coreaudio.AudioComponentFindNext = (mal_proc)AudioComponentFindNext; + pContext->coreaudio.AudioComponentInstanceDispose = (mal_proc)AudioComponentInstanceDispose; + pContext->coreaudio.AudioComponentInstanceNew = (mal_proc)AudioComponentInstanceNew; + pContext->coreaudio.AudioOutputUnitStart = (mal_proc)AudioOutputUnitStart; + pContext->coreaudio.AudioOutputUnitStop = (mal_proc)AudioOutputUnitStop; + pContext->coreaudio.AudioUnitAddPropertyListener = (mal_proc)AudioUnitAddPropertyListener; + pContext->coreaudio.AudioUnitGetProperty = (mal_proc)AudioUnitGetProperty; + pContext->coreaudio.AudioUnitSetProperty = (mal_proc)AudioUnitSetProperty; + pContext->coreaudio.AudioUnitInitialize = (mal_proc)AudioUnitInitialize; + pContext->coreaudio.AudioUnitRender = (mal_proc)AudioUnitRender; #endif pContext->onDeviceIDEqual = mal_context_is_device_id_equal__coreaudio; @@ -13781,8 +13918,8 @@ mal_result mal_context_uninit__coreaudio(mal_context* pContext) mal_assert(pContext != NULL); mal_assert(pContext->backend == mal_backend_coreaudio); -#ifndef MAL_NO_RUNTIME_LINKING - mal_dlclose(pContext->coreaudio.hAudioToolbox); +#if !defined(MAL_NO_RUNTIME_LINKING) && !defined(MAL_APPLE_MOBILE) + mal_dlclose(pContext->coreaudio.hAudioUnit); mal_dlclose(pContext->coreaudio.hCoreAudio); mal_dlclose(pContext->coreaudio.hCoreFoundation); #endif @@ -13902,13 +14039,17 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev mal_assert(pDevice != NULL); mal_assert(deviceType == mal_device_type_playback || deviceType == mal_device_type_capture); + mal_result result; + +#if defined(MAL_APPLE_DESKTOP) AudioObjectID deviceObjectID; - mal_result result = mal_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); + result = mal_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); if (result != MAL_SUCCESS) { return result; } pDevice->coreaudio.deviceObjectID = deviceObjectID; +#endif // Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. if (pDevice->periods < 1) { @@ -13922,7 +14063,7 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev // Audio component. AudioComponentDescription desc; desc.componentType = kAudioUnitType_Output; -#if defined(TARGET_OS_OSX) +#if defined(MAL_APPLE_DESKTOP) desc.componentSubType = kAudioUnitSubType_HALOutput; #else desc.componentSubType = kAudioUnitSubType_RemoteIO; @@ -13964,13 +14105,14 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev } - // Set the device to use with this audio unit. + // Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. +#if defined(MAL_APPLE_DESKTOP) status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)((AudioUnit)pDevice->coreaudio.audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, (deviceType == mal_device_type_playback) ? MAL_COREAUDIO_OUTPUT_BUS : MAL_COREAUDIO_INPUT_BUS, &deviceObjectID, sizeof(AudioDeviceID)); if (status != noErr) { ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); return mal_result_from_OSStatus(result); } - +#endif // Format. This is the hardest part of initialization because there's a few variables to take into account. // 1) The format must be supported by the device. @@ -13980,11 +14122,14 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev // Ideally we would like to use a format that's as close to the hardware as possible so we can get as close to a passthrough as possible. The // most important property is the sample rate. mini_al can do format conversion for any sample rate and channel count, but cannot do the same // for the sample data format. If the sample data format is not supported by mini_al it must be ignored completely. + // + // On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to. { AudioUnitScope formatScope = (deviceType == mal_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; AudioUnitElement formatElement = (deviceType == mal_device_type_playback) ? MAL_COREAUDIO_OUTPUT_BUS : MAL_COREAUDIO_INPUT_BUS; AudioStreamBasicDescription bestFormat; + #if defined(MAL_APPLE_DESKTOP) result = mal_device_find_best_format__coreaudio(pDevice, &bestFormat); if (result != MAL_SUCCESS) { ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); @@ -14001,6 +14146,32 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev return mal_result_from_OSStatus(status); } } + #else + UInt32 propSize = sizeof(bestFormat); + status = ((mal_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)((AudioUnit)pDevice->coreaudio.audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + return mal_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 + // setting the sample rate to what the user has requested and then just see the results of it. Need to use some Objective-C here for this since + // it depends on Apple's AVAudioSession API. To do this we just get the shared AVAudioSession instance and then set it. Note that from what I + // can tell, it looks like the sample rate is shared between playback and capture for everything. + @autoreleasepool { + AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; + mal_assert(pAudioSession != NULL); + + [pAudioSession setPreferredSampleRate:(double)pDevice->sampleRate error:nil]; + bestFormat.mSampleRate = pAudioSession.sampleRate; + } + + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)((AudioUnit)pDevice->coreaudio.audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + return mal_result_from_OSStatus(status); + } + #endif result = mal_format_from_AudioStreamBasicDescription(&bestFormat, &pDevice->internalFormat); if (result != MAL_SUCCESS || pDevice->internalFormat == mal_format_unknown) { @@ -14008,23 +14179,30 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev return result; } - pDevice->channels = bestFormat.mChannelsPerFrame; - pDevice->sampleRate = bestFormat.mSampleRate; + pDevice->internalChannels = bestFormat.mChannelsPerFrame; + pDevice->internalSampleRate = bestFormat.mSampleRate; } + // Internal channel map. +#if defined(MAL_APPLE_DESKTOP) result = mal_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pDevice->internalChannelMap); if (result != MAL_SUCCESS) { return result; } +#else + // TODO: Figure out how to get the channel map using AVAudioSession. + mal_get_standard_channel_map(mal_standard_channel_map_default, pDevice->internalChannels, pDevice->internalChannelMap); +#endif - // Buffer size. + // Buffer size. Not allowing this to be configurable on iOS. mal_uint32 actualBufferSizeInFrames = pDevice->bufferSizeInFrames; if (actualBufferSizeInFrames < pDevice->periods) { actualBufferSizeInFrames = pDevice->periods; } +#if defined(MAL_APPLE_DESKTOP) if (pDevice->usingDefaultBufferSize) { // CPU speed is a factor to consider when determine how large of a buffer we need. float fCPUSpeed = mal_calculate_cpu_speed_factor(); @@ -14053,7 +14231,10 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev if (result != MAL_SUCCESS) { return result; } - +#else + actualBufferSizeInFrames = 4096; +#endif + pDevice->bufferSizeInFrames = actualBufferSizeInFrames * pDevice->periods; @@ -19000,13 +19181,13 @@ mal_bool32 mal_channel_map_contains_channel_position(mal_uint32 channels, const void mal_copy_memory_64(void* dst, const void* src, mal_uint64 sizeInBytes) { -#if 0xFFFFFFFFFFFFFFFF <= SIZE_MAX +#if 0xFFFFFFFFFFFFFFFF <= MAL_SIZE_MAX mal_copy_memory(dst, src, (size_t)sizeInBytes); #else while (sizeInBytes > 0) { mal_uint64 bytesToCopyNow = sizeInBytes; - if (bytesToCopyNow > SIZE_MAX) { - bytesToCopyNow = SIZE_MAX; + if (bytesToCopyNow > MAL_SIZE_MAX) { + bytesToCopyNow = MAL_SIZE_MAX; } mal_copy_memory(dst, src, (size_t)bytesToCopyNow); // Safe cast to size_t. @@ -19020,13 +19201,13 @@ void mal_copy_memory_64(void* dst, const void* src, mal_uint64 sizeInBytes) void mal_zero_memory_64(void* dst, mal_uint64 sizeInBytes) { -#if 0xFFFFFFFFFFFFFFFF <= SIZE_MAX +#if 0xFFFFFFFFFFFFFFFF <= MAL_SIZE_MAX mal_zero_memory(dst, (size_t)sizeInBytes); #else while (sizeInBytes > 0) { mal_uint64 bytesToZeroNow = sizeInBytes; - if (bytesToZeroNow > SIZE_MAX) { - bytesToZeroNow = SIZE_MAX; + if (bytesToZeroNow > MAL_SIZE_MAX) { + bytesToZeroNow = MAL_SIZE_MAX; } mal_zero_memory(dst, (size_t)bytesToZeroNow); // Safe cast to size_t. @@ -20486,6 +20667,12 @@ void mal_pcm_f32_to_s16__optimized(void* dst, const void* src, mal_uint64 count, #if defined(MAL_SUPPORT_SSE2) void mal_pcm_f32_to_s16__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) { + // Both the input and output buffers need to be aligned to 16 bytes. + if ((((mal_uintptr)dst & 15) != 0) || (((mal_uintptr)src & 15) != 0)) { + mal_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); + return; + } + mal_int16* dst_s16 = (mal_int16*)dst; const float* src_f32 = (const float*)src; @@ -20563,6 +20750,12 @@ void mal_pcm_f32_to_s16__sse2(void* dst, const void* src, mal_uint64 count, mal_ #if defined(MAL_SUPPORT_AVX2) void mal_pcm_f32_to_s16__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) { + // Both the input and output buffers need to be aligned to 32 bytes. + if ((((mal_uintptr)dst & 31) != 0) || (((mal_uintptr)src & 31) != 0)) { + mal_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); + return; + } + mal_int16* dst_s16 = (mal_int16*)dst; const float* src_f32 = (const float*)src; @@ -20670,6 +20863,12 @@ void mal_pcm_f32_to_s16__avx512(void* dst, const void* src, mal_uint64 count, ma #if defined(MAL_SUPPORT_NEON) void mal_pcm_f32_to_s16__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) { + // Both the input and output buffers need to be aligned to 16 bytes. + if ((((mal_uintptr)dst & 15) != 0) || (((mal_uintptr)src & 15) != 0)) { + mal_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); + return; + } + mal_int16* dst_s16 = (mal_int16*)dst; const float* src_f32 = (const float*)src; @@ -23046,7 +23245,7 @@ mal_uint64 mal_src_read_deinterleaved__sinc(mal_src* pSRC, mal_uint64 frameCount } #endif mal_int32 windowWidthSIMD2 = windowWidthSIMD*2; - + (void)windowWidthSIMD2; // <-- Silence a warning when SIMD is disabled. float* ppNextSamplesOut[MAL_MAX_CHANNELS]; mal_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(void*) * pSRC->config.channels); @@ -24039,7 +24238,7 @@ float mal_calculate_cpu_speed_factor() // Our profiling test is based on how quick it can process 1 second worth of samples through mini_al's data conversion pipeline. // This factor is multiplied with the profiling time. May need to fiddle with this to get an accurate value. - float f = 1000; + double f = 1000; // Experiment: Reduce the factor a little when debug mode is used to reduce a blowout. #if !defined(NDEBUG) || defined(_DEBUG) @@ -24110,9 +24309,11 @@ float mal_calculate_cpu_speed_factor() double executionTimeInSeconds = mal_timer_get_time_in_seconds(&timer) - startTime; executionTimeInSeconds /= iterationCount; - + mal_free(pData); - return (float)(executionTimeInSeconds * f); + + // Guard against extreme blowouts. + return (float)mal_clamp(executionTimeInSeconds * f, 0.1, 100.0); } mal_uint32 mal_scale_buffer_size(mal_uint32 baseBufferSize, float scale) @@ -24122,11 +24323,20 @@ mal_uint32 mal_scale_buffer_size(mal_uint32 baseBufferSize, float scale) mal_uint32 mal_calculate_default_buffer_size_in_frames(mal_performance_profile performanceProfile, mal_uint32 sampleRate, float scale) { + mal_uint32 baseLatency; if (performanceProfile == mal_performance_profile_low_latency) { - return mal_scale_buffer_size((sampleRate/1000) * MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY, scale); + baseLatency = MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY; } else { - return mal_scale_buffer_size((sampleRate/1000) * MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE, scale); + baseLatency = MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE; } + + mal_uint32 sampleRateMS = (sampleRate/1000); + + mal_uint32 minBufferSize = sampleRateMS * mal_min(baseLatency / 5, 1); // <-- Guard against multiply by zero. + mal_uint32 maxBufferSize = sampleRateMS * (baseLatency * 40); + + mal_uint32 bufferSize = mal_scale_buffer_size((sampleRate/1000) * baseLatency, scale); + return mal_clamp(bufferSize, minBufferSize, maxBufferSize); } @@ -25449,7 +25659,7 @@ mal_result mal_decoder__full_decode_and_uninit(mal_decoder* pDecoder, mal_decode newDataCapInFrames = 4096; } - if ((newDataCapInFrames * bpf) > SIZE_MAX) { + if ((newDataCapInFrames * bpf) > MAL_SIZE_MAX) { mal_free(pDataOut); return MAL_TOO_LARGE; } @@ -25612,7 +25822,7 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSineWave, mal_uint64 count, float* // REVISION HISTORY // ================ // -// v0.x - 2018-xx-xx +// v0.8 - 2018-07-05 // - Changed MAL_IMPLEMENTATION to MINI_AL_IMPLEMENTATION for consistency with other libraries. The old // way is still supported for now, but you should update as it may be removed in the future. // - API CHANGE: Replace device enumeration APIs. mal_enumerate_devices() has been replaced with |
