summaryrefslogtreecommitdiffhomepage
path: root/src/external/mini_al.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/external/mini_al.h')
-rw-r--r--src/external/mini_al.h354
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