diff --git a/src/external/mini_al.c b/src/external/mini_al.c
index f28055130..d52789a7d 100644
--- a/src/external/mini_al.c
+++ b/src/external/mini_al.c
@@ -3,5 +3,6 @@
#define MINI_AL_IMPLEMENTATION
#define MAL_NO_JACK
#define MAL_NO_OPENAL
+#define MAL_NO_SDL
//#define MAL_NO_NULL
#include "mini_al.h"
diff --git a/src/external/mini_al.h b/src/external/mini_al.h
index 384ece565..e892b031b 100644
--- a/src/external/mini_al.h
+++ b/src/external/mini_al.h
@@ -1,16 +1,12 @@
// Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file.
-// mini_al - v0.8.11 - 2018-11-21
+// mini_al - v0.8.15 - 201x-xx-xx
//
// David Reid - davidreidsoftware@gmail.com
// ABOUT
// =====
-// mini_al is a small library for making it easy to connect to a playback or capture device and send
-// or receive data from that device.
-//
-// mini_al uses an asynchronous API. Every device is created with it's own thread, with audio data
-// being delivered to or from the device via a callback. Synchronous APIs are not supported in the
-// interest of keeping the library as simple and light-weight as possible.
+// mini_al is a single file library for audio playback and capture. It's written in C (compilable as
+// C++) and released into the public domain.
//
// Supported Backends:
// - WASAPI
@@ -23,9 +19,9 @@
// - sndio (OpenBSD)
// - audio(4) (NetBSD and OpenBSD)
// - OSS (FreeBSD)
+// - AAudio (Android 8.0+)
// - OpenSL|ES (Android only)
-// - OpenAL
-// - SDL
+// - Web Audio (Emscripten)
// - Null (Silence)
//
// Supported Formats:
@@ -44,42 +40,10 @@
//
// You can then #include this file in other parts of the program as you would with any other header file.
//
-// If you want to disable a specific backend, #define the appropriate MAL_NO_* option before the implementation.
-//
-// Note that GCC and Clang requires "-msse2", "-mavx2", etc. for SIMD optimizations.
-//
-//
-// Building for Windows
-// --------------------
-// 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 and iOS
-// --------------------------
-// The macOS build should compile clean without the need to download any dependencies or link to any libraries or
-// 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
-// ------------------
-// The Linux build only requires linking to -ldl, -lpthread and -lm. You do not need any development packages for any
-// of the supported backends.
-//
-// Building for BSD
-// ----------------
-// The BSD build only requires linking to -ldl, -lpthread and -lm. NetBSD uses audio(4), OpenBSD uses sndio and
-// FreeBSD uses OSS.
-//
-// Building for Android
-// --------------------
-// The Android build uses OpenSL|ES, and will require an appropriate API level that supports OpenSL|ES. mini_al has
-// been tested against API levels 16 and 21.
-//
-// Building for Emscripten
-// -----------------------
-// The Emscripten build currently uses SDL 1.2 for it's backend which means specifying "-s USE_SDL=2" is unecessary
-// as of this version.
-//
+// mini_al uses an asynchronous, callback based API. You initialize a device with a configuration (sample rate,
+// channel count, etc.) which includes the callback you want to use to handle data transmission to/from the
+// device. In the callback you either read from a data pointer in the case of playback or write to it in the case
+// of capture.
//
// Playback Example
// ----------------
@@ -111,6 +75,42 @@
//
//
//
+// If you want to disable a specific backend, #define the appropriate MAL_NO_* option before the implementation.
+//
+// Note that GCC and Clang requires "-msse2", "-mavx2", etc. for SIMD optimizations.
+//
+//
+// Building for Windows
+// --------------------
+// 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 and iOS
+// --------------------------
+// The macOS build should compile clean without the need to download any dependencies or link to any libraries or
+// 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
+// ------------------
+// The Linux build only requires linking to -ldl, -lpthread and -lm. You do not need any development packages.
+//
+// Building for BSD
+// ----------------
+// The BSD build only requires linking to -ldl, -lpthread and -lm. NetBSD uses audio(4), OpenBSD uses sndio and
+// FreeBSD uses OSS.
+//
+// Building for Android
+// --------------------
+// AAudio is the highest priority backend on Android. This should work out out of the box without needing any kind of
+// compiler configuration. Support for AAudio starts with Android 8 which means older versions will fall back to
+// OpenSL|ES which requires API level 16+.
+//
+// Building for Emscripten
+// -----------------------
+// The Emscripten build emits Web Audio JavaScript directly and should Just Work without any configuration.
+//
+//
// NOTES
// =====
// - This library uses an asynchronous API for delivering and requesting audio data. Each device will have
@@ -127,7 +127,6 @@
// mode.
//
//
-//
// BACKEND NUANCES
// ===============
//
@@ -141,7 +140,12 @@
// -------
// - To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest:
//
-// - Only a single mal_context can be active at any given time. This is due to a limitation with OpenSL|ES.
+// - With OpenSL|ES, only a single mal_context can be active at any given time. This is due to a limitation with OpenSL|ES.
+// - With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration API (devices are
+// enumerated through Java). You can however perform your own device enumeration through Java and then set the ID in the
+// mal_device_id structure (mal_device_id.aaudio) and pass it to mal_device_init().
+// - The backend API will perform resampling where possible. The reason for this as opposed to using mini_al's built-in
+// resampler is to take advantage of any potential device-specific optimizations the driver may implement.
//
// UWP
// ---
@@ -154,6 +158,15 @@
//
//
//
+// Web Audio / Emscripten
+// ----------------------
+// - The first time a context is initialized it will create a global object called "mal" whose primary purpose is to act
+// as a factory for device objects.
+// - Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as they've been deprecated.
+// - Google is implementing a policy in their browsers that prevent automatic media output without first receiving some kind
+// of user input. See here for details: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting
+// the device may fail if you try to start playback without first handling some kind of user input.
+//
// OpenAL
// ------
// - Capture is not supported on iOS with OpenAL. Use the Core Audio backend instead.
@@ -193,9 +206,15 @@
// #define MAL_NO_OSS
// Disables the OSS backend.
//
+// #define MAL_NO_AAUDIO
+// Disables the AAudio backend.
+//
// #define MAL_NO_OPENSL
// Disables the OpenSL|ES backend.
//
+// #define MAL_NO_WEBAUDIO
+// Disables the Web Audio backend.
+//
// #define MAL_NO_OPENAL
// Disables the OpenAL backend.
//
@@ -576,8 +595,10 @@ typedef enum
typedef enum
{
- mal_channel_mix_mode_planar_blend = 0, // Simple averaging based on the plane(s) the channel is sitting on.
+ mal_channel_mix_mode_rectangular = 0, // Simple averaging based on the plane(s) the channel is sitting on.
mal_channel_mix_mode_simple, // Drop excess channels; zeroed out extra channels.
+ mal_channel_mix_mode_custom_weights, // Use custom weights specified in mal_channel_router_config.
+ mal_channel_mix_mode_planar_blend = mal_channel_mix_mode_rectangular,
mal_channel_mix_mode_default = mal_channel_mix_mode_planar_blend
} mal_channel_mix_mode;
@@ -590,6 +611,7 @@ typedef enum
mal_standard_channel_map_vorbis,
mal_standard_channel_map_sound4, // FreeBSD's sound(4).
mal_standard_channel_map_sndio, // www.sndio.org/tips.html
+ mal_standard_channel_map_webaudio = mal_standard_channel_map_flac, // https://webaudio.github.io/web-audio-api/#ChannelOrdering. Only 1, 2, 4 and 6 channels are defined, but can fill in the gaps with logical assumptions.
mal_standard_channel_map_default = mal_standard_channel_map_microsoft
} mal_standard_channel_map;
@@ -645,6 +667,7 @@ typedef struct
mal_channel channelMapIn[MAL_MAX_CHANNELS];
mal_channel channelMapOut[MAL_MAX_CHANNELS];
mal_channel_mix_mode mixingMode;
+ float weights[MAL_MAX_CHANNELS][MAL_MAX_CHANNELS]; // [in][out]. Only used when mixingMode is set to mal_channel_mix_mode_custom_weights.
mal_bool32 noSSE2 : 1;
mal_bool32 noAVX2 : 1;
mal_bool32 noAVX512 : 1;
@@ -663,7 +686,6 @@ struct mal_channel_router
mal_bool32 useAVX512 : 1;
mal_bool32 useNEON : 1;
mal_uint8 shuffleTable[MAL_MAX_CHANNELS];
- float weights[MAL_MAX_CHANNELS][MAL_MAX_CHANNELS];
};
@@ -918,8 +940,8 @@ mal_format_converter_config mal_format_converter_config_init_deinterleaved(mal_f
// 2) Planar Blending
// Channels are blended based on a set of planes that each speaker emits audio from.
//
-// Planar Blending
-// ---------------
+// Rectangular / Planar Blending
+// -----------------------------
// In this mode, channel positions are associated with a set of planes where the channel conceptually emits audio from. An example is the front/left speaker.
// This speaker is positioned to the front of the listener, so you can think of it as emitting audio from the front plane. It is also positioned to the left
// of the listener so you can think of it as also emitting audio from the left plane. Now consider the (unrealistic) situation where the input channel map
@@ -949,7 +971,8 @@ mal_format_converter_config mal_format_converter_config_init_deinterleaved(mal_f
// Note that input and output data is always deinterleaved 32-bit floating point.
//
// Initialize the channel router with mal_channel_router_init(). You will need to pass in a config object which specifies the input and output configuration,
-// mixing mode and a callback for sending data to the router. This callback will be called when input data needs to be sent to the router for processing.
+// mixing mode and a callback for sending data to the router. This callback will be called when input data needs to be sent to the router for processing. Note
+// that the mixing mode is only used when a 1:1 mapping is unavailable. This includes the custom weights mode.
//
// Read data from the channel router with mal_channel_router_read_deinterleaved(). Output data is always 32-bit floating point.
//
@@ -1125,10 +1148,10 @@ void mal_pcm_f32_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dithe
void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, mal_uint64 sampleCount, mal_dither_mode ditherMode);
// Deinterleaves an interleaved buffer.
-void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);
+void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);
// Interleaves a group of deinterleaved buffers.
-void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);
+void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1162,6 +1185,7 @@ void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint3
#define MAL_SUPPORT_JACK
#endif
#if defined(MAL_ANDROID)
+ #define MAL_SUPPORT_AAUDIO
#define MAL_SUPPORT_OPENSL
#endif
#if defined(__OpenBSD__) // <-- Change this to "#if defined(MAL_BSD)" to enable sndio on all BSD flavors.
@@ -1177,13 +1201,15 @@ void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint3
#if defined(MAL_APPLE)
#define MAL_SUPPORT_COREAUDIO
#endif
-
-#define MAL_SUPPORT_SDL // All platforms support SDL.
+#if defined(MAL_EMSCRIPTEN)
+ #define MAL_SUPPORT_WEBAUDIO
+#endif
// Explicitly disable OpenAL and Null backends for Emscripten because they both use a background thread which is not properly supported right now.
#if !defined(MAL_EMSCRIPTEN)
+#define MAL_SUPPORT_SDL
#define MAL_SUPPORT_OPENAL
-#define MAL_SUPPORT_NULL // All platforms support the null backend.
+#define MAL_SUPPORT_NULL
#endif
@@ -1217,9 +1243,15 @@ void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint3
#if !defined(MAL_NO_OSS) && defined(MAL_SUPPORT_OSS)
#define MAL_ENABLE_OSS
#endif
+#if !defined(MAL_NO_AAUDIO) && defined(MAL_SUPPORT_AAUDIO)
+ #define MAL_ENABLE_AAUDIO
+#endif
#if !defined(MAL_NO_OPENSL) && defined(MAL_SUPPORT_OPENSL)
#define MAL_ENABLE_OPENSL
#endif
+#if !defined(MAL_NO_WEBAUDIO) && defined(MAL_SUPPORT_WEBAUDIO)
+ #define MAL_ENABLE_WEBAUDIO
+#endif
#if !defined(MAL_NO_OPENAL) && defined(MAL_SUPPORT_OPENAL)
#define MAL_ENABLE_OPENAL
#endif
@@ -1254,7 +1286,9 @@ typedef enum
mal_backend_sndio,
mal_backend_audio4,
mal_backend_oss,
+ mal_backend_aaudio,
mal_backend_opensl,
+ mal_backend_webaudio,
mal_backend_openal,
mal_backend_sdl
} mal_backend;
@@ -1290,7 +1324,6 @@ typedef struct
pthread_t thread;
} posix;
#endif
-
int _unused;
};
} mal_thread;
@@ -1313,7 +1346,6 @@ typedef struct
pthread_mutex_t mutex;
} posix;
#endif
-
int _unused;
};
} mal_mutex;
@@ -1338,7 +1370,6 @@ typedef struct
mal_uint32 value;
} posix;
#endif
-
int _unused;
};
} mal_event;
@@ -1396,9 +1427,15 @@ typedef union
#ifdef MAL_SUPPORT_OSS
char oss[64]; // "dev/dsp0", etc. "dev/dsp" for the default device.
#endif
+#ifdef MAL_SUPPORT_AAUDIO
+ mal_int32 aaudio; // AAudio uses a 32-bit integer for identification.
+#endif
#ifdef MAL_SUPPORT_OPENSL
mal_uint32 opensl; // OpenSL|ES uses a 32-bit unsigned integer for identification.
#endif
+#ifdef MAL_SUPPORT_WEBAUDIO
+ char webaudio[32]; // Web Audio always uses default devices for now, but if this changes it'll be a GUID.
+#endif
#ifdef MAL_SUPPORT_OPENAL
char openal[256]; // OpenAL seems to use human-readable device names as the ID.
#endif
@@ -1432,7 +1469,11 @@ typedef struct
typedef struct
{
- mal_int64 counter;
+ union
+ {
+ mal_int64 counter;
+ double counterD;
+ };
} mal_timer;
typedef struct
@@ -1699,6 +1740,7 @@ struct mal_context
mal_proc AudioOutputUnitStart;
mal_proc AudioOutputUnitStop;
mal_proc AudioUnitAddPropertyListener;
+ mal_proc AudioUnitGetPropertyInfo;
mal_proc AudioUnitGetProperty;
mal_proc AudioUnitSetProperty;
mal_proc AudioUnitInitialize;
@@ -1741,12 +1783,48 @@ struct mal_context
int versionMinor;
} oss;
#endif
+#ifdef MAL_SUPPORT_AAUDIO
+ struct
+ {
+ mal_handle hAAudio; /* libaaudio.so */
+ mal_proc AAudio_createStreamBuilder;
+ mal_proc AAudioStreamBuilder_delete;
+ mal_proc AAudioStreamBuilder_setDeviceId;
+ mal_proc AAudioStreamBuilder_setDirection;
+ mal_proc AAudioStreamBuilder_setSharingMode;
+ mal_proc AAudioStreamBuilder_setFormat;
+ mal_proc AAudioStreamBuilder_setChannelCount;
+ mal_proc AAudioStreamBuilder_setSampleRate;
+ mal_proc AAudioStreamBuilder_setBufferCapacityInFrames;
+ mal_proc AAudioStreamBuilder_setFramesPerDataCallback;
+ mal_proc AAudioStreamBuilder_setDataCallback;
+ mal_proc AAudioStreamBuilder_setPerformanceMode;
+ mal_proc AAudioStreamBuilder_openStream;
+ mal_proc AAudioStream_close;
+ mal_proc AAudioStream_getState;
+ mal_proc AAudioStream_waitForStateChange;
+ mal_proc AAudioStream_getFormat;
+ mal_proc AAudioStream_getChannelCount;
+ mal_proc AAudioStream_getSampleRate;
+ mal_proc AAudioStream_getBufferCapacityInFrames;
+ mal_proc AAudioStream_getFramesPerDataCallback;
+ mal_proc AAudioStream_getFramesPerBurst;
+ mal_proc AAudioStream_requestStart;
+ mal_proc AAudioStream_requestStop;
+ } aaudio;
+#endif
#ifdef MAL_SUPPORT_OPENSL
struct
{
int _unused;
} opensl;
#endif
+#ifdef MAL_SUPPORT_WEBAUDIO
+ struct
+ {
+ int _unused;
+ } webaudio;
+#endif
#ifdef MAL_SUPPORT_OPENAL
struct
{
@@ -1840,16 +1918,11 @@ struct mal_context
mal_handle hSDL; // SDL
mal_proc SDL_InitSubSystem;
mal_proc SDL_QuitSubSystem;
- mal_proc SDL_CloseAudio;
- mal_proc SDL_OpenAudio;
- mal_proc SDL_PauseAudio;
mal_proc SDL_GetNumAudioDevices;
mal_proc SDL_GetAudioDeviceName;
mal_proc SDL_CloseAudioDevice;
mal_proc SDL_OpenAudioDevice;
mal_proc SDL_PauseAudioDevice;
-
- mal_bool32 usingSDL1;
} sdl;
#endif
#ifdef MAL_SUPPORT_NULL
@@ -2030,6 +2103,7 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device
/*AudioComponent*/ mal_ptr component; // <-- Can this be per-context?
/*AudioUnit*/ mal_ptr audioUnit;
/*AudioBufferList**/ mal_ptr pAudioBufferList; // Only used for input devices.
+ mal_event stopEvent;
mal_bool32 isSwitchingDevice; /* <-- Set to true when the default device has changed and mini_al is in the process of switching. */
} coreaudio;
#endif
@@ -2060,6 +2134,12 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device
void* pIntermediaryBuffer;
} oss;
#endif
+#ifdef MAL_SUPPORT_AAUDIO
+ struct
+ {
+ /*AAudioStream**/ mal_ptr pStream;
+ } aaudio;
+#endif
#ifdef MAL_SUPPORT_OPENSL
struct
{
@@ -2075,6 +2155,12 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device
mal_uint8* pBuffer; // This is malloc()'d and is used for storing audio data. Typed as mal_uint8 for easy offsetting.
} opensl;
#endif
+#ifdef MAL_SUPPORT_WEBAUDIO
+ struct
+ {
+ int index; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */
+ } webaudio;
+#endif
#ifdef MAL_SUPPORT_OPENAL
struct
{
@@ -2130,9 +2216,9 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device
// - PulseAudio
// - ALSA
// - JACK
+// - AAudio
// - OpenSL|ES
-// - OpenAL
-// - SDL
+// - Web Audio / Emscripten
// - Null
//
// is used to configure the context. Use the onLog config to set a callback for whenever a
@@ -2704,6 +2790,10 @@ mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount
#include
#endif
+#ifdef MAL_EMSCRIPTEN
+#include
+#endif
+
#if !defined(MAL_64BIT) && !defined(MAL_32BIT)
#ifdef _WIN32
#ifdef _WIN64
@@ -3749,11 +3839,17 @@ mal_uint32 mal_get_standard_sample_rate_priority_index(mal_uint32 sampleRate)
#define MAL_HAS_AUDIO4 // When enabled, always assume audio(4) is available.
#endif
#ifdef MAL_ENABLE_OSS
- #define MAL_HAS_OSS // OSS is the only supported backend for Unix and BSD, so it must be present else this library is useless.
+ #define MAL_HAS_OSS
+#endif
+#ifdef MAL_ENABLE_AAUDIO
+ #define MAL_HAS_AAUDIO
#endif
#ifdef MAL_ENABLE_OPENSL
#define MAL_HAS_OPENSL // OpenSL is the only supported backend for Android. It must be present.
#endif
+#ifdef MAL_ENABLE_WEBAUDIO
+ #define MAL_HAS_WEBAUDIO
+#endif
#ifdef MAL_ENABLE_OPENAL
#define MAL_HAS_OPENAL
#ifdef MAL_NO_RUNTIME_LINKING
@@ -3797,7 +3893,9 @@ const mal_backend g_malDefaultBackends[] = {
mal_backend_pulseaudio,
mal_backend_alsa,
mal_backend_jack,
+ mal_backend_aaudio,
mal_backend_opensl,
+ mal_backend_webaudio,
mal_backend_openal,
mal_backend_sdl,
mal_backend_null
@@ -3818,7 +3916,9 @@ const char* mal_get_backend_name(mal_backend backend)
case mal_backend_sndio: return "sndio";
case mal_backend_audio4: return "audio(4)";
case mal_backend_oss: return "OSS";
+ case mal_backend_aaudio: return "AAudio";
case mal_backend_opensl: return "OpenSL|ES";
+ case mal_backend_webaudio: return "Web Audio";
case mal_backend_openal: return "OpenAL";
case mal_backend_sdl: return "SDL";
default: return "Unknown";
@@ -3909,6 +4009,16 @@ double mal_timer_get_time_in_seconds(mal_timer* pTimer)
return (newTimeCounter - oldTimeCounter) / g_mal_TimerFrequency;
}
+#elif defined(MAL_EMSCRIPTEN)
+void mal_timer_init(mal_timer* pTimer)
+{
+ pTimer->counterD = emscripten_get_now();
+}
+
+double mal_timer_get_time_in_seconds(mal_timer* pTimer)
+{
+ return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */
+}
#else
#if defined(CLOCK_MONOTONIC)
#define MAL_CLOCK_ID CLOCK_MONOTONIC
@@ -4172,7 +4282,12 @@ void mal_thread_wait__posix(mal_thread* pThread)
void mal_sleep__posix(mal_uint32 milliseconds)
{
- usleep(milliseconds * 1000); // <-- usleep is in microseconds.
+#ifdef MAL_EMSCRIPTEN
+ (void)milliseconds;
+ mal_assert(MAL_FALSE); /* The Emscripten build should never sleep. */
+#else
+ usleep(milliseconds * 1000); /* <-- usleep is in microseconds. */
+#endif
}
@@ -4852,7 +4967,7 @@ mal_result mal_device_init__null(mal_context* pContext, mal_device_type type, co
return MAL_SUCCESS;
}
-mal_result mal_device__start_backend__null(mal_device* pDevice)
+mal_result mal_device_start__null(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -4862,7 +4977,7 @@ mal_result mal_device__start_backend__null(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__stop_backend__null(mal_device* pDevice)
+mal_result mal_device_stop__null(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
(void)pDevice;
@@ -4870,7 +4985,7 @@ mal_result mal_device__stop_backend__null(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__break_main_loop__null(mal_device* pDevice)
+mal_result mal_device_break_main_loop__null(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -4948,7 +5063,7 @@ mal_uint32 mal_device__wait_for_frames__null(mal_device* pDevice)
return mal_device__get_available_frames__null(pDevice);
}
-mal_result mal_device__main_loop__null(mal_device* pDevice)
+mal_result mal_device_main_loop__null(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -5009,10 +5124,10 @@ mal_result mal_context_init__null(mal_context* pContext)
pContext->onGetDeviceInfo = mal_context_get_device_info__null;
pContext->onDeviceInit = mal_device_init__null;
pContext->onDeviceUninit = mal_device_uninit__null;
- pContext->onDeviceStart = mal_device__start_backend__null;
- pContext->onDeviceStop = mal_device__stop_backend__null;
- pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__null;
- pContext->onDeviceMainLoop = mal_device__main_loop__null;
+ pContext->onDeviceStart = mal_device_start__null;
+ pContext->onDeviceStop = mal_device_stop__null;
+ pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__null;
+ pContext->onDeviceMainLoop = mal_device_main_loop__null;
// The null backend always works.
return MAL_SUCCESS;
@@ -6978,7 +7093,7 @@ done:
}
}
-mal_result mal_device__start_backend__wasapi(mal_device* pDevice)
+mal_result mal_device_start__wasapi(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -7006,7 +7121,7 @@ mal_result mal_device__start_backend__wasapi(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__stop_backend__wasapi(mal_device* pDevice)
+mal_result mal_device_stop__wasapi(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -7028,7 +7143,7 @@ mal_result mal_device__stop_backend__wasapi(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__break_main_loop__wasapi(mal_device* pDevice)
+mal_result mal_device_break_main_loop__wasapi(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -7148,7 +7263,7 @@ mal_result mal_device__wait_for_frames__wasapi(mal_device* pDevice, mal_uint32*
return mal_device__get_available_frames__wasapi(pDevice, pFrameCount);
}
-mal_result mal_device__main_loop__wasapi(mal_device* pDevice)
+mal_result mal_device_main_loop__wasapi(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -7262,10 +7377,10 @@ mal_result mal_context_init__wasapi(mal_context* pContext)
pContext->onDeviceInit = mal_device_init__wasapi;
pContext->onDeviceUninit = mal_device_uninit__wasapi;
pContext->onDeviceReinit = mal_device_reinit__wasapi;
- pContext->onDeviceStart = mal_device__start_backend__wasapi;
- pContext->onDeviceStop = mal_device__stop_backend__wasapi;
- pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__wasapi;
- pContext->onDeviceMainLoop = mal_device__main_loop__wasapi;
+ pContext->onDeviceStart = mal_device_start__wasapi;
+ pContext->onDeviceStop = mal_device_stop__wasapi;
+ pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__wasapi;
+ pContext->onDeviceMainLoop = mal_device_main_loop__wasapi;
return result;
}
@@ -8366,7 +8481,7 @@ mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type type,
}
-mal_result mal_device__start_backend__dsound(mal_device* pDevice)
+mal_result mal_device_start__dsound(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -8400,7 +8515,7 @@ mal_result mal_device__start_backend__dsound(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__stop_backend__dsound(mal_device* pDevice)
+mal_result mal_device_stop__dsound(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -8419,7 +8534,7 @@ mal_result mal_device__stop_backend__dsound(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__break_main_loop__dsound(mal_device* pDevice)
+mal_result mal_device_break_main_loop__dsound(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -8522,7 +8637,7 @@ mal_uint32 mal_device__wait_for_frames__dsound(mal_device* pDevice)
return mal_device__get_available_frames__dsound(pDevice);
}
-mal_result mal_device__main_loop__dsound(mal_device* pDevice)
+mal_result mal_device_main_loop__dsound(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -8609,10 +8724,10 @@ mal_result mal_context_init__dsound(mal_context* pContext)
pContext->onGetDeviceInfo = mal_context_get_device_info__dsound;
pContext->onDeviceInit = mal_device_init__dsound;
pContext->onDeviceUninit = mal_device_uninit__dsound;
- pContext->onDeviceStart = mal_device__start_backend__dsound;
- pContext->onDeviceStop = mal_device__stop_backend__dsound;
- pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__dsound;
- pContext->onDeviceMainLoop = mal_device__main_loop__dsound;
+ pContext->onDeviceStart = mal_device_start__dsound;
+ pContext->onDeviceStop = mal_device_stop__dsound;
+ pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__dsound;
+ pContext->onDeviceMainLoop = mal_device_main_loop__dsound;
return MAL_SUCCESS;
}
@@ -9204,7 +9319,7 @@ on_error:
}
-mal_result mal_device__start_backend__winmm(mal_device* pDevice)
+mal_result mal_device_start__winmm(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -9272,7 +9387,7 @@ mal_result mal_device__start_backend__winmm(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__stop_backend__winmm(mal_device* pDevice)
+mal_result mal_device_stop__winmm(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -9311,7 +9426,7 @@ mal_result mal_device__stop_backend__winmm(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__break_main_loop__winmm(mal_device* pDevice)
+mal_result mal_device_break_main_loop__winmm(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -9321,7 +9436,7 @@ mal_result mal_device__break_main_loop__winmm(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__main_loop__winmm(mal_device* pDevice)
+mal_result mal_device_main_loop__winmm(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -9473,10 +9588,10 @@ mal_result mal_context_init__winmm(mal_context* pContext)
pContext->onGetDeviceInfo = mal_context_get_device_info__winmm;
pContext->onDeviceInit = mal_device_init__winmm;
pContext->onDeviceUninit = mal_device_uninit__winmm;
- pContext->onDeviceStart = mal_device__start_backend__winmm;
- pContext->onDeviceStop = mal_device__stop_backend__winmm;
- pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__winmm;
- pContext->onDeviceMainLoop = mal_device__main_loop__winmm;
+ pContext->onDeviceStart = mal_device_start__winmm;
+ pContext->onDeviceStop = mal_device_stop__winmm;
+ pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__winmm;
+ pContext->onDeviceMainLoop = mal_device_main_loop__winmm;
return MAL_SUCCESS;
}
@@ -11005,7 +11120,7 @@ mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, co
}
-mal_result mal_device__start_backend__alsa(mal_device* pDevice)
+mal_result mal_device_start__alsa(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -11036,7 +11151,7 @@ mal_result mal_device__start_backend__alsa(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__stop_backend__alsa(mal_device* pDevice)
+mal_result mal_device_stop__alsa(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -11044,7 +11159,7 @@ mal_result mal_device__stop_backend__alsa(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__break_main_loop__alsa(mal_device* pDevice)
+mal_result mal_device_break_main_loop__alsa(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -11057,7 +11172,7 @@ mal_result mal_device__break_main_loop__alsa(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__main_loop__alsa(mal_device* pDevice)
+mal_result mal_device_main_loop__alsa(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -11283,10 +11398,10 @@ mal_result mal_context_init__alsa(mal_context* pContext)
pContext->onGetDeviceInfo = mal_context_get_device_info__alsa;
pContext->onDeviceInit = mal_device_init__alsa;
pContext->onDeviceUninit = mal_device_uninit__alsa;
- pContext->onDeviceStart = mal_device__start_backend__alsa;
- pContext->onDeviceStop = mal_device__stop_backend__alsa;
- pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__alsa;
- pContext->onDeviceMainLoop = mal_device__main_loop__alsa;
+ pContext->onDeviceStart = mal_device_start__alsa;
+ pContext->onDeviceStop = mal_device_stop__alsa;
+ pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__alsa;
+ pContext->onDeviceMainLoop = mal_device_main_loop__alsa;
return MAL_SUCCESS;
}
@@ -12794,7 +12909,7 @@ mal_result mal_device__cork_stream__pulse(mal_device* pDevice, int cork)
return MAL_SUCCESS;
}
-mal_result mal_device__start_backend__pulse(mal_device* pDevice)
+mal_result mal_device_start__pulse(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -12826,7 +12941,7 @@ mal_result mal_device__start_backend__pulse(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__stop_backend__pulse(mal_device* pDevice)
+mal_result mal_device_stop__pulse(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -12865,7 +12980,7 @@ mal_result mal_device__stop_backend__pulse(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__break_main_loop__pulse(mal_device* pDevice)
+mal_result mal_device_break_main_loop__pulse(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -12878,7 +12993,7 @@ mal_result mal_device__break_main_loop__pulse(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__main_loop__pulse(mal_device* pDevice)
+mal_result mal_device_main_loop__pulse(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -13071,10 +13186,10 @@ mal_result mal_context_init__pulse(mal_context* pContext)
pContext->onGetDeviceInfo = mal_context_get_device_info__pulse;
pContext->onDeviceInit = mal_device_init__pulse;
pContext->onDeviceUninit = mal_device_uninit__pulse;
- pContext->onDeviceStart = mal_device__start_backend__pulse;
- pContext->onDeviceStop = mal_device__stop_backend__pulse;
- pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__pulse;
- pContext->onDeviceMainLoop = mal_device__main_loop__pulse;
+ pContext->onDeviceStart = mal_device_start__pulse;
+ pContext->onDeviceStop = mal_device_stop__pulse;
+ pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__pulse;
+ pContext->onDeviceMainLoop = mal_device_main_loop__pulse;
// Although we have found the libpulse library, it doesn't necessarily mean PulseAudio is useable. We need to initialize
@@ -13462,7 +13577,7 @@ mal_result mal_device_init__jack(mal_context* pContext, mal_device_type type, co
}
-mal_result mal_device__start_backend__jack(mal_device* pDevice)
+mal_result mal_device_start__jack(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -13511,7 +13626,7 @@ mal_result mal_device__start_backend__jack(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__stop_backend__jack(mal_device* pDevice)
+mal_result mal_device_stop__jack(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -13632,8 +13747,8 @@ mal_result mal_context_init__jack(mal_context* pContext)
pContext->onGetDeviceInfo = mal_context_get_device_info__jack;
pContext->onDeviceInit = mal_device_init__jack;
pContext->onDeviceUninit = mal_device_uninit__jack;
- pContext->onDeviceStart = mal_device__start_backend__jack;
- pContext->onDeviceStop = mal_device__stop_backend__jack;
+ pContext->onDeviceStart = mal_device_start__jack;
+ pContext->onDeviceStop = mal_device_stop__jack;
// Getting here means the JACK library is installed, but it doesn't necessarily mean it's usable. We need to quickly test this by connecting
@@ -13691,6 +13806,7 @@ typedef OSStatus (* mal_AudioComponentInstanceNew_proc)(AudioComponent inCompone
typedef OSStatus (* mal_AudioOutputUnitStart_proc)(AudioUnit inUnit);
typedef OSStatus (* mal_AudioOutputUnitStop_proc)(AudioUnit inUnit);
typedef OSStatus (* mal_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData);
+typedef OSStatus (* mal_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable);
typedef OSStatus (* mal_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize);
typedef OSStatus (* mal_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
typedef OSStatus (* mal_AudioUnitInitialize_proc)(AudioUnit inUnit);
@@ -13943,6 +14059,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().
{
@@ -14124,7 +14314,6 @@ mal_result mal_get_AudioObject_stream_descriptions(mal_context* pContext, AudioO
}
-
mal_result mal_get_AudioObject_channel_layout(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, AudioChannelLayout** ppChannelLayout) // NOTE: Free the returned pointer with mal_free().
{
mal_assert(pContext != NULL);
@@ -14183,79 +14372,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);
@@ -14268,9 +14384,11 @@ mal_result mal_get_AudioObject_channel_map(mal_context* pContext, AudioObjectID
result = mal_get_channel_map_from_AudioChannelLayout(pChannelLayout, channelMap);
if (result != MAL_SUCCESS) {
+ mal_free(pChannelLayout);
return result;
}
+ mal_free(pChannelLayout);
return result;
}
@@ -14688,7 +14806,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)
{
@@ -15060,7 +15217,7 @@ OSStatus mal_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pA
}
#if defined(MAL_DEBUG_OUTPUT)
- printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderBufferList->mBuffers[iBuffer].mNumberChannels, pRenderBufferList->mBuffers[iBuffer].mDataByteSize);
+ printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
#endif
}
}
@@ -15108,6 +15265,8 @@ void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPro
if (onStop) {
onStop(pDevice);
}
+
+ mal_event_signal(&pDevice->coreaudio.stopEvent);
} else {
UInt32 isRunning;
UInt32 isRunningSize = sizeof(isRunning);
@@ -15397,11 +15556,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_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut);
+ 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.
@@ -15530,6 +15702,12 @@ mal_result mal_device_init_internal__coreaudio(mal_context* pContext, mal_device
// Grab the name.
#if defined(MAL_APPLE_DESKTOP)
mal_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName);
+#else
+ if (deviceType == mal_device_type_playback) {
+ mal_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MAL_DEFAULT_PLAYBACK_DEVICE_NAME);
+ } else {
+ mal_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MAL_DEFAULT_CAPTURE_DEVICE_NAME);
+ }
#endif
return result;
@@ -15608,7 +15786,7 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev
data.usingDefaultChannelMap = pDevice->usingDefaultChannelMap;
data.shareMode = pDevice->initConfig.shareMode;
- mal_result result = mal_device_init_internal__coreaudio(pDevice->pContext, pDevice->type, NULL, &data, (void*)pDevice);
+ mal_result result = mal_device_init_internal__coreaudio(pDevice->pContext, pDevice->type, pDeviceID, &data, (void*)pDevice);
if (result != MAL_SUCCESS) {
return result;
}
@@ -15646,11 +15824,17 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev
((mal_AudioObjectAddPropertyListener_proc)pDevice->pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &mal_default_output_device_changed__coreaudio, pDevice);
#endif
+ /*
+ When stopping the device, a callback is called on another thread. We need to wait for this callback
+ before returning from mal_device_stop(). This event is used for this.
+ */
+ mal_event_init(pContext, &pDevice->coreaudio.stopEvent);
+
return MAL_SUCCESS;
}
-mal_result mal_device__start_backend__coreaudio(mal_device* pDevice)
+mal_result mal_device_start__coreaudio(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -15662,7 +15846,7 @@ mal_result mal_device__start_backend__coreaudio(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__stop_backend__coreaudio(mal_device* pDevice)
+mal_result mal_device_stop__coreaudio(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -15671,6 +15855,8 @@ mal_result mal_device__stop_backend__coreaudio(mal_device* pDevice)
return mal_result_from_OSStatus(status);
}
+ /* We need to wait for the callback to finish before returning. */
+ mal_event_wait(&pDevice->coreaudio.stopEvent);
return MAL_SUCCESS;
}
@@ -15759,6 +15945,7 @@ mal_result mal_context_init__coreaudio(mal_context* pContext)
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.AudioUnitGetPropertyInfo = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo");
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");
@@ -15779,6 +15966,7 @@ mal_result mal_context_init__coreaudio(mal_context* pContext)
pContext->coreaudio.AudioOutputUnitStart = (mal_proc)AudioOutputUnitStart;
pContext->coreaudio.AudioOutputUnitStop = (mal_proc)AudioOutputUnitStop;
pContext->coreaudio.AudioUnitAddPropertyListener = (mal_proc)AudioUnitAddPropertyListener;
+ pContext->coreaudio.AudioUnitGetPropertyInfo = (mal_proc)AudioUnitGetPropertyInfo;
pContext->coreaudio.AudioUnitGetProperty = (mal_proc)AudioUnitGetProperty;
pContext->coreaudio.AudioUnitSetProperty = (mal_proc)AudioUnitSetProperty;
pContext->coreaudio.AudioUnitInitialize = (mal_proc)AudioUnitInitialize;
@@ -15793,8 +15981,8 @@ mal_result mal_context_init__coreaudio(mal_context* pContext)
pContext->onGetDeviceInfo = mal_context_get_device_info__coreaudio;
pContext->onDeviceInit = mal_device_init__coreaudio;
pContext->onDeviceUninit = mal_device_uninit__coreaudio;
- pContext->onDeviceStart = mal_device__start_backend__coreaudio;
- pContext->onDeviceStop = mal_device__stop_backend__coreaudio;
+ pContext->onDeviceStart = mal_device_start__coreaudio;
+ pContext->onDeviceStop = mal_device_stop__coreaudio;
return MAL_SUCCESS;
}
@@ -16422,7 +16610,7 @@ mal_result mal_device_init__sndio(mal_context* pContext, mal_device_type deviceT
return MAL_SUCCESS;
}
-mal_result mal_device__start_backend__sndio(mal_device* pDevice)
+mal_result mal_device_start__sndio(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -16451,7 +16639,7 @@ mal_result mal_device__start_backend__sndio(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__stop_backend__sndio(mal_device* pDevice)
+mal_result mal_device_stop__sndio(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -16459,7 +16647,7 @@ mal_result mal_device__stop_backend__sndio(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__break_main_loop__sndio(mal_device* pDevice)
+mal_result mal_device_break_main_loop__sndio(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -16467,7 +16655,7 @@ mal_result mal_device__break_main_loop__sndio(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__main_loop__sndio(mal_device* pDevice)
+mal_result mal_device_main_loop__sndio(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -16561,10 +16749,10 @@ mal_result mal_context_init__sndio(mal_context* pContext)
pContext->onGetDeviceInfo = mal_context_get_device_info__sndio;
pContext->onDeviceInit = mal_device_init__sndio;
pContext->onDeviceUninit = mal_device_uninit__sndio;
- pContext->onDeviceStart = mal_device__start_backend__sndio;
- pContext->onDeviceStop = mal_device__stop_backend__sndio;
- pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__sndio;
- pContext->onDeviceMainLoop = mal_device__main_loop__sndio;
+ pContext->onDeviceStart = mal_device_start__sndio;
+ pContext->onDeviceStop = mal_device_stop__sndio;
+ pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__sndio;
+ pContext->onDeviceMainLoop = mal_device_main_loop__sndio;
return MAL_SUCCESS;
}
@@ -17090,7 +17278,7 @@ mal_result mal_device_init__audio4(mal_context* pContext, mal_device_type device
return MAL_SUCCESS;
}
-mal_result mal_device__start_backend__audio4(mal_device* pDevice)
+mal_result mal_device_start__audio4(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -17119,7 +17307,7 @@ mal_result mal_device__start_backend__audio4(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__stop_backend__audio4(mal_device* pDevice)
+mal_result mal_device_stop__audio4(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -17140,7 +17328,7 @@ mal_result mal_device__stop_backend__audio4(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__break_main_loop__audio4(mal_device* pDevice)
+mal_result mal_device_break_main_loop__audio4(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -17186,7 +17374,7 @@ mal_result mal_device__wait__audio4(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__main_loop__audio4(mal_device* pDevice)
+mal_result mal_device_main_loop__audio4(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -17275,10 +17463,10 @@ mal_result mal_context_init__audio4(mal_context* pContext)
pContext->onGetDeviceInfo = mal_context_get_device_info__audio4;
pContext->onDeviceInit = mal_device_init__audio4;
pContext->onDeviceUninit = mal_device_uninit__audio4;
- pContext->onDeviceStart = mal_device__start_backend__audio4;
- pContext->onDeviceStop = mal_device__stop_backend__audio4;
- pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__audio4;
- pContext->onDeviceMainLoop = mal_device__main_loop__audio4;
+ pContext->onDeviceStart = mal_device_start__audio4;
+ pContext->onDeviceStop = mal_device_stop__audio4;
+ pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__audio4;
+ pContext->onDeviceMainLoop = mal_device_main_loop__audio4;
return MAL_SUCCESS;
}
@@ -17629,7 +17817,7 @@ mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, con
}
-mal_result mal_device__start_backend__oss(mal_device* pDevice)
+mal_result mal_device_start__oss(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -17652,7 +17840,7 @@ mal_result mal_device__start_backend__oss(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__stop_backend__oss(mal_device* pDevice)
+mal_result mal_device_stop__oss(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -17675,7 +17863,7 @@ mal_result mal_device__stop_backend__oss(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__break_main_loop__oss(mal_device* pDevice)
+mal_result mal_device_break_main_loop__oss(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -17683,7 +17871,7 @@ mal_result mal_device__break_main_loop__oss(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__main_loop__oss(mal_device* pDevice)
+mal_result mal_device_main_loop__oss(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -17754,10 +17942,10 @@ mal_result mal_context_init__oss(mal_context* pContext)
pContext->onGetDeviceInfo = mal_context_get_device_info__oss;
pContext->onDeviceInit = mal_device_init__oss;
pContext->onDeviceUninit = mal_device_uninit__oss;
- pContext->onDeviceStart = mal_device__start_backend__oss;
- pContext->onDeviceStop = mal_device__stop_backend__oss;
- pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__oss;
- pContext->onDeviceMainLoop = mal_device__main_loop__oss;
+ pContext->onDeviceStart = mal_device_start__oss;
+ pContext->onDeviceStop = mal_device_stop__oss;
+ pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__oss;
+ pContext->onDeviceMainLoop = mal_device_main_loop__oss;
close(fd);
return MAL_SUCCESS;
@@ -17765,6 +17953,483 @@ mal_result mal_context_init__oss(mal_context* pContext)
#endif // OSS
+///////////////////////////////////////////////////////////////////////////////
+//
+// AAudio Backend
+//
+///////////////////////////////////////////////////////////////////////////////
+#ifdef MAL_HAS_AAUDIO
+//#include
+
+#define MAL_AAUDIO_UNSPECIFIED 0
+
+typedef int32_t mal_aaudio_result_t;
+typedef int32_t mal_aaudio_direction_t;
+typedef int32_t mal_aaudio_sharing_mode_t;
+typedef int32_t mal_aaudio_format_t;
+typedef int32_t mal_aaudio_stream_state_t;
+typedef int32_t mal_aaudio_performance_mode_t;
+typedef int32_t mal_aaudio_data_callback_result_t;
+
+/* Result codes. mini_al only cares about the success code. */
+#define MAL_AAUDIO_OK 0
+
+/* Directions. */
+#define MAL_AAUDIO_DIRECTION_OUTPUT 0
+#define MAL_AAUDIO_DIRECTION_INPUT 1
+
+/* Sharing modes. */
+#define MAL_AAUDIO_SHARING_MODE_EXCLUSIVE 0
+#define MAL_AAUDIO_SHARING_MODE_SHARED 1
+
+/* Formats. */
+#define MAL_AAUDIO_FORMAT_PCM_I16 1
+#define MAL_AAUDIO_FORMAT_PCM_FLOAT 2
+
+/* Stream states. */
+#define MAL_AAUDIO_STREAM_STATE_UNINITIALIZED 0
+#define MAL_AAUDIO_STREAM_STATE_UNKNOWN 1
+#define MAL_AAUDIO_STREAM_STATE_OPEN 2
+#define MAL_AAUDIO_STREAM_STATE_STARTING 3
+#define MAL_AAUDIO_STREAM_STATE_STARTED 4
+#define MAL_AAUDIO_STREAM_STATE_PAUSING 5
+#define MAL_AAUDIO_STREAM_STATE_PAUSED 6
+#define MAL_AAUDIO_STREAM_STATE_FLUSHING 7
+#define MAL_AAUDIO_STREAM_STATE_FLUSHED 8
+#define MAL_AAUDIO_STREAM_STATE_STOPPING 9
+#define MAL_AAUDIO_STREAM_STATE_STOPPED 10
+#define MAL_AAUDIO_STREAM_STATE_CLOSING 11
+#define MAL_AAUDIO_STREAM_STATE_CLOSED 12
+#define MAL_AAUDIO_STREAM_STATE_DISCONNECTED 13
+
+/* Performance modes. */
+#define MAL_AAUDIO_PERFORMANCE_MODE_NONE 10
+#define MAL_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11
+#define MAL_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12
+
+/* Callback results. */
+#define MAL_AAUDIO_CALLBACK_RESULT_CONTINUE 0
+#define MAL_AAUDIO_CALLBACK_RESULT_STOP 1
+
+/* Objects. */
+typedef struct mal_AAudioStreamBuilder_t* mal_AAudioStreamBuilder;
+typedef struct mal_AAudioStream_t* mal_AAudioStream;
+
+typedef mal_aaudio_data_callback_result_t (*mal_AAudioStream_dataCallback)(mal_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames);
+
+typedef mal_aaudio_result_t (* MAL_PFN_AAudio_createStreamBuilder) (mal_AAudioStreamBuilder** ppBuilder);
+typedef mal_aaudio_result_t (* MAL_PFN_AAudioStreamBuilder_delete) (mal_AAudioStreamBuilder* pBuilder);
+typedef void (* MAL_PFN_AAudioStreamBuilder_setDeviceId) (mal_AAudioStreamBuilder* pBuilder, int32_t deviceId);
+typedef void (* MAL_PFN_AAudioStreamBuilder_setDirection) (mal_AAudioStreamBuilder* pBuilder, mal_aaudio_direction_t direction);
+typedef void (* MAL_PFN_AAudioStreamBuilder_setSharingMode) (mal_AAudioStreamBuilder* pBuilder, mal_aaudio_sharing_mode_t sharingMode);
+typedef void (* MAL_PFN_AAudioStreamBuilder_setFormat) (mal_AAudioStreamBuilder* pBuilder, mal_aaudio_format_t format);
+typedef void (* MAL_PFN_AAudioStreamBuilder_setChannelCount) (mal_AAudioStreamBuilder* pBuilder, int32_t channelCount);
+typedef void (* MAL_PFN_AAudioStreamBuilder_setSampleRate) (mal_AAudioStreamBuilder* pBuilder, int32_t sampleRate);
+typedef void (* MAL_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(mal_AAudioStreamBuilder* pBuilder, int32_t numFrames);
+typedef void (* MAL_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (mal_AAudioStreamBuilder* pBuilder, int32_t numFrames);
+typedef void (* MAL_PFN_AAudioStreamBuilder_setDataCallback) (mal_AAudioStreamBuilder* pBuilder, mal_AAudioStream_dataCallback callback, void* pUserData);
+typedef void (* MAL_PFN_AAudioStreamBuilder_setPerformanceMode) (mal_AAudioStreamBuilder* pBuilder, mal_aaudio_performance_mode_t mode);
+typedef mal_aaudio_result_t (* MAL_PFN_AAudioStreamBuilder_openStream) (mal_AAudioStreamBuilder* pBuilder, mal_AAudioStream** ppStream);
+typedef mal_aaudio_result_t (* MAL_PFN_AAudioStream_close) (mal_AAudioStream* pStream);
+typedef mal_aaudio_stream_state_t (* MAL_PFN_AAudioStream_getState) (mal_AAudioStream* pStream);
+typedef mal_aaudio_result_t (* MAL_PFN_AAudioStream_waitForStateChange) (mal_AAudioStream* pStream, mal_aaudio_stream_state_t inputState, mal_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds);
+typedef mal_aaudio_format_t (* MAL_PFN_AAudioStream_getFormat) (mal_AAudioStream* pStream);
+typedef int32_t (* MAL_PFN_AAudioStream_getChannelCount) (mal_AAudioStream* pStream);
+typedef int32_t (* MAL_PFN_AAudioStream_getSampleRate) (mal_AAudioStream* pStream);
+typedef int32_t (* MAL_PFN_AAudioStream_getBufferCapacityInFrames) (mal_AAudioStream* pStream);
+typedef int32_t (* MAL_PFN_AAudioStream_getFramesPerDataCallback) (mal_AAudioStream* pStream);
+typedef int32_t (* MAL_PFN_AAudioStream_getFramesPerBurst) (mal_AAudioStream* pStream);
+typedef mal_aaudio_result_t (* MAL_PFN_AAudioStream_requestStart) (mal_AAudioStream* pStream);
+typedef mal_aaudio_result_t (* MAL_PFN_AAudioStream_requestStop) (mal_AAudioStream* pStream);
+
+mal_result mal_result_from_aaudio(mal_aaudio_result_t resultAA)
+{
+ switch (resultAA)
+ {
+ case MAL_AAUDIO_OK: return MAL_SUCCESS;
+ default: break;
+ }
+
+ return MAL_ERROR;
+}
+
+mal_aaudio_data_callback_result_t mal_stream_data_callback__aaudio(mal_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
+{
+ mal_device* pDevice = (mal_device*)pUserData;
+ mal_assert(pDevice != NULL);
+
+ (void)pStream;
+
+ if (pDevice->type == mal_device_type_playback) {
+ mal_device__read_frames_from_client(pDevice, frameCount, pAudioData);
+ } else {
+ mal_device__send_frames_to_client(pDevice, frameCount, pAudioData);
+ }
+
+ return MAL_AAUDIO_CALLBACK_RESULT_CONTINUE;
+}
+
+mal_result mal_open_stream__aaudio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, const mal_device_config* pConfig, const mal_device* pDevice, mal_AAudioStream** ppStream)
+{
+ mal_AAudioStreamBuilder* pBuilder;
+ mal_aaudio_result_t resultAA;
+
+ (void)pContext;
+ *ppStream = NULL;
+
+ resultAA = ((MAL_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder);
+ if (resultAA != MAL_AAUDIO_OK) {
+ return mal_result_from_aaudio(resultAA);
+ }
+
+ if (pDeviceID != NULL) {
+ ((MAL_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio);
+ }
+
+ ((MAL_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == mal_device_type_playback) ? MAL_AAUDIO_DIRECTION_OUTPUT : MAL_AAUDIO_DIRECTION_INPUT);
+ ((MAL_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == mal_share_mode_shared) ? MAL_AAUDIO_SHARING_MODE_SHARED : MAL_AAUDIO_SHARING_MODE_EXCLUSIVE);
+
+ if (pConfig != NULL) {
+ if (pDevice == NULL || !pDevice->usingDefaultSampleRate) {
+ ((MAL_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pConfig->sampleRate);
+ }
+ if (pDevice == NULL || !pDevice->usingDefaultChannels) {
+ ((MAL_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pConfig->channels);
+ }
+ if (pDevice == NULL || !pDevice->usingDefaultFormat) {
+ ((MAL_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pConfig->format == mal_format_s16) ? MAL_AAUDIO_FORMAT_PCM_I16 : MAL_AAUDIO_FORMAT_PCM_FLOAT);
+ }
+
+ ((MAL_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, pConfig->bufferSizeInFrames);
+
+ /* TODO: Don't set the data callback when synchronous reading and writing is being used. */
+ ((MAL_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, pConfig->bufferSizeInFrames / pConfig->periods);
+ ((MAL_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, mal_stream_data_callback__aaudio, (void*)pDevice);
+
+ /* Not sure how this affects things, but since there's a mapping between mini_al's performance profiles and AAudio's performance modes, let go ahead and set it. */
+ ((MAL_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == mal_performance_profile_low_latency) ? MAL_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MAL_AAUDIO_PERFORMANCE_MODE_NONE);
+ }
+
+ resultAA = ((MAL_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream);
+ if (resultAA != MAL_AAUDIO_OK) {
+ *ppStream = NULL;
+ ((MAL_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
+ return mal_result_from_aaudio(resultAA);
+ }
+
+ ((MAL_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
+ return MAL_SUCCESS;
+}
+
+mal_result mal_close_stream__aaudio(mal_context* pContext, mal_AAudioStream* pStream)
+{
+ return mal_result_from_aaudio(((MAL_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream));
+}
+
+mal_bool32 mal_has_default_device__aaudio(mal_context* pContext, mal_device_type deviceType)
+{
+ /* The only way to know this is to try creating a stream. */
+ mal_AAudioStream* pStream;
+ mal_result result = mal_open_stream__aaudio(pContext, deviceType, NULL, mal_share_mode_shared, NULL, NULL, &pStream);
+ if (result != MAL_SUCCESS) {
+ return MAL_FALSE;
+ }
+
+ mal_close_stream__aaudio(pContext, pStream);
+ return MAL_TRUE;
+}
+
+mal_result mal_wait_for_simple_state_transition__aaudio(mal_context* pContext, mal_AAudioStream* pStream, mal_aaudio_stream_state_t oldState, mal_aaudio_stream_state_t newState)
+{
+ mal_aaudio_stream_state_t actualNewState;
+ mal_aaudio_result_t resultAA = ((MAL_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */
+ if (resultAA != MAL_AAUDIO_OK) {
+ return mal_result_from_aaudio(resultAA);
+ }
+
+ if (newState != actualNewState) {
+ return MAL_ERROR; /* Failed to transition into the expected state. */
+ }
+
+ return MAL_SUCCESS;
+}
+
+
+mal_bool32 mal_context_is_device_id_equal__aaudio(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1)
+{
+ mal_assert(pContext != NULL);
+ mal_assert(pID0 != NULL);
+ mal_assert(pID1 != NULL);
+ (void)pContext;
+
+ return pID0->aaudio == pID1->aaudio;
+}
+
+mal_result mal_context_enumerate_devices__aaudio(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData)
+{
+ mal_bool32 cbResult = MAL_TRUE;
+
+ mal_assert(pContext != NULL);
+ mal_assert(callback != NULL);
+
+ /* Unfortunately AAudio does not have an enumeration API. Therefore I'm only going to report default devices, but only if it can instantiate a stream. */
+
+ /* Playback. */
+ if (cbResult) {
+ mal_device_info deviceInfo;
+ mal_zero_object(&deviceInfo);
+ deviceInfo.id.aaudio = MAL_AAUDIO_UNSPECIFIED;
+ mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
+
+ if (mal_has_default_device__aaudio(pContext, mal_device_type_playback)) {
+ cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData);
+ }
+ }
+
+ /* Capture. */
+ if (cbResult) {
+ mal_device_info deviceInfo;
+ mal_zero_object(&deviceInfo);
+ deviceInfo.id.aaudio = MAL_AAUDIO_UNSPECIFIED;
+ mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
+
+ if (mal_has_default_device__aaudio(pContext, mal_device_type_capture)) {
+ cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData);
+ }
+ }
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_context_get_device_info__aaudio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo)
+{
+ mal_AAudioStream* pStream;
+ mal_result result;
+
+ mal_assert(pContext != NULL);
+
+ /* ID */
+ if (pDeviceID != NULL) {
+ pDeviceInfo->id.aaudio = pDeviceID->aaudio;
+ } else {
+ pDeviceInfo->id.aaudio = MAL_AAUDIO_UNSPECIFIED;
+ }
+
+ /* Name */
+ 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);
+ }
+
+
+ /* We'll need to open the device to get accurate sample rate and channel count information. */
+ result = mal_open_stream__aaudio(pContext, deviceType, pDeviceID, shareMode, NULL, NULL, &pStream);
+ if (result != MAL_SUCCESS) {
+ return result;
+ }
+
+ pDeviceInfo->minChannels = ((MAL_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream);
+ pDeviceInfo->maxChannels = pDeviceInfo->minChannels;
+ pDeviceInfo->minSampleRate = ((MAL_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream);
+ pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
+
+ mal_close_stream__aaudio(pContext, pStream);
+ pStream = NULL;
+
+
+ /* AAudio supports s16 and f32. */
+ pDeviceInfo->formatCount = 2;
+ pDeviceInfo->formats[0] = mal_format_s16;
+ pDeviceInfo->formats[1] = mal_format_f32;
+
+ return MAL_SUCCESS;
+}
+
+
+void mal_device_uninit__aaudio(mal_device* pDevice)
+{
+ mal_assert(pDevice != NULL);
+
+ mal_close_stream__aaudio(pDevice->pContext, (mal_AAudioStream*)pDevice->aaudio.pStream);
+ pDevice->aaudio.pStream = NULL;
+}
+
+mal_result mal_device_init__aaudio(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice)
+{
+ mal_result result;
+
+ mal_assert(pDevice != NULL);
+
+ /* We need to make a copy of the config so we can make an adjustment to the buffer size. */
+ mal_device_config config = *pConfig;
+ config.bufferSizeInFrames = pDevice->bufferSizeInFrames;
+ if (config.bufferSizeInFrames == 0) {
+ config.bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->sampleRate);
+ }
+
+ /* We first need to try opening the stream. */
+ result = mal_open_stream__aaudio(pContext, type, pDeviceID, pConfig->shareMode, &config, pDevice, (mal_AAudioStream**)&pDevice->aaudio.pStream);
+ if (result != MAL_SUCCESS) {
+ return result; /* Failed to open the AAudio stream. */
+ }
+
+ pDevice->internalFormat = (((MAL_PFN_AAudioStream_getFormat)pContext->aaudio.AAudioStream_getFormat)((mal_AAudioStream*)pDevice->aaudio.pStream) == MAL_AAUDIO_FORMAT_PCM_I16) ? mal_format_s16 : mal_format_f32;
+ pDevice->internalChannels = ((MAL_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)((mal_AAudioStream*)pDevice->aaudio.pStream);
+ pDevice->internalSampleRate = ((MAL_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)((mal_AAudioStream*)pDevice->aaudio.pStream);
+ mal_get_standard_channel_map(mal_standard_channel_map_default, pDevice->internalChannels, pDevice->internalChannelMap); /* <-- Cannot find info on channel order, so assuming a default. */
+ pDevice->bufferSizeInFrames = ((MAL_PFN_AAudioStream_getBufferCapacityInFrames)pContext->aaudio.AAudioStream_getBufferCapacityInFrames)((mal_AAudioStream*)pDevice->aaudio.pStream);
+
+ /* TODO: When synchronous reading and writing is supported, use AAudioStream_getFramesPerBurst() instead of AAudioStream_getFramesPerDataCallback(). Keep
+ * using AAudioStream_getFramesPerDataCallback() for asynchronous mode, though. */
+ int32_t framesPerPeriod = ((MAL_PFN_AAudioStream_getFramesPerDataCallback)pContext->aaudio.AAudioStream_getFramesPerDataCallback)((mal_AAudioStream*)pDevice->aaudio.pStream);
+ if (framesPerPeriod > 0) {
+ pDevice->periods = 1;
+ } else {
+ pDevice->periods = pDevice->bufferSizeInFrames / framesPerPeriod;
+ }
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_device_start__aaudio(mal_device* pDevice)
+{
+ mal_aaudio_result_t resultAA;
+
+ mal_assert(pDevice != NULL);
+
+ resultAA = ((MAL_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)((mal_AAudioStream*)pDevice->aaudio.pStream);
+ if (resultAA != MAL_AAUDIO_OK) {
+ return mal_result_from_aaudio(resultAA);
+ }
+
+ /* Do we actually need to wait for the device to transition into it's started state? */
+
+ /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */
+ mal_aaudio_stream_state_t currentState = ((MAL_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)((mal_AAudioStream*)pDevice->aaudio.pStream);
+ if (currentState != MAL_AAUDIO_STREAM_STATE_STARTED) {
+ mal_result result;
+
+ if (currentState != MAL_AAUDIO_STREAM_STATE_STARTING) {
+ return MAL_ERROR; /* Expecting the stream to be a starting or started state. */
+ }
+
+ result = mal_wait_for_simple_state_transition__aaudio(pDevice->pContext, (mal_AAudioStream*)pDevice->aaudio.pStream, currentState, MAL_AAUDIO_STREAM_STATE_STARTED);
+ if (result != MAL_SUCCESS) {
+ return result;
+ }
+ }
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_device_stop__aaudio(mal_device* pDevice)
+{
+ mal_aaudio_result_t resultAA;
+
+ mal_assert(pDevice != NULL);
+
+ resultAA = ((MAL_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)((mal_AAudioStream*)pDevice->aaudio.pStream);
+ if (resultAA != MAL_AAUDIO_OK) {
+ return mal_result_from_aaudio(resultAA);
+ }
+
+ /* The device should be in either a stopping or stopped state. If it's not set to started we need to wait for it to transition. It should go from stopping to stopped. */
+ mal_aaudio_stream_state_t currentState = ((MAL_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)((mal_AAudioStream*)pDevice->aaudio.pStream);
+ if (currentState != MAL_AAUDIO_STREAM_STATE_STOPPED) {
+ mal_result result;
+
+ if (currentState != MAL_AAUDIO_STREAM_STATE_STOPPING) {
+ return MAL_ERROR; /* Expecting the stream to be a stopping or stopped state. */
+ }
+
+ result = mal_wait_for_simple_state_transition__aaudio(pDevice->pContext, (mal_AAudioStream*)pDevice->aaudio.pStream, currentState, MAL_AAUDIO_STREAM_STATE_STOPPED);
+ if (result != MAL_SUCCESS) {
+ return result;
+ }
+ }
+
+ mal_stop_proc onStop = pDevice->onStop;
+ if (onStop) {
+ onStop(pDevice);
+ }
+
+ return MAL_SUCCESS;
+}
+
+
+mal_result mal_context_uninit__aaudio(mal_context* pContext)
+{
+ mal_assert(pContext != NULL);
+ mal_assert(pContext->backend == mal_backend_aaudio);
+
+ mal_dlclose(pContext->aaudio.hAAudio);
+ pContext->aaudio.hAAudio = NULL;
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_context_init__aaudio(mal_context* pContext)
+{
+ mal_assert(pContext != NULL);
+ (void)pContext;
+
+ const char* libNames[] = {
+ "libaaudio.so"
+ };
+
+ for (size_t i = 0; i < mal_countof(libNames); ++i) {
+ pContext->aaudio.hAAudio = mal_dlopen(libNames[i]);
+ if (pContext->aaudio.hAAudio != NULL) {
+ break;
+ }
+ }
+
+ if (pContext->aaudio.hAAudio == NULL) {
+ return MAL_FAILED_TO_INIT_BACKEND;
+ }
+
+ pContext->aaudio.AAudio_createStreamBuilder = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudio_createStreamBuilder");
+ pContext->aaudio.AAudioStreamBuilder_delete = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete");
+ pContext->aaudio.AAudioStreamBuilder_setDeviceId = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId");
+ pContext->aaudio.AAudioStreamBuilder_setDirection = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection");
+ pContext->aaudio.AAudioStreamBuilder_setSharingMode = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode");
+ pContext->aaudio.AAudioStreamBuilder_setFormat = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat");
+ pContext->aaudio.AAudioStreamBuilder_setChannelCount = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount");
+ pContext->aaudio.AAudioStreamBuilder_setSampleRate = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate");
+ pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames");
+ pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback");
+ pContext->aaudio.AAudioStreamBuilder_setDataCallback = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback");
+ pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode");
+ pContext->aaudio.AAudioStreamBuilder_openStream = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream");
+ pContext->aaudio.AAudioStream_close = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_close");
+ pContext->aaudio.AAudioStream_getState = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getState");
+ pContext->aaudio.AAudioStream_waitForStateChange = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange");
+ pContext->aaudio.AAudioStream_getFormat = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getFormat");
+ pContext->aaudio.AAudioStream_getChannelCount = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getChannelCount");
+ pContext->aaudio.AAudioStream_getSampleRate = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getSampleRate");
+ pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames");
+ pContext->aaudio.AAudioStream_getFramesPerDataCallback = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback");
+ pContext->aaudio.AAudioStream_getFramesPerBurst = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst");
+ pContext->aaudio.AAudioStream_requestStart = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_requestStart");
+ pContext->aaudio.AAudioStream_requestStop = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_requestStop");
+
+ pContext->isBackendAsynchronous = MAL_TRUE;
+
+ pContext->onUninit = mal_context_uninit__aaudio;
+ pContext->onDeviceIDEqual = mal_context_is_device_id_equal__aaudio;
+ pContext->onEnumDevices = mal_context_enumerate_devices__aaudio;
+ pContext->onGetDeviceInfo = mal_context_get_device_info__aaudio;
+ pContext->onDeviceInit = mal_device_init__aaudio;
+ pContext->onDeviceUninit = mal_device_uninit__aaudio;
+ pContext->onDeviceStart = mal_device_start__aaudio;
+ pContext->onDeviceStop = mal_device_stop__aaudio;
+
+ return MAL_SUCCESS;
+}
+#endif // AAudio
+
+
///////////////////////////////////////////////////////////////////////////////
//
// OpenSL|ES Backend
@@ -17949,6 +18614,11 @@ mal_result mal_context_enumerate_devices__opensl(mal_context* pContext, mal_enum
mal_assert(pContext != NULL);
mal_assert(callback != NULL);
+ mal_assert(g_malOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to enumerate devices. */
+ if (g_malOpenSLInitCounter == 0) {
+ return MAL_INVALID_OPERATION;
+ }
+
// TODO: Test Me.
//
// This is currently untested, so for now we are just returning default devices.
@@ -18049,6 +18719,11 @@ mal_result mal_context_get_device_info__opensl(mal_context* pContext, mal_device
mal_assert(pContext != NULL);
(void)shareMode;
+ mal_assert(g_malOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to get device info. */
+ if (g_malOpenSLInitCounter == 0) {
+ return MAL_INVALID_OPERATION;
+ }
+
// TODO: Test Me.
//
// This is currently untested, so for now we are just returning default devices.
@@ -18176,12 +18851,23 @@ void mal_device_uninit__opensl(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
+ mal_assert(g_malOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before uninitializing the device. */
+ if (g_malOpenSLInitCounter == 0) {
+ return;
+ }
+
// Uninit device.
if (pDevice->type == mal_device_type_playback) {
- if (pDevice->opensl.pAudioPlayerObj) MAL_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj);
- if (pDevice->opensl.pOutputMixObj) MAL_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj);
+ if (pDevice->opensl.pAudioPlayerObj) {
+ MAL_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj);
+ }
+ if (pDevice->opensl.pOutputMixObj) {
+ MAL_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj);
+ }
} else {
- if (pDevice->opensl.pAudioRecorderObj) MAL_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj);
+ if (pDevice->opensl.pAudioRecorderObj) {
+ MAL_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj);
+ }
}
mal_free(pDevice->opensl.pBuffer);
@@ -18191,6 +18877,11 @@ mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type type,
{
(void)pContext;
+ mal_assert(g_malOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to initialize a new device. */
+ if (g_malOpenSLInitCounter == 0) {
+ return MAL_INVALID_OPERATION;
+ }
+
// For now, only supporting Android implementations of OpenSL|ES since that's the only one I've
// been able to test with and I currently depend on Android-specific extensions (simple buffer
// queues).
@@ -18451,10 +19142,15 @@ mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type type,
return MAL_SUCCESS;
}
-mal_result mal_device__start_backend__opensl(mal_device* pDevice)
+mal_result mal_device_start__opensl(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
+ mal_assert(g_malOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to start the device. */
+ if (g_malOpenSLInitCounter == 0) {
+ return MAL_INVALID_OPERATION;
+ }
+
if (pDevice->type == mal_device_type_playback) {
SLresult resultSL = MAL_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING);
if (resultSL != SL_RESULT_SUCCESS) {
@@ -18491,10 +19187,15 @@ mal_result mal_device__start_backend__opensl(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__stop_backend__opensl(mal_device* pDevice)
+mal_result mal_device_stop__opensl(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
+ mal_assert(g_malOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before stopping/uninitializing the device. */
+ if (g_malOpenSLInitCounter == 0) {
+ return MAL_INVALID_OPERATION;
+ }
+
if (pDevice->type == mal_device_type_playback) {
SLresult resultSL = MAL_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
if (resultSL != SL_RESULT_SUCCESS) {
@@ -18511,7 +19212,6 @@ mal_result mal_device__stop_backend__opensl(mal_device* pDevice)
MAL_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueue)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueue);
// Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it.
- mal_device__set_state(pDevice, MAL_STATE_STOPPED);
mal_stop_proc onStop = pDevice->onStop;
if (onStop) {
onStop(pDevice);
@@ -18568,13 +19268,481 @@ mal_result mal_context_init__opensl(mal_context* pContext)
pContext->onGetDeviceInfo = mal_context_get_device_info__opensl;
pContext->onDeviceInit = mal_device_init__opensl;
pContext->onDeviceUninit = mal_device_uninit__opensl;
- pContext->onDeviceStart = mal_device__start_backend__opensl;
- pContext->onDeviceStop = mal_device__stop_backend__opensl;
+ pContext->onDeviceStart = mal_device_start__opensl;
+ pContext->onDeviceStop = mal_device_stop__opensl;
return MAL_SUCCESS;
}
#endif // OpenSL|ES
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Web Audio Backend
+//
+///////////////////////////////////////////////////////////////////////////////
+#ifdef MAL_HAS_WEBAUDIO
+#include
+
+mal_bool32 mal_is_capture_supported__webaudio()
+{
+ return EM_ASM_INT({
+ return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined);
+ }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+EMSCRIPTEN_KEEPALIVE void mal_device_process_pcm_frames__webaudio(mal_device* pDevice, int frameCount, float* pFrames)
+{
+ if (pDevice->type == mal_device_type_playback) {
+ /* Playback. Write to pFrames. */
+ mal_device__read_frames_from_client(pDevice, (mal_uint32)frameCount, pFrames);
+ } else {
+ /* Capture. Read from pFrames. */
+ mal_device__send_frames_to_client(pDevice, (mal_uint32)frameCount, pFrames);
+ }
+}
+#ifdef __cplusplus
+}
+#endif
+
+mal_bool32 mal_context_is_device_id_equal__webaudio(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1)
+{
+ mal_assert(pContext != NULL);
+ mal_assert(pID0 != NULL);
+ mal_assert(pID1 != NULL);
+ (void)pContext;
+
+ return mal_strcmp(pID0->webaudio, pID1->webaudio) == 0;
+}
+
+mal_result mal_context_enumerate_devices__webaudio(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData)
+{
+ mal_assert(pContext != NULL);
+ mal_assert(callback != NULL);
+
+ // Only supporting default devices for now.
+ mal_bool32 cbResult = MAL_TRUE;
+
+ // Playback.
+ if (cbResult) {
+ mal_device_info deviceInfo;
+ mal_zero_object(&deviceInfo);
+ mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
+ cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData);
+ }
+
+ // Capture.
+ if (cbResult) {
+ if (mal_is_capture_supported__webaudio()) {
+ mal_device_info deviceInfo;
+ mal_zero_object(&deviceInfo);
+ mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
+ cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData);
+ }
+ }
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_context_get_device_info__webaudio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo)
+{
+ mal_assert(pContext != NULL);
+ (void)shareMode;
+
+ if (deviceType == mal_device_type_capture && !mal_is_capture_supported__webaudio()) {
+ return MAL_NO_DEVICE;
+ }
+
+
+ mal_zero_memory(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio));
+
+ /* Only supporting default devices for now. */
+ 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);
+ }
+
+ /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */
+ pDeviceInfo->minChannels = 1;
+ pDeviceInfo->maxChannels = MAL_MAX_CHANNELS;
+ if (pDeviceInfo->maxChannels > 32) {
+ pDeviceInfo->maxChannels = 32; /* Maximum output channel count is 32 for createScriptProcessor() (JavaScript). */
+ }
+
+ /* We can query the sample rate by just using a temporary audio context. */
+ pDeviceInfo->minSampleRate = EM_ASM_INT({
+ try {
+ var temp = new (window.AudioContext || window.webkitAudioContext)();
+ var sampleRate = temp.sampleRate;
+ temp.close();
+ return sampleRate;
+ } catch(e) {
+ return 0;
+ }
+ }, 0); /* Must pass in a dummy argument for C99 compatibility. */
+ pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
+ if (pDeviceInfo->minSampleRate == 0) {
+ return MAL_NO_DEVICE;
+ }
+
+ /* Web Audio only supports f32. */
+ pDeviceInfo->formatCount = 1;
+ pDeviceInfo->formats[0] = mal_format_f32;
+
+ return MAL_SUCCESS;
+}
+
+
+void mal_device_uninit__webaudio(mal_device* pDevice)
+{
+ mal_assert(pDevice != NULL);
+
+ EM_ASM({
+ var device = mal.get_device_by_index($0);
+
+ /* Make sure all nodes are disconnected and marked for collection. */
+ if (device.scriptNode !== undefined) {
+ device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */
+ device.scriptNode.disconnect();
+ device.scriptNode = undefined;
+ }
+ if (device.streamNode !== undefined) {
+ device.streamNode.disconnect();
+ device.streamNode = undefined;
+ }
+
+ /*
+ Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want
+ to clear the callback before closing.
+ */
+ device.webaudio.close();
+ device.webaudio = undefined;
+
+ /* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */
+ if (device.intermediaryBuffer !== undefined) {
+ Module._free(device.intermediaryBuffer);
+ device.intermediaryBuffer = undefined;
+ device.intermediaryBufferView = undefined;
+ device.intermediaryBufferSizeInBytes = undefined;
+ }
+
+ /* Make sure the device is untracked so the slot can be reused later. */
+ mal.untrack_device_by_index($0);
+ }, pDevice->webaudio.index, pDevice->type == mal_device_type_playback);
+}
+
+mal_result mal_device_init__webaudio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice)
+{
+ if (deviceType == mal_device_type_capture && !mal_is_capture_supported__webaudio()) {
+ return MAL_NO_DEVICE;
+ }
+
+ /* Try calculating an appropriate default buffer size. */
+ if (pDevice->bufferSizeInFrames == 0) {
+ pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->sampleRate);
+ if (pDevice->usingDefaultBufferSize) {
+ float bufferSizeScaleFactor = 1;
+ pDevice->bufferSizeInFrames = mal_scale_buffer_size(pDevice->bufferSizeInFrames, bufferSizeScaleFactor);
+ }
+ }
+
+ /* The size of the buffer must be a power of 2 and between 256 and 16384. */
+ if (pDevice->bufferSizeInFrames < 256) {
+ pDevice->bufferSizeInFrames = 256;
+ } else if (pDevice->bufferSizeInFrames > 16384) {
+ pDevice->bufferSizeInFrames = 16384;
+ } else {
+ pDevice->bufferSizeInFrames = mal_next_power_of_2(pDevice->bufferSizeInFrames);
+ }
+
+ /* We create the device on the JavaScript side and reference it using an index. We use this to make it possible to reference the device between JavaScript and C. */
+ pDevice->webaudio.index = EM_ASM_INT({
+ var channels = $0;
+ var sampleRate = $1;
+ var bufferSize = $2; /* In PCM frames. */
+ var isPlayback = $3;
+ var pDevice = $4;
+
+ if (typeof(mal) === 'undefined') {
+ return -1; /* Context not initialized. */
+ }
+
+ var device = {};
+
+ /* The AudioContext must be created in a suspended state. */
+ device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate});
+ device.webaudio.suspend();
+
+ /*
+ We need an intermediary buffer which we use for JavaScript and C interop. This buffer stores interleaved f32 PCM data. Because it's passed between
+ JavaScript and C it needs to be allocated and freed using Module._malloc() and Module._free().
+ */
+ device.intermediaryBufferSizeInBytes = channels * bufferSize * 4;
+ device.intermediaryBuffer = Module._malloc(device.intermediaryBufferSizeInBytes);
+ device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
+
+ /*
+ Both playback and capture devices use a ScriptProcessorNode for performing per-sample operations.
+
+ ScriptProcessorNode is actually deprecated so this is likely to be temporary. The way this works for playback is very simple. You just set a callback
+ that's periodically fired, just like a normal audio callback function. But apparently this design is "flawed" and is now deprecated in favour of
+ something called AudioWorklets which _forces_ you to load a _separate_ .js file at run time... nice... Hopefully ScriptProcessorNode will continue to
+ work for years to come, but this may need to change to use AudioSourceBufferNode instead, which I think is what Emscripten uses for it's built-in SDL
+ implementation. I'll be avoiding that insane AudioWorklet API like the plague...
+
+ For capture it is a bit unintuitive. We use the ScriptProccessorNode _only_ to get the raw PCM data. It is connected to an AudioContext just like the
+ playback case, however we just output silence to the AudioContext instead of passing any real data. It would make more sense to me to use the
+ MediaRecorder API, but unfortunately you need to specify a MIME time (Opus, Vorbis, etc.) for the binary blob that's returned to the client, but I've
+ been unable to figure out how to get this as raw PCM. The closes I can think is to use the MIME type for WAV files and just parse it, but I don't know
+ how well this would work. Although ScriptProccessorNode is deprecated, in practice it seems to have pretty good browser support so I'm leaving it like
+ this for now. If anything knows how I could get raw PCM data using the MediaRecorder API please let me know!
+ */
+ device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channels, channels);
+
+ if (isPlayback) {
+ device.scriptNode.onaudioprocess = function(e) {
+ if (device.intermediaryBuffer === undefined) {
+ return; /* This means the device has been uninitialized. */
+ }
+
+ var outputSilence = false;
+
+ /* Sanity check. This will never happen, right? */
+ if (e.outputBuffer.numberOfChannels != channels) {
+ console.log("Playback: Channel count mismatch. " + e.outputBufer.numberOfChannels + " != " + channels + ". Outputting silence.");
+ outputSilence = true;
+ return;
+ }
+
+ /* This looped design guards against the situation where e.outputBuffer is a different size to the original buffer size. Should never happen in practice. */
+ var totalFramesProcessed = 0;
+ while (totalFramesProcessed < e.outputBuffer.length) {
+ var framesRemaining = e.outputBuffer.length - totalFramesProcessed;
+ var framesToProcess = framesRemaining;
+ if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
+ framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
+ }
+
+ /* Read data from the client into our intermediary buffer. */
+ ccall("mal_device_process_pcm_frames__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]);
+
+ /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */
+ if (outputSilence) {
+ for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
+ e.outputBuffer.getChannelData(iChannel).fill(0.0);
+ }
+ } else {
+ for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
+ for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
+ e.outputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame] = device.intermediaryBufferView[iFrame*channels + iChannel];
+ }
+ }
+ }
+
+ totalFramesProcessed += framesToProcess;
+ }
+ };
+
+ device.scriptNode.connect(device.webaudio.destination);
+ } else {
+ device.scriptNode.onaudioprocess = function(e) {
+ if (device.intermediaryBuffer === undefined) {
+ return; /* This means the device has been uninitialized. */
+ }
+
+ /* Make sure silence it output to the AudioContext destination. Not doing this will cause sound to come out of the speakers! */
+ for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
+ e.outputBuffer.getChannelData(iChannel).fill(0.0);
+ }
+
+ /* There are some situations where we may want to send silence to the client. */
+ var sendSilence = false;
+ if (device.streamNode === undefined) {
+ sendSilence = true;
+ }
+
+ /* Sanity check. This will never happen, right? */
+ if (e.inputBuffer.numberOfChannels != channels) {
+ console.log("Capture: Channel count mismatch. " + e.inputBufer.numberOfChannels + " != " + channels + ". Sending silence.");
+ sendSilence = true;
+ }
+
+ /* This looped design guards against the situation where e.inputBuffer is a different size to the original buffer size. Should never happen in practice. */
+ var totalFramesProcessed = 0;
+ while (totalFramesProcessed < e.inputBuffer.length) {
+ var framesRemaining = e.inputBuffer.length - totalFramesProcessed;
+ var framesToProcess = framesRemaining;
+ if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
+ framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
+ }
+
+ /* We need to do the reverse of the playback case. We need to interleave the input data and copy it into the intermediary buffer. Then we send it to the client. */
+ if (sendSilence) {
+ device.intermediaryBufferView.fill(0.0);
+ } else {
+ for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
+ for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) {
+ device.intermediaryBufferView[iFrame*channels + iChannel] = e.inputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame];
+ }
+ }
+ }
+
+ /* Send data to the client from our intermediary buffer. */
+ ccall("mal_device_process_pcm_frames__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]);
+
+ totalFramesProcessed += framesToProcess;
+ }
+ };
+
+ navigator.mediaDevices.getUserMedia({audio:true, video:false})
+ .then(function(stream) {
+ device.streamNode = device.webaudio.createMediaStreamSource(stream);
+ device.streamNode.connect(device.scriptNode);
+ device.scriptNode.connect(device.webaudio.destination);
+ })
+ .catch(function(error) {
+ /* I think this should output silence... */
+ device.scriptNode.connect(device.webaudio.destination);
+ });
+ }
+
+ return mal.track_device(device);
+ }, pConfig->channels, pConfig->sampleRate, pDevice->bufferSizeInFrames, deviceType == mal_device_type_playback, pDevice);
+
+ if (pDevice->webaudio.index < 0) {
+ return MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
+ }
+
+ pDevice->internalFormat = mal_format_f32;
+ pDevice->internalChannels = pConfig->channels;
+ pDevice->internalSampleRate = EM_ASM_INT({ return mal.get_device_by_index($0).webaudio.sampleRate; }, pDevice->webaudio.index);
+ mal_get_standard_channel_map(mal_standard_channel_map_webaudio, pDevice->internalChannels, pDevice->internalChannelMap);
+ pDevice->periods = 1;
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_device_start__webaudio(mal_device* pDevice)
+{
+ mal_assert(pDevice != NULL);
+
+ EM_ASM({
+ mal.get_device_by_index($0).webaudio.resume();
+ }, pDevice->webaudio.index);
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_device_stop__webaudio(mal_device* pDevice)
+{
+ mal_assert(pDevice != NULL);
+
+ EM_ASM({
+ mal.get_device_by_index($0).webaudio.suspend();
+ }, pDevice->webaudio.index);
+
+ mal_stop_proc onStop = pDevice->onStop;
+ if (onStop) {
+ onStop(pDevice);
+ }
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_context_uninit__webaudio(mal_context* pContext)
+{
+ mal_assert(pContext != NULL);
+ mal_assert(pContext->backend == mal_backend_webaudio);
+
+ /* Nothing needs to be done here. */
+ (void)pContext;
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_context_init__webaudio(mal_context* pContext)
+{
+ mal_assert(pContext != NULL);
+
+ /* Here is where our global JavaScript object is initialized. */
+ int resultFromJS = EM_ASM_INT({
+ if ((window.AudioContext || window.webkitAudioContext) === undefined) {
+ return 0; /* Web Audio not supported. */
+ }
+
+ if (typeof(mal) === 'undefined') {
+ mal = {};
+ mal.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */
+
+ mal.track_device = function(device) {
+ /* Try inserting into a free slot first. */
+ for (var iDevice = 0; iDevice < mal.devices.length; ++iDevice) {
+ if (mal.devices[iDevice] == null) {
+ mal.devices[iDevice] = device;
+ return iDevice;
+ }
+ }
+
+ /* Getting here means there is no empty slots in the array so we just push to the end. */
+ mal.devices.push(device);
+ return mal.devices.length - 1;
+ };
+
+ mal.untrack_device_by_index = function(deviceIndex) {
+ /* We just set the device's slot to null. The slot will get reused in the next call to mal_track_device. */
+ mal.devices[deviceIndex] = null;
+
+ /* Trim the array if possible. */
+ while (mal.devices.length > 0) {
+ if (mal.devices[mal.devices.length-1] == null) {
+ mal.devices.pop();
+ } else {
+ break;
+ }
+ }
+ };
+
+ mal.untrack_device = function(device) {
+ for (var iDevice = 0; iDevice < mal.devices.length; ++iDevice) {
+ if (mal.devices[iDevice] == device) {
+ return mal.untrack_device_by_index(iDevice);
+ }
+ }
+ };
+
+ mal.get_device_by_index = function(deviceIndex) {
+ return mal.devices[deviceIndex];
+ };
+ }
+
+ return 1;
+ }, 0); /* Must pass in a dummy argument for C99 compatibility. */
+
+ if (resultFromJS != 1) {
+ return MAL_FAILED_TO_INIT_BACKEND;
+ }
+
+
+ pContext->isBackendAsynchronous = MAL_TRUE;
+
+ pContext->onUninit = mal_context_uninit__webaudio;
+ pContext->onDeviceIDEqual = mal_context_is_device_id_equal__webaudio;
+ pContext->onEnumDevices = mal_context_enumerate_devices__webaudio;
+ pContext->onGetDeviceInfo = mal_context_get_device_info__webaudio;
+ pContext->onDeviceInit = mal_device_init__webaudio;
+ pContext->onDeviceUninit = mal_device_uninit__webaudio;
+ pContext->onDeviceStart = mal_device_start__webaudio;
+ pContext->onDeviceStop = mal_device_stop__webaudio;
+
+ return MAL_SUCCESS;
+}
+#endif // Web Audio
+
+
///////////////////////////////////////////////////////////////////////////////
//
// OpenAL Backend
@@ -19180,7 +20348,7 @@ mal_result mal_device_init__openal(mal_context* pContext, mal_device_type type,
return MAL_SUCCESS;
}
-mal_result mal_device__start_backend__openal(mal_device* pDevice)
+mal_result mal_device_start__openal(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -19210,7 +20378,7 @@ mal_result mal_device__start_backend__openal(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__stop_backend__openal(mal_device* pDevice)
+mal_result mal_device_stop__openal(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -19224,7 +20392,7 @@ mal_result mal_device__stop_backend__openal(mal_device* pDevice)
return MAL_SUCCESS;
}
-mal_result mal_device__break_main_loop__openal(mal_device* pDevice)
+mal_result mal_device_break_main_loop__openal(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -19272,7 +20440,7 @@ mal_uint32 mal_device__wait_for_frames__openal(mal_device* pDevice)
}
}
-mal_result mal_device__main_loop__openal(mal_device* pDevice)
+mal_result mal_device_main_loop__openal(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
@@ -19540,10 +20708,10 @@ mal_result mal_context_init__openal(mal_context* pContext)
pContext->onGetDeviceInfo = mal_context_get_device_info__openal;
pContext->onDeviceInit = mal_device_init__openal;
pContext->onDeviceUninit = mal_device_uninit__openal;
- pContext->onDeviceStart = mal_device__start_backend__openal;
- pContext->onDeviceStop = mal_device__stop_backend__openal;
- pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__openal;
- pContext->onDeviceMainLoop = mal_device__main_loop__openal;
+ pContext->onDeviceStart = mal_device_start__openal;
+ pContext->onDeviceStop = mal_device_stop__openal;
+ pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__openal;
+ pContext->onDeviceMainLoop = mal_device_main_loop__openal;
return MAL_SUCCESS;
}
@@ -19558,8 +20726,6 @@ mal_result mal_context_init__openal(mal_context* pContext)
///////////////////////////////////////////////////////////////////////////////
#ifdef MAL_HAS_SDL
-//#define MAL_USE_SDL_1
-
#define MAL_SDL_INIT_AUDIO 0x00000010
#define MAL_AUDIO_U8 0x0008
#define MAL_AUDIO_S16 0x8010
@@ -19576,11 +20742,6 @@ mal_result mal_context_init__openal(mal_context* pContext)
#define SDL_MAIN_HANDLED
#ifdef MAL_EMSCRIPTEN
#include
-
- // For now just use SDL 1.2 with Emscripten. This avoids the need for "-s USE_SDL=2" at compile time.
- #ifndef MAL_USE_SDL_1
- #define MAL_USE_SDL_1
- #endif
#else
#include
#endif
@@ -19612,11 +20773,8 @@ typedef int (* MAL_PFN_SDL_InitSubSystem)(mal_uint32 flags);
typedef void (* MAL_PFN_SDL_QuitSubSystem)(mal_uint32 flags);
typedef int (* MAL_PFN_SDL_GetNumAudioDevices)(int iscapture);
typedef const char* (* MAL_PFN_SDL_GetAudioDeviceName)(int index, int iscapture);
-typedef void (* MAL_PFN_SDL_CloseAudio)(void);
typedef void (* MAL_PFN_SDL_CloseAudioDevice)(MAL_SDL_AudioDeviceID dev);
-typedef int (* MAL_PFN_SDL_OpenAudio)(MAL_SDL_AudioSpec* desired, MAL_SDL_AudioSpec* obtained);
typedef MAL_SDL_AudioDeviceID (* MAL_PFN_SDL_OpenAudioDevice)(const char* device, int iscapture, const MAL_SDL_AudioSpec* desired, MAL_SDL_AudioSpec* obtained, int allowed_changes);
-typedef void (* MAL_PFN_SDL_PauseAudio)(int pause_on);
typedef void (* MAL_PFN_SDL_PauseAudioDevice)(MAL_SDL_AudioDeviceID dev, int pause_on);
MAL_SDL_AudioFormat mal_format_to_sdl(mal_format format)
@@ -19659,68 +20817,42 @@ mal_result mal_context_enumerate_devices__sdl(mal_context* pContext, mal_enum_de
mal_assert(pContext != NULL);
mal_assert(callback != NULL);
-#ifndef MAL_USE_SDL_1
- if (!pContext->sdl.usingSDL1) {
- mal_bool32 isTerminated = MAL_FALSE;
+ mal_bool32 isTerminated = MAL_FALSE;
- // Playback
- if (!isTerminated) {
- int deviceCount = ((MAL_PFN_SDL_GetNumAudioDevices)pContext->sdl.SDL_GetNumAudioDevices)(0);
- for (int i = 0; i < deviceCount; ++i) {
- mal_device_info deviceInfo;
- mal_zero_object(&deviceInfo);
-
- deviceInfo.id.sdl = i;
- mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(i, 0), (size_t)-1);
-
- mal_bool32 cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData);
- if (cbResult == MAL_FALSE) {
- isTerminated = MAL_TRUE;
- break;
- }
- }
- }
-
- // Capture
- if (!isTerminated) {
- int deviceCount = ((MAL_PFN_SDL_GetNumAudioDevices)pContext->sdl.SDL_GetNumAudioDevices)(1);
- for (int i = 0; i < deviceCount; ++i) {
- mal_device_info deviceInfo;
- mal_zero_object(&deviceInfo);
-
- deviceInfo.id.sdl = i;
- mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(i, 1), (size_t)-1);
-
- mal_bool32 cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData);
- if (cbResult == MAL_FALSE) {
- isTerminated = MAL_TRUE;
- break;
- }
- }
- }
- } else
-#endif
- {
- // SDL1 only uses default devices, and does not support capture.
- mal_bool32 cbResult = MAL_TRUE;
-
- // Playback.
- if (cbResult) {
+ // Playback
+ if (!isTerminated) {
+ int deviceCount = ((MAL_PFN_SDL_GetNumAudioDevices)pContext->sdl.SDL_GetNumAudioDevices)(0);
+ for (int i = 0; i < deviceCount; ++i) {
mal_device_info deviceInfo;
mal_zero_object(&deviceInfo);
- mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
- cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData);
- }
-#if 0 // No capture with SDL1.
- // Capture.
- if (cbResult) {
+ deviceInfo.id.sdl = i;
+ mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(i, 0), (size_t)-1);
+
+ mal_bool32 cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData);
+ if (cbResult == MAL_FALSE) {
+ isTerminated = MAL_TRUE;
+ break;
+ }
+ }
+ }
+
+ // Capture
+ if (!isTerminated) {
+ int deviceCount = ((MAL_PFN_SDL_GetNumAudioDevices)pContext->sdl.SDL_GetNumAudioDevices)(1);
+ for (int i = 0; i < deviceCount; ++i) {
mal_device_info deviceInfo;
mal_zero_object(&deviceInfo);
- mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
- cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData);
+
+ deviceInfo.id.sdl = i;
+ mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(i, 1), (size_t)-1);
+
+ mal_bool32 cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData);
+ if (cbResult == MAL_FALSE) {
+ isTerminated = MAL_TRUE;
+ break;
+ }
}
-#endif
}
return MAL_SUCCESS;
@@ -19731,24 +20863,7 @@ mal_result mal_context_get_device_info__sdl(mal_context* pContext, mal_device_ty
mal_assert(pContext != NULL);
(void)shareMode;
-#ifndef MAL_USE_SDL_1
- if (!pContext->sdl.usingSDL1) {
- if (pDeviceID == NULL) {
- if (deviceType == mal_device_type_playback) {
- pDeviceInfo->id.sdl = 0;
- mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
- } else {
- pDeviceInfo->id.sdl = 0;
- mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
- }
- } else {
- pDeviceInfo->id.sdl = pDeviceID->sdl;
- mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, (deviceType == mal_device_type_playback) ? 0 : 1), (size_t)-1);
- }
- } else
-#endif
- {
- // SDL1 uses default devices.
+ if (pDeviceID == NULL) {
if (deviceType == mal_device_type_playback) {
pDeviceInfo->id.sdl = 0;
mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
@@ -19756,6 +20871,9 @@ mal_result mal_context_get_device_info__sdl(mal_context* pContext, mal_device_ty
pDeviceInfo->id.sdl = 0;
mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
}
+ } else {
+ pDeviceInfo->id.sdl = pDeviceID->sdl;
+ mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, (deviceType == mal_device_type_playback) ? 0 : 1), (size_t)-1);
}
// To get an accurate idea on the backend's native format we need to open the device. Not ideal, but it's the only way. An
@@ -19779,40 +20897,20 @@ mal_result mal_context_get_device_info__sdl(mal_context* pContext, mal_device_ty
MAL_SDL_AudioSpec desiredSpec, obtainedSpec;
mal_zero_memory(&desiredSpec, sizeof(desiredSpec));
-#ifndef MAL_USE_SDL_1
- if (!pContext->sdl.usingSDL1) {
- int isCapture = (deviceType == mal_device_type_playback) ? 0 : 1;
+ int isCapture = (deviceType == mal_device_type_playback) ? 0 : 1;
- const char* pDeviceName = NULL;
- if (pDeviceID != NULL) {
- pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture);
- }
-
- MAL_SDL_AudioDeviceID tempDeviceID = ((MAL_PFN_SDL_OpenAudioDevice)pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE);
- if (tempDeviceID == 0) {
- return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
- }
-
- ((MAL_PFN_SDL_CloseAudioDevice)pContext->sdl.SDL_CloseAudioDevice)(tempDeviceID);
- } else
-#endif
- {
- // SDL1 uses default devices.
- (void)pDeviceID;
-
- // SDL1 only supports playback as far as I can tell.
- if (deviceType != mal_device_type_playback) {
- return MAL_NO_DEVICE;
- }
-
- MAL_SDL_AudioDeviceID tempDeviceID = ((MAL_PFN_SDL_OpenAudio)pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec);
- if (tempDeviceID != 0) {
- return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
- }
-
- ((MAL_PFN_SDL_CloseAudio)pContext->sdl.SDL_CloseAudio)();
+ const char* pDeviceName = NULL;
+ if (pDeviceID != NULL) {
+ pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture);
}
+ MAL_SDL_AudioDeviceID tempDeviceID = ((MAL_PFN_SDL_OpenAudioDevice)pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE);
+ if (tempDeviceID == 0) {
+ return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+ }
+
+ ((MAL_PFN_SDL_CloseAudioDevice)pContext->sdl.SDL_CloseAudioDevice)(tempDeviceID);
+
pDeviceInfo->minChannels = obtainedSpec.channels;
pDeviceInfo->maxChannels = obtainedSpec.channels;
pDeviceInfo->minSampleRate = obtainedSpec.freq;
@@ -19839,14 +20937,7 @@ void mal_device_uninit__sdl(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
-#ifndef MAL_USE_SDL_1
- if (!pDevice->pContext->sdl.usingSDL1) {
- ((MAL_PFN_SDL_CloseAudioDevice)pDevice->pContext->sdl.SDL_CloseAudioDevice)(pDevice->sdl.deviceID);
- } else
-#endif
- {
- ((MAL_PFN_SDL_CloseAudio)pDevice->pContext->sdl.SDL_CloseAudio)();
- }
+ ((MAL_PFN_SDL_CloseAudioDevice)pDevice->pContext->sdl.SDL_CloseAudioDevice)(pDevice->sdl.deviceID);
}
@@ -19858,7 +20949,7 @@ void mal_audio_callback__sdl(void* pUserData, mal_uint8* pBuffer, int bufferSize
mal_uint32 bufferSizeInFrames = (mal_uint32)bufferSizeInBytes / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
#ifdef MAL_DEBUG_OUTPUT
- printf("[SDL] Callback: bufferSizeInBytes=%d, bufferSizeInFrames=%d\n", bufferSizeInBytes, bufferSizeInFrames);
+ printf("[SDL] Callback: bufferSizeInBytes=%d, bufferSizeInFrames=%d\n", bufferSizeInBytes, bufferSizeInFrames);
#endif
if (pDevice->type == mal_device_type_playback) {
@@ -19906,41 +20997,16 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
desiredSpec.format = MAL_AUDIO_F32;
}
-#ifndef MAL_USE_SDL_1
- if (!pDevice->pContext->sdl.usingSDL1) {
- int isCapture = (type == mal_device_type_playback) ? 0 : 1;
+ int isCapture = (type == mal_device_type_playback) ? 0 : 1;
- const char* pDeviceName = NULL;
- if (pDeviceID != NULL) {
- pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pDevice->pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture);
- }
+ const char* pDeviceName = NULL;
+ if (pDeviceID != NULL) {
+ pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pDevice->pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture);
+ }
- pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudioDevice)pDevice->pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE);
- if (pDevice->sdl.deviceID == 0) {
- return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL2 device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
- }
- } else
-#endif
- {
- // SDL1 uses default devices.
- (void)pDeviceID;
-
- // SDL1 only supports playback as far as I can tell.
- if (type != mal_device_type_playback) {
- return MAL_NO_DEVICE;
- }
-
- // SDL1 does not support floating point formats.
- if (desiredSpec.format == MAL_AUDIO_F32) {
- desiredSpec.format = MAL_AUDIO_S16;
- }
-
- int deviceID = ((MAL_PFN_SDL_OpenAudio)pDevice->pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec);
- if (deviceID < 0) {
- return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL1 device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
- }
-
- pDevice->sdl.deviceID = (mal_uint32)deviceID;
+ pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudioDevice)pDevice->pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE);
+ if (pDevice->sdl.deviceID == 0) {
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL2 device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
pDevice->internalFormat = mal_format_from_sdl(obtainedSpec.format);
@@ -19952,7 +21018,6 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
#ifdef MAL_DEBUG_OUTPUT
printf("=== SDL CONFIG ===\n");
- printf(" SDL VERSION: %s\n", pDevice->pContext->sdl.usingSDL1 ? "1" : "2");
printf(" FORMAT: %s -> %s\n", mal_get_format_name(pConfig->format), mal_get_format_name(pDevice->internalFormat));
printf(" CHANNELS: %d -> %d\n", desiredSpec.channels, obtainedSpec.channels);
printf(" SAMPLE RATE: %d -> %d\n", desiredSpec.freq, obtainedSpec.freq);
@@ -19962,34 +21027,19 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
return MAL_SUCCESS;
}
-mal_result mal_device__start_backend__sdl(mal_device* pDevice)
+mal_result mal_device_start__sdl(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
-#ifndef MAL_USE_SDL_1
- if (!pDevice->pContext->sdl.usingSDL1) {
- ((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 0);
- } else
-#endif
- {
- ((MAL_PFN_SDL_PauseAudio)pDevice->pContext->sdl.SDL_PauseAudio)(0);
- }
-
+ ((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 0);
return MAL_SUCCESS;
}
-mal_result mal_device__stop_backend__sdl(mal_device* pDevice)
+mal_result mal_device_stop__sdl(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
-#ifndef MAL_USE_SDL_1
- if (!pDevice->pContext->sdl.usingSDL1) {
- ((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 1);
- } else
-#endif
- {
- ((MAL_PFN_SDL_PauseAudio)pDevice->pContext->sdl.SDL_PauseAudio)(1);
- }
+ ((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 1);
mal_device__set_state(pDevice, MAL_STATE_STOPPED);
mal_stop_proc onStop = pDevice->onStop;
@@ -20018,14 +21068,11 @@ mal_result mal_context_init__sdl(mal_context* pContext)
// Run-time linking.
const char* libNames[] = {
#if defined(MAL_WIN32)
- "SDL2.dll",
- "SDL.dll"
+ "SDL2.dll"
#elif defined(MAL_APPLE)
- "SDL2.framework/SDL2",
- "SDL.framework/SDL"
+ "SDL2.framework/SDL2"
#else
- "libSDL2-2.0.so.0",
- "libSDL-1.2.so.0"
+ "libSDL2-2.0.so.0"
#endif
};
@@ -20042,41 +21089,21 @@ mal_result mal_context_init__sdl(mal_context* pContext)
pContext->sdl.SDL_InitSubSystem = mal_dlsym(pContext->sdl.hSDL, "SDL_InitSubSystem");
pContext->sdl.SDL_QuitSubSystem = mal_dlsym(pContext->sdl.hSDL, "SDL_QuitSubSystem");
- pContext->sdl.SDL_CloseAudio = mal_dlsym(pContext->sdl.hSDL, "SDL_CloseAudio");
- pContext->sdl.SDL_OpenAudio = mal_dlsym(pContext->sdl.hSDL, "SDL_OpenAudio");
- pContext->sdl.SDL_PauseAudio = mal_dlsym(pContext->sdl.hSDL, "SDL_PauseAudio");
-#ifndef MAL_USE_SDL_1
pContext->sdl.SDL_GetNumAudioDevices = mal_dlsym(pContext->sdl.hSDL, "SDL_GetNumAudioDevices");
pContext->sdl.SDL_GetAudioDeviceName = mal_dlsym(pContext->sdl.hSDL, "SDL_GetAudioDeviceName");
pContext->sdl.SDL_CloseAudioDevice = mal_dlsym(pContext->sdl.hSDL, "SDL_CloseAudioDevice");
pContext->sdl.SDL_OpenAudioDevice = mal_dlsym(pContext->sdl.hSDL, "SDL_OpenAudioDevice");
pContext->sdl.SDL_PauseAudioDevice = mal_dlsym(pContext->sdl.hSDL, "SDL_PauseAudioDevice");
-#endif
#else
// Compile-time linking.
pContext->sdl.SDL_InitSubSystem = (mal_proc)SDL_InitSubSystem;
pContext->sdl.SDL_QuitSubSystem = (mal_proc)SDL_QuitSubSystem;
- pContext->sdl.SDL_CloseAudio = (mal_proc)SDL_CloseAudio;
- pContext->sdl.SDL_OpenAudio = (mal_proc)SDL_OpenAudio;
- pContext->sdl.SDL_PauseAudio = (mal_proc)SDL_PauseAudio;
-#ifndef MAL_USE_SDL_1
pContext->sdl.SDL_GetNumAudioDevices = (mal_proc)SDL_GetNumAudioDevices;
pContext->sdl.SDL_GetAudioDeviceName = (mal_proc)SDL_GetAudioDeviceName;
pContext->sdl.SDL_CloseAudioDevice = (mal_proc)SDL_CloseAudioDevice;
pContext->sdl.SDL_OpenAudioDevice = (mal_proc)SDL_OpenAudioDevice;
pContext->sdl.SDL_PauseAudioDevice = (mal_proc)SDL_PauseAudioDevice;
#endif
-#endif
-
- // We need to determine whether or not we are using SDL2 or SDL1. We can know this by looking at whether or not certain
- // function pointers are NULL.
- if (pContext->sdl.SDL_GetNumAudioDevices == NULL ||
- pContext->sdl.SDL_GetAudioDeviceName == NULL ||
- pContext->sdl.SDL_CloseAudioDevice == NULL ||
- pContext->sdl.SDL_OpenAudioDevice == NULL ||
- pContext->sdl.SDL_PauseAudioDevice == NULL) {
- pContext->sdl.usingSDL1 = MAL_TRUE;
- }
int resultSDL = ((MAL_PFN_SDL_InitSubSystem)pContext->sdl.SDL_InitSubSystem)(MAL_SDL_INIT_AUDIO);
if (resultSDL != 0) {
@@ -20091,8 +21118,8 @@ mal_result mal_context_init__sdl(mal_context* pContext)
pContext->onGetDeviceInfo = mal_context_get_device_info__sdl;
pContext->onDeviceInit = mal_device_init__sdl;
pContext->onDeviceUninit = mal_device_uninit__sdl;
- pContext->onDeviceStart = mal_device__start_backend__sdl;
- pContext->onDeviceStop = mal_device__stop_backend__sdl;
+ pContext->onDeviceStart = mal_device_start__sdl;
+ pContext->onDeviceStop = mal_device_stop__sdl;
return MAL_SUCCESS;
}
@@ -20549,12 +21576,24 @@ mal_result mal_context_init(const mal_backend backends[], mal_uint32 backendCoun
result = mal_context_init__oss(pContext);
} break;
#endif
+ #ifdef MAL_HAS_AAUDIO
+ case mal_backend_aaudio:
+ {
+ result = mal_context_init__aaudio(pContext);
+ } break;
+ #endif
#ifdef MAL_HAS_OPENSL
case mal_backend_opensl:
{
result = mal_context_init__opensl(pContext);
} break;
#endif
+ #ifdef MAL_HAS_WEBAUDIO
+ case mal_backend_webaudio:
+ {
+ result = mal_context_init__webaudio(pContext);
+ } break;
+ #endif
#ifdef MAL_HAS_OPENAL
case mal_backend_openal:
{
@@ -20991,14 +22030,14 @@ mal_result mal_device_init_ex(const mal_backend backends[], mal_uint32 backendCo
void mal_device_uninit(mal_device* pDevice)
{
- if (!mal_device__is_initialized(pDevice)) return;
+ if (!mal_device__is_initialized(pDevice)) {
+ return;
+ }
// Make sure the device is stopped first. The backends will probably handle this naturally,
// but I like to do it explicitly for my own sanity.
if (mal_device_is_started(pDevice)) {
- while (mal_device_stop(pDevice) == MAL_DEVICE_BUSY) {
- mal_sleep(1);
- }
+ mal_device_stop(pDevice);
}
// Putting the device into an uninitialized state will make the worker thread return.
@@ -21045,28 +22084,19 @@ void mal_device_set_stop_callback(mal_device* pDevice, mal_stop_proc proc)
mal_result mal_device_start(mal_device* pDevice)
{
- if (pDevice == NULL) return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS);
- if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED);
+ if (pDevice == NULL) {
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS);
+ }
+
+ if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) {
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED);
+ }
mal_result result = MAL_ERROR;
mal_mutex_lock(&pDevice->lock);
{
- // Be a bit more descriptive if the device is already started or is already in the process of starting. This is likely
- // a bug with the application.
- if (mal_device__get_state(pDevice) == MAL_STATE_STARTING) {
- mal_mutex_unlock(&pDevice->lock);
- return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called while another thread is already starting it.", MAL_DEVICE_ALREADY_STARTING);
- }
- if (mal_device__get_state(pDevice) == MAL_STATE_STARTED) {
- mal_mutex_unlock(&pDevice->lock);
- return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called for a device that's already started.", MAL_DEVICE_ALREADY_STARTED);
- }
-
- // The device needs to be in a stopped state. If it's not, we just let the caller know the device is busy.
- if (mal_device__get_state(pDevice) != MAL_STATE_STOPPED) {
- mal_mutex_unlock(&pDevice->lock);
- return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called while another thread is in the process of stopping it.", MAL_DEVICE_BUSY);
- }
+ // Starting, stopping and pausing are wrapped in a mutex which means we can assert that the device is in a stopped or paused state.
+ mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STOPPED /*|| mal_device__get_state(pDevice) == MAL_STATE_PAUSED*/);
mal_device__set_state(pDevice, MAL_STATE_STARTING);
@@ -21094,28 +22124,19 @@ mal_result mal_device_start(mal_device* pDevice)
mal_result mal_device_stop(mal_device* pDevice)
{
- if (pDevice == NULL) return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS);
- if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED);
+ if (pDevice == NULL) {
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS);
+ }
+
+ if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) {
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED);
+ }
mal_result result = MAL_ERROR;
mal_mutex_lock(&pDevice->lock);
{
- // Be a bit more descriptive if the device is already stopped or is already in the process of stopping. This is likely
- // a bug with the application.
- if (mal_device__get_state(pDevice) == MAL_STATE_STOPPING) {
- mal_mutex_unlock(&pDevice->lock);
- return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called while another thread is already stopping it.", MAL_DEVICE_ALREADY_STOPPING);
- }
- if (mal_device__get_state(pDevice) == MAL_STATE_STOPPED) {
- mal_mutex_unlock(&pDevice->lock);
- return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called for a device that's already stopped.", MAL_DEVICE_ALREADY_STOPPED);
- }
-
- // The device needs to be in a started state. If it's not, we just let the caller know the device is busy.
- if (mal_device__get_state(pDevice) != MAL_STATE_STARTED) {
- mal_mutex_unlock(&pDevice->lock);
- return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called while another thread is in the process of starting it.", MAL_DEVICE_BUSY);
- }
+ // Starting, stopping and pausing are wrapped in a mutex which means we can assert that the device is in a started or paused state.
+ mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STARTED /*|| mal_device__get_state(pDevice) == MAL_STATE_PAUSED*/);
mal_device__set_state(pDevice, MAL_STATE_STOPPING);
@@ -21124,6 +22145,7 @@ mal_result mal_device_stop(mal_device* pDevice)
// Asynchronous backends need to be handled differently.
if (mal_context_is_backend_asynchronous(pDevice->pContext)) {
result = pDevice->pContext->onDeviceStop(pDevice);
+ mal_device__set_state(pDevice, MAL_STATE_STOPPED);
} else {
// Synchronous backends.
@@ -24759,8 +25781,8 @@ float g_malChannelPlaneRatios[MAL_CHANNEL_POSITION_COUNT][6] = {
{ 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_RIGHT
{ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_CENTER
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_LFE
- { 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f}, // MAL_CHANNEL_BACK_LEFT
- { 0.0f, 0.5f, 0.0f, 0.0f, 0.5f, 0.0f}, // MAL_CHANNEL_BACK_RIGHT
+ { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, // MAL_CHANNEL_BACK_LEFT
+ { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, // MAL_CHANNEL_BACK_RIGHT
{ 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_LEFT_CENTER
{ 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_RIGHT_CENTER
{ 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, // MAL_CHANNEL_BACK_CENTER
@@ -24971,7 +25993,7 @@ mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
if (channelPosIn == channelPosOut) {
- pRouter->weights[iChannelIn][iChannelOut] = 1;
+ pRouter->config.weights[iChannelIn][iChannelOut] = 1;
}
}
}
@@ -24986,7 +26008,7 @@ mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
if (channelPosOut != MAL_CHANNEL_NONE && channelPosOut != MAL_CHANNEL_MONO && channelPosOut != MAL_CHANNEL_LFE) {
- pRouter->weights[iChannelIn][iChannelOut] = 1;
+ pRouter->config.weights[iChannelIn][iChannelOut] = 1;
}
}
}
@@ -25014,7 +26036,7 @@ mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal
mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
if (channelPosIn != MAL_CHANNEL_NONE && channelPosIn != MAL_CHANNEL_MONO && channelPosIn != MAL_CHANNEL_LFE) {
- pRouter->weights[iChannelIn][iChannelOut] += monoWeight;
+ pRouter->config.weights[iChannelIn][iChannelOut] += monoWeight;
}
}
}
@@ -25024,56 +26046,67 @@ mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal
// Input and output channels that are not present on the other side need to be blended in based on spatial locality.
- if (pRouter->config.mixingMode != mal_channel_mix_mode_simple) {
- // Unmapped input channels.
- for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
- mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
+ switch (pRouter->config.mixingMode)
+ {
+ case mal_channel_mix_mode_rectangular:
+ {
+ // Unmapped input channels.
+ for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
+ mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
- if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) {
- if (!mal_channel_map_contains_channel_position(pRouter->config.channelsOut, pRouter->config.channelMapOut, channelPosIn)) {
- for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
- mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
+ if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) {
+ if (!mal_channel_map_contains_channel_position(pRouter->config.channelsOut, pRouter->config.channelMapOut, channelPosIn)) {
+ for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
+ mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
- if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) {
- float weight = 0;
- if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) {
- weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut);
- }
+ if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) {
+ float weight = 0;
+ if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) {
+ weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut);
+ }
- // Only apply the weight if we haven't already got some contribution from the respective channels.
- if (pRouter->weights[iChannelIn][iChannelOut] == 0) {
- pRouter->weights[iChannelIn][iChannelOut] = weight;
+ // Only apply the weight if we haven't already got some contribution from the respective channels.
+ if (pRouter->config.weights[iChannelIn][iChannelOut] == 0) {
+ pRouter->config.weights[iChannelIn][iChannelOut] = weight;
+ }
}
}
}
}
}
- }
- // Unmapped output channels.
- for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
- mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
+ // Unmapped output channels.
+ for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
+ mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
- if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) {
- if (!mal_channel_map_contains_channel_position(pRouter->config.channelsIn, pRouter->config.channelMapIn, channelPosOut)) {
- for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
- mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
+ if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) {
+ if (!mal_channel_map_contains_channel_position(pRouter->config.channelsIn, pRouter->config.channelMapIn, channelPosOut)) {
+ for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
+ mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
- if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) {
- float weight = 0;
- if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) {
- weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut);
- }
+ if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) {
+ float weight = 0;
+ if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) {
+ weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut);
+ }
- // Only apply the weight if we haven't already got some contribution from the respective channels.
- if (pRouter->weights[iChannelIn][iChannelOut] == 0) {
- pRouter->weights[iChannelIn][iChannelOut] = weight;
+ // Only apply the weight if we haven't already got some contribution from the respective channels.
+ if (pRouter->config.weights[iChannelIn][iChannelOut] == 0) {
+ pRouter->config.weights[iChannelIn][iChannelOut] = weight;
+ }
}
}
}
}
}
- }
+ } break;
+
+ case mal_channel_mix_mode_custom_weights:
+ case mal_channel_mix_mode_simple:
+ default:
+ {
+ /* Fallthrough. */
+ } break;
}
return MAL_SUCCESS;
@@ -25125,7 +26158,7 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram
mal_uint64 iFrame = 0;
#if defined(MAL_SUPPORT_NEON)
if (mal_channel_router__can_use_neon(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
- float32x4_t weight = vmovq_n_f32(pRouter->weights[iChannelIn][iChannelOut]);
+ float32x4_t weight = vmovq_n_f32(pRouter->config.weights[iChannelIn][iChannelOut]);
mal_uint64 frameCount4 = frameCount/4;
for (mal_uint64 iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) {
@@ -25140,7 +26173,7 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram
#endif
#if defined(MAL_SUPPORT_AVX512)
if (mal_channel_router__can_use_avx512(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
- __m512 weight = _mm512_set1_ps(pRouter->weights[iChannelIn][iChannelOut]);
+ __m512 weight = _mm512_set1_ps(pRouter->config.weights[iChannelIn][iChannelOut]);
mal_uint64 frameCount16 = frameCount/16;
for (mal_uint64 iFrame16 = 0; iFrame16 < frameCount16; iFrame16 += 1) {
@@ -25155,7 +26188,7 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram
#endif
#if defined(MAL_SUPPORT_AVX2)
if (mal_channel_router__can_use_avx2(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
- __m256 weight = _mm256_set1_ps(pRouter->weights[iChannelIn][iChannelOut]);
+ __m256 weight = _mm256_set1_ps(pRouter->config.weights[iChannelIn][iChannelOut]);
mal_uint64 frameCount8 = frameCount/8;
for (mal_uint64 iFrame8 = 0; iFrame8 < frameCount8; iFrame8 += 1) {
@@ -25170,7 +26203,7 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram
#endif
#if defined(MAL_SUPPORT_SSE2)
if (mal_channel_router__can_use_sse2(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
- __m128 weight = _mm_set1_ps(pRouter->weights[iChannelIn][iChannelOut]);
+ __m128 weight = _mm_set1_ps(pRouter->config.weights[iChannelIn][iChannelOut]);
mal_uint64 frameCount4 = frameCount/4;
for (mal_uint64 iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) {
@@ -25183,10 +26216,10 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram
} else
#endif
{ // Reference.
- float weight0 = pRouter->weights[iChannelIn][iChannelOut];
- float weight1 = pRouter->weights[iChannelIn][iChannelOut];
- float weight2 = pRouter->weights[iChannelIn][iChannelOut];
- float weight3 = pRouter->weights[iChannelIn][iChannelOut];
+ float weight0 = pRouter->config.weights[iChannelIn][iChannelOut];
+ float weight1 = pRouter->config.weights[iChannelIn][iChannelOut];
+ float weight2 = pRouter->config.weights[iChannelIn][iChannelOut];
+ float weight3 = pRouter->config.weights[iChannelIn][iChannelOut];
mal_uint64 frameCount4 = frameCount/4;
for (mal_uint64 iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) {
@@ -25200,7 +26233,7 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram
// Leftover.
for (; iFrame < frameCount; ++iFrame) {
- ppSamplesOut[iChannelOut][iFrame] += ppSamplesIn[iChannelIn][iFrame] * pRouter->weights[iChannelIn][iChannelOut];
+ ppSamplesOut[iChannelOut][iFrame] += ppSamplesIn[iChannelIn][iFrame] * pRouter->config.weights[iChannelIn][iChannelOut];
}
}
}
@@ -26309,7 +27342,7 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form
}
}
-void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
+void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
{
if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {
return; // Invalid args.
@@ -26320,7 +27353,7 @@ void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uin
case mal_format_s16:
{
const mal_int16* pSrcS16 = (const mal_int16*)pInterleavedPCMFrames;
- for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
+ for (mal_uint64 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
mal_int16* pDstS16 = (mal_int16*)ppDeinterleavedPCMFrames[iChannel];
pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];
@@ -26331,7 +27364,7 @@ void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uin
case mal_format_f32:
{
const float* pSrcF32 = (const float*)pInterleavedPCMFrames;
- for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
+ for (mal_uint64 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];
pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];
@@ -26343,7 +27376,7 @@ void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uin
{
mal_uint32 sampleSizeInBytes = mal_get_bytes_per_sample(format);
- for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
+ for (mal_uint64 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
void* pDst = mal_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
const void* pSrc = mal_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
@@ -26354,14 +27387,14 @@ void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uin
}
}
-void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
+void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
{
switch (format)
{
case mal_format_s16:
{
mal_int16* pDstS16 = (mal_int16*)pInterleavedPCMFrames;
- for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
+ for (mal_uint64 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
const mal_int16* pSrcS16 = (const mal_int16*)ppDeinterleavedPCMFrames[iChannel];
pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];
@@ -26372,7 +27405,7 @@ void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint3
case mal_format_f32:
{
float* pDstF32 = (float*)pInterleavedPCMFrames;
- for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
+ for (mal_uint64 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];
pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];
@@ -26384,7 +27417,7 @@ void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint3
{
mal_uint32 sampleSizeInBytes = mal_get_bytes_per_sample(format);
- for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
+ for (mal_uint64 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
void* pDst = mal_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
const void* pSrc = mal_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
@@ -26790,7 +27823,7 @@ mal_uint64 mal_dsp_read(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut,
}
}
- // Slower path. The real is done here. To do this all we need to do is read from the last stage in the pipeline.
+ // Slower path. The real work is done here. To do this all we need to do is read from the last stage in the pipeline.
mal_assert(pDSP->isPostFormatConversionRequired == MAL_TRUE);
mal_dsp_callback_data data;
@@ -27155,7 +28188,7 @@ mal_result mal_decoder_internal_on_seek_to_frame__wav(mal_decoder* pDecoder, mal
drwav* pWav = (drwav*)pDecoder->pInternalDecoder;
mal_assert(pWav != NULL);
- drwav_bool32 result = drwav_seek_to_sample(pWav, frameIndex*pWav->channels);
+ drwav_bool32 result = drwav_seek_to_pcm_frame(pWav, frameIndex);
if (result) {
return MAL_SUCCESS;
} else {
@@ -27180,9 +28213,9 @@ mal_uint32 mal_decoder_internal_on_read_frames__wav(mal_dsp* pDSP, mal_uint32 fr
mal_assert(pWav != NULL);
switch (pDecoder->internalFormat) {
- case mal_format_s16: return (mal_uint32)drwav_read_s16(pWav, frameCount*pDecoder->internalChannels, (drwav_int16*)pSamplesOut) / pDecoder->internalChannels;
- case mal_format_s32: return (mal_uint32)drwav_read_s32(pWav, frameCount*pDecoder->internalChannels, (drwav_int32*)pSamplesOut) / pDecoder->internalChannels;
- case mal_format_f32: return (mal_uint32)drwav_read_f32(pWav, frameCount*pDecoder->internalChannels, (float*)pSamplesOut) / pDecoder->internalChannels;
+ case mal_format_s16: return (mal_uint32)drwav_read_pcm_frames_s16(pWav, frameCount, (drwav_int16*)pSamplesOut);
+ case mal_format_s32: return (mal_uint32)drwav_read_pcm_frames_s32(pWav, frameCount, (drwav_int32*)pSamplesOut);
+ case mal_format_f32: return (mal_uint32)drwav_read_pcm_frames_f32(pWav, frameCount, (float*)pSamplesOut);
default: break;
}
@@ -27282,7 +28315,7 @@ mal_result mal_decoder_internal_on_seek_to_frame__flac(mal_decoder* pDecoder, ma
drflac* pFlac = (drflac*)pDecoder->pInternalDecoder;
mal_assert(pFlac != NULL);
- drflac_bool32 result = drflac_seek_to_sample(pFlac, frameIndex*pFlac->channels);
+ drflac_bool32 result = drflac_seek_to_pcm_frame(pFlac, frameIndex);
if (result) {
return MAL_SUCCESS;
} else {
@@ -27307,7 +28340,16 @@ mal_uint32 mal_decoder_internal_on_read_frames__flac(mal_dsp* pDSP, mal_uint32 f
drflac* pFlac = (drflac*)pDecoder->pInternalDecoder;
mal_assert(pFlac != NULL);
- return (mal_uint32)drflac_read_s32(pFlac, frameCount*pDecoder->internalChannels, (drflac_int32*)pSamplesOut) / pDecoder->internalChannels;
+ switch (pDecoder->internalFormat) {
+ case mal_format_s16: return (mal_uint32)drflac_read_pcm_frames_s16(pFlac, frameCount, (drflac_int16*)pSamplesOut);
+ case mal_format_s32: return (mal_uint32)drflac_read_pcm_frames_s32(pFlac, frameCount, (drflac_int32*)pSamplesOut);
+ case mal_format_f32: return (mal_uint32)drflac_read_pcm_frames_f32(pFlac, frameCount, (float*)pSamplesOut);
+ default: break;
+ }
+
+ // Should never get here. If we do, it means the internal format was not set correctly at initialization time.
+ mal_assert(MAL_FALSE);
+ return 0;
}
mal_result mal_decoder_init_flac__internal(const mal_decoder_config* pConfig, mal_decoder* pDecoder)
@@ -27326,8 +28368,15 @@ mal_result mal_decoder_init_flac__internal(const mal_decoder_config* pConfig, ma
pDecoder->onUninit = mal_decoder_internal_on_uninit__flac;
pDecoder->pInternalDecoder = pFlac;
- // The internal format is always s32.
+ // dr_flac supports reading as s32, s16 and f32. Try to do a one-to-one mapping if possible, but fall back to s32 if not. s32 is the "native" FLAC format
+ // since it's the only one that's truly lossless.
pDecoder->internalFormat = mal_format_s32;
+ if (pConfig->format == mal_format_s16) {
+ pDecoder->internalFormat = mal_format_s16;
+ } else if (pConfig->format == mal_format_f32) {
+ pDecoder->internalFormat = mal_format_f32;
+ }
+
pDecoder->internalChannels = pFlac->channels;
pDecoder->internalSampleRate = pFlac->sampleRate;
mal_get_standard_channel_map(mal_standard_channel_map_flac, pDecoder->internalChannels, pDecoder->internalChannelMap);
@@ -27646,7 +28695,7 @@ mal_result mal_decoder_internal_on_seek_to_frame__mp3(mal_decoder* pDecoder, mal
drmp3* pMP3 = (drmp3*)pDecoder->pInternalDecoder;
mal_assert(pMP3 != NULL);
- drmp3_bool32 result = drmp3_seek_to_frame(pMP3, frameIndex);
+ drmp3_bool32 result = drmp3_seek_to_pcm_frame(pMP3, frameIndex);
if (result) {
return MAL_SUCCESS;
} else {
@@ -27672,7 +28721,7 @@ mal_uint32 mal_decoder_internal_on_read_frames__mp3(mal_dsp* pDSP, mal_uint32 fr
drmp3* pMP3 = (drmp3*)pDecoder->pInternalDecoder;
mal_assert(pMP3 != NULL);
- return (mal_uint32)drmp3_read_f32(pMP3, frameCount, (float*)pSamplesOut);
+ return (mal_uint32)drmp3_read_pcm_frames_f32(pMP3, frameCount, (float*)pSamplesOut);
}
mal_result mal_decoder_init_mp3__internal(const mal_decoder_config* pConfig, mal_decoder* pDecoder)
@@ -28556,6 +29605,28 @@ mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount
// REVISION HISTORY
// ================
//
+// v0.8.15 - 201x-xx-xx
+// - Add Web Audio backend. This is used when compiling with Emscripten. The SDL backend, which was previously
+// used for web support, will be removed in a future version.
+// - Add AAudio backend (Android Audio). This is the new priority backend for Android. Support for AAudio starts
+// with Android 8. OpenSL|ES is used as a fallback for older versions of Android.
+// - Deprecate the OpenAL backend.
+// - Deprecate the SDL backend.
+//
+// v0.8.14 - 2018-12-16
+// - Core Audio: Fix a bug where the device state is not set correctly after stopping.
+// - Add support for custom weights to the channel router.
+// - Update decoders to use updated APIs in dr_flac, dr_mp3 and dr_wav.
+//
+// v0.8.13 - 2018-12-04
+// - Core Audio: Fix a bug with channel mapping.
+// - Fix a bug with channel routing where the back/left and back/right channels have the wrong weight.
+//
+// v0.8.12 - 2018-11-27
+// - Drop support for SDL 1.2. The Emscripten build now requires "-s USE_SDL=2".
+// - Fix a linking error with ALSA.
+// - Fix a bug on iOS where the device name is not set correctly.
+//
// v0.8.11 - 2018-11-21
// - iOS bug fixes.
// - Minor tweaks to PulseAudio.