shithub: cstory

Download patch

ref: 4c8598d460bc7a07b9193954a51054a68e0e52f8
parent: 9e6d144b6790a30c678bef9b46fa5f86578140dc
author: Clownacy <Clownacy@users.noreply.github.com>
date: Mon Aug 31 16:56:38 EDT 2020

Update miniaudio from v0.10.9 to v0.10.18

--- a/external/miniaudio.h
+++ b/external/miniaudio.h
@@ -1,201 +1,25 @@
 /*
 Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
-miniaudio - v0.10.9 - 2020-06-24
+miniaudio - v0.10.18 - 2020-08-30
 
-David Reid - davidreidsoftware@gmail.com
+David Reid - mackron@gmail.com
 
-Website: https://miniaud.io
-GitHub:  https://github.com/dr-soft/miniaudio
+Website:       https://miniaud.io
+Documentation: https://miniaud.io/docs
+GitHub:        https://github.com/mackron/miniaudio
 */
 
 /*
-RELEASE NOTES - VERSION 0.10.x
-==============================
-Version 0.10 includes major API changes and refactoring, mostly concerned with the data conversion system. Data conversion is performed internally to convert
-audio data between the format requested when initializing the `ma_device` object and the format of the internal device used by the backend. The same applies
-to the `ma_decoder` object. The previous design has several design flaws and missing features which necessitated a complete redesign.
-
-
-Changes to Data Conversion
---------------------------
-The previous data conversion system used callbacks to deliver input data for conversion. This design works well in some specific situations, but in other
-situations it has some major readability and maintenance issues. The decision was made to replace this with a more iterative approach where you just pass in a
-pointer to the input data directly rather than dealing with a callback.
-
-The following are the data conversion APIs that have been removed and their replacements:
-
-  - ma_format_converter -> ma_convert_pcm_frames_format()
-  - ma_channel_router   -> ma_channel_converter
-  - ma_src              -> ma_resampler
-  - ma_pcm_converter    -> ma_data_converter
-
-The previous conversion APIs accepted a callback in their configs. There are no longer any callbacks to deal with. Instead you just pass the data into the
-`*_process_pcm_frames()` function as a pointer to a buffer.
-
-The simplest aspect of data conversion is sample format conversion. To convert between two formats, just call `ma_convert_pcm_frames_format()`. Channel
-conversion is also simple which you can do with `ma_channel_converter` via `ma_channel_converter_process_pcm_frames()`.
-
-Resampling is more complicated because the number of output frames that are processed is different to the number of input frames that are consumed. When you
-call `ma_resampler_process_pcm_frames()` you need to pass in the number of input frames available for processing and the number of output frames you want to
-output. Upon returning they will receive the number of input frames that were consumed and the number of output frames that were generated.
-
-The `ma_data_converter` API is a wrapper around format, channel and sample rate conversion and handles all of the data conversion you'll need which probably
-makes it the best option if you need to do data conversion.
-
-In addition to changes to the API design, a few other changes have been made to the data conversion pipeline:
-
-  - The sinc resampler has been removed. This was completely broken and never actually worked properly.
-  - The linear resampler now uses low-pass filtering to remove aliasing. The quality of the low-pass filter can be controlled via the resampler config with the
-    `lpfOrder` option, which has a maximum value of MA_MAX_FILTER_ORDER.
-  - Data conversion now supports s16 natively which runs through a fixed point pipeline. Previously everything needed to be converted to floating point before
-    processing, whereas now both s16 and f32 are natively supported. Other formats still require conversion to either s16 or f32 prior to processing, however
-    `ma_data_converter` will handle this for you.
-
-
-Custom Memory Allocators
-------------------------
-miniaudio has always supported macro level customization for memory allocation via MA_MALLOC, MA_REALLOC and MA_FREE, however some scenarios require more
-flexibility by allowing a user data pointer to be passed to the custom allocation routines. Support for this has been added to version 0.10 via the
-`ma_allocation_callbacks` structure. Anything making use of heap allocations has been updated to accept this new structure.
-
-The `ma_context_config` structure has been updated with a new member called `allocationCallbacks`. Leaving this set to it's defaults returned by
-`ma_context_config_init()` will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. Likewise, The `ma_decoder_config` structure has been updated in the same
-way, and leaving everything as-is after `ma_decoder_config_init()` will cause it to use the same defaults.
-
-The following APIs have been updated to take a pointer to a `ma_allocation_callbacks` object. Setting this parameter to NULL will cause it to use defaults.
-Otherwise they will use the relevant callback in the structure.
-
-  - ma_malloc()
-  - ma_realloc()
-  - ma_free()
-  - ma_aligned_malloc()
-  - ma_aligned_free()
-  - ma_rb_init() / ma_rb_init_ex()
-  - ma_pcm_rb_init() / ma_pcm_rb_init_ex()
-
-Note that you can continue to use MA_MALLOC, MA_REALLOC and MA_FREE as per normal. These will continue to be used by default if you do not specify custom
-allocation callbacks.
-
-
-Buffer and Period Configuration Changes
----------------------------------------
-The way in which the size of the internal buffer and periods are specified in the device configuration have changed. In previous versions, the config variables
-`bufferSizeInFrames` and `bufferSizeInMilliseconds` defined the size of the entire buffer, with the size of a period being the size of this variable divided by
-the period count. This became confusing because people would expect the value of `bufferSizeInFrames` or `bufferSizeInMilliseconds` to independantly determine
-latency, when in fact it was that value divided by the period count that determined it. These variables have been removed and replaced with new ones called
-`periodSizeInFrames` and `periodSizeInMilliseconds`.
-
-These new configuration variables work in the same way as their predecessors in that if one is set to 0, the other will be used, but the main difference is
-that you now set these to you desired latency rather than the size of the entire buffer. The benefit of this is that it's much easier and less confusing to
-configure latency.
-
-The following unused APIs have been removed:
-
-    ma_get_default_buffer_size_in_milliseconds()
-    ma_get_default_buffer_size_in_frames()
-
-The following macros have been removed:
-
-    MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY
-    MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE
-
-
-Other API Changes
------------------
-Other less major API changes have also been made in version 0.10.
-
-`ma_device_set_stop_callback()` has been removed. If you require a stop callback, you must now set it via the device config just like the data callback.
-
-The `ma_sine_wave` API has been replaced with a more general API called `ma_waveform`. This supports generation of different types of waveforms, including
-sine, square, triangle and sawtooth. Use `ma_waveform_init()` in place of `ma_sine_wave_init()` to initialize the waveform object. This takes a configuration
-object called `ma_waveform_config` which defines the properties of the waveform. Use `ma_waveform_config_init()` to initialize a `ma_waveform_config` object.
-Use `ma_waveform_read_pcm_frames()` in place of `ma_sine_wave_read_f32()` and `ma_sine_wave_read_f32_ex()`.
-
-`ma_convert_frames()` and `ma_convert_frames_ex()` have been changed. Both of these functions now take a new parameter called `frameCountOut` which specifies
-the size of the output buffer in PCM frames. This has been added for safety. In addition to this, the parameters for `ma_convert_frames_ex()` have changed to
-take a pointer to a `ma_data_converter_config` object to specify the input and output formats to convert between. This was done to make it more flexible, to
-prevent the parameter list getting too long, and to prevent API breakage whenever a new conversion property is added.
-
-`ma_calculate_frame_count_after_src()` has been renamed to `ma_calculate_frame_count_after_resampling()` for consistency with the new `ma_resampler` API.
-
-
-Filters
--------
-The following filters have been added:
-
-    |-------------|-------------------------------------------------------------------|
-    | API         | Description                                                       |
-    |-------------|-------------------------------------------------------------------|
-    | ma_biquad   | Biquad filter (transposed direct form 2)                          |
-    | ma_lpf1     | First order low-pass filter                                       |
-    | ma_lpf2     | Second order low-pass filter                                      |
-    | ma_lpf      | High order low-pass filter (Butterworth)                          |
-    | ma_hpf1     | First order high-pass filter                                      |
-    | ma_hpf2     | Second order high-pass filter                                     |
-    | ma_hpf      | High order high-pass filter (Butterworth)                         |
-    | ma_bpf2     | Second order band-pass filter                                     |
-    | ma_bpf      | High order band-pass filter                                       |
-    | ma_peak2    | Second order peaking filter                                       |
-    | ma_notch2   | Second order notching filter                                      |
-    | ma_loshelf2 | Second order low shelf filter                                     |
-    | ma_hishelf2 | Second order high shelf filter                                    |
-    |-------------|-------------------------------------------------------------------|
-
-These filters all support 32-bit floating point and 16-bit signed integer formats natively. Other formats need to be converted beforehand.
-
-
-Sine, Square, Triangle and Sawtooth Waveforms
----------------------------------------------
-Previously miniaudio supported only sine wave generation. This has now been generalized to support sine, square, triangle and sawtooth waveforms. The old
-`ma_sine_wave` API has been removed and replaced with the `ma_waveform` API. Use `ma_waveform_config_init()` to initialize a config object, and then pass it
-into `ma_waveform_init()`. Then use `ma_waveform_read_pcm_frames()` to read PCM data.
-
-
-Noise Generation
-----------------
-A noise generation API has been added. This is used via the `ma_noise` API. Currently white, pink and Brownian noise is supported. The `ma_noise` API is
-similar to the waveform API. Use `ma_noise_config_init()` to initialize a config object, and then pass it into `ma_noise_init()` to initialize a `ma_noise`
-object. Then use `ma_noise_read_pcm_frames()` to read PCM data.
-
-
-Miscellaneous Changes
----------------------
-The MA_NO_STDIO option has been removed. This would disable file I/O APIs, however this has proven to be too hard to maintain for it's perceived value and was
-therefore removed.
-
-Internal functions have all been made static where possible. If you get warnings about unused functions, please submit a bug report.
-
-The `ma_device` structure is no longer defined as being aligned to MA_SIMD_ALIGNMENT. This resulted in a possible crash when allocating a `ma_device` object on
-the heap, but not aligning it to MA_SIMD_ALIGNMENT. This crash would happen due to the compiler seeing the alignment specified on the structure and assuming it
-was always aligned as such and thinking it was safe to emit alignment-dependant SIMD instructions. Since miniaudio's philosophy is for things to just work,
-this has been removed from all structures.
-
-Results codes have been overhauled. Unnecessary result codes have been removed, and some have been renumbered for organisation purposes. If you are are binding
-maintainer you will need to update your result codes. Support has also been added for retrieving a human readable description of a given result code via the
-`ma_result_description()` API.
-
-ALSA: The automatic format conversion, channel conversion and resampling performed by ALSA is now disabled by default as they were causing some compatibility
-issues with certain devices and configurations. These can be individually enabled via the device config:
-
-    ```c
-    deviceConfig.alsa.noAutoFormat   = MA_TRUE;
-    deviceConfig.alsa.noAutoChannels = MA_TRUE;
-    deviceConfig.alsa.noAutoResample = MA_TRUE;
-    ```
-*/
-
-
-/*
-Introduction
-============
+1. Introduction
+===============
 miniaudio is a single file library for audio playback and capture. To use it, do the following in one .c file:
 
     ```c
     #define MINIAUDIO_IMPLEMENTATION
-    #include "miniaudio.h
+    #include "miniaudio.h"
     ```
 
-You can #include miniaudio.h in other parts of the program just like any other header.
+You can do `#include "miniaudio.h"` in other parts of the program just like any other header.
 
 miniaudio uses the concept of a "device" as the abstraction for physical devices. The idea is that you choose a physical device to emit or capture audio from,
 and then move data to/from the device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a callback which you specify when
@@ -211,29 +35,32 @@
     ```c
     void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
     {
-        // In playback mode copy data to pOutput. In capture mode read data from pInput. In full-duplex mode, both pOutput and pInput will be valid and you can
-        // move data from pInput into pOutput. Never process more than frameCount frames.
+        // In playback mode copy data to pOutput. In capture mode read data from pInput. In full-duplex mode, both
+        // pOutput and pInput will be valid and you can move data from pInput into pOutput. Never process more than
+        // frameCount frames.
     }
 
-    ...
+    int main()
+    {
+        ma_device_config config = ma_device_config_init(ma_device_type_playback);
+        config.playback.format   = ma_format_f32;   // Set to ma_format_unknown to use the device's native format.
+        config.playback.channels = 2;               // Set to 0 to use the device's native channel count.
+        config.sampleRate        = 48000;           // Set to 0 to use the device's native sample rate.
+        config.dataCallback      = data_callback;   // This function will be called when miniaudio needs more data.
+        config.pUserData         = pMyCustomData;   // Can be accessed from the device object (device.pUserData).
 
-    ma_device_config config = ma_device_config_init(ma_device_type_playback);
-    config.playback.format   = MY_FORMAT;
-    config.playback.channels = MY_CHANNEL_COUNT;
-    config.sampleRate        = MY_SAMPLE_RATE;
-    config.dataCallback      = data_callback;
-    config.pUserData         = pMyCustomData;   // Can be accessed from the device object (device.pUserData).
+        ma_device device;
+        if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) {
+            return -1;  // Failed to initialize the device.
+        }
 
-    ma_device device;
-    if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) {
-        ... An error occurred ...
-    }
+        ma_device_start(&device);     // The device is sleeping by default so you'll need to start it manually.
 
-    ma_device_start(&device);     // The device is sleeping by default so you'll need to start it manually.
+        // Do something here. Probably your program's main loop.
 
-    ...
-
-    ma_device_uninit(&device);    // This will stop the device so no need to do that manually.
+        ma_device_uninit(&device);    // This will stop the device so no need to do that manually.
+        return 0;
+    }
     ```
 
 In the example above, `data_callback()` is where audio data is written and read from the device. The idea is in playback mode you cause sound to be emitted
@@ -251,15 +78,15 @@
 takes a single parameter, which is whether or not the device is a playback, capture, duplex or loopback device (loopback devices are not supported on all
 backends). The `config.playback.format` member sets the sample format which can be one of the following (all formats are native-endian):
 
-    |---------------|----------------------------------------|---------------------------|
+    +---------------+----------------------------------------+---------------------------+
     | Symbol        | Description                            | Range                     |
-    |---------------|----------------------------------------|---------------------------|
+    +---------------+----------------------------------------+---------------------------+
     | ma_format_f32 | 32-bit floating point                  | [-1, 1]                   |
     | ma_format_s16 | 16-bit signed integer                  | [-32768, 32767]           |
     | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607]       |
     | ma_format_s32 | 32-bit signed integer                  | [-2147483648, 2147483647] |
     | ma_format_u8  | 8-bit unsigned integer                 | [0, 255]                  |
-    |---------------|----------------------------------------|---------------------------|
+    +---------------+----------------------------------------+---------------------------+
 
 The `config.playback.channels` member sets the number of channels to use with the device. The channel count cannot exceed MA_MAX_CHANNELS. The
 `config.sampleRate` member sets the sample rate (which must be the same for both playback and capture in full-duplex configurations). This is usually set to
@@ -278,11 +105,13 @@
 Note that it's important to never stop or start the device from inside the callback. This will result in a deadlock. Instead you set a variable or signal an
 event indicating that the device needs to stop and handle it in a different thread. The following APIs must never be called inside the callback:
 
+    ```c
     ma_device_init()
     ma_device_init_ex()
     ma_device_uninit()
     ma_device_start()
     ma_device_stop()
+    ```
 
 You must never try uninitializing and reinitializing a device inside the callback. You must also never try to stop and start it from inside the callback. There
 are a few other things you shouldn't do in the callback depending on your requirements, however this isn't so much a thread-safety thing, but rather a real-
@@ -302,14 +131,14 @@
 
 These are the available device types and how you should handle the buffers in the callback:
 
-    |-------------------------|--------------------------------------------------------|
+    +-------------------------+--------------------------------------------------------+
     | Device Type             | Callback Behavior                                      |
-    |-------------------------|--------------------------------------------------------|
+    +-------------------------+--------------------------------------------------------+
     | ma_device_type_playback | Write to output buffer, leave input buffer untouched.  |
     | ma_device_type_capture  | Read from input buffer, leave output buffer untouched. |
     | ma_device_type_duplex   | Read from input buffer, write to output buffer.        |
     | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. |
-    |-------------------------|--------------------------------------------------------|
+    +-------------------------+--------------------------------------------------------+
 
 You will notice in the example above that the sample format and channel count is specified separately for playback and capture. This is to support different
 data formats between the playback and capture devices in a full-duplex system. An example may be that you want to capture audio data as a monaural stream (one
@@ -319,7 +148,7 @@
 The example above did not specify a physical device to connect to which means it will use the operating system's default device. If you have multiple physical
 devices connected and you want to use a specific one you will need to specify the device ID in the configuration, like so:
 
-    ```
+    ```c
     config.playback.pDeviceID = pMyPlaybackDeviceID;    // Only if requesting a playback or duplex device.
     config.capture.pDeviceID = pMyCaptureDeviceID;      // Only if requesting a capture, duplex or loopback device.
     ```
@@ -335,22 +164,22 @@
         // Error.
     }
 
-    ma_device_info* pPlaybackDeviceInfos;
-    ma_uint32 playbackDeviceCount;
-    ma_device_info* pCaptureDeviceInfos;
-    ma_uint32 captureDeviceCount;
-    if (ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, &pCaptureDeviceInfos, &captureDeviceCount) != MA_SUCCESS) {
+    ma_device_info* pPlaybackInfos;
+    ma_uint32 playbackCount;
+    ma_device_info* pCaptureInfos;
+    ma_uint32 captureCount;
+    if (ma_context_get_devices(&context, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount) != MA_SUCCESS) {
         // Error.
     }
 
-    // Loop over each device info and do something with it. Here we just print the name with their index. You may want to give the user the
-    // opportunity to choose which device they'd prefer.
-    for (ma_uint32 iDevice = 0; iDevice < playbackDeviceCount; iDevice += 1) {
-        printf("%d - %s\n", iDevice, pPlaybackDeviceInfos[iDevice].name);
+    // Loop over each device info and do something with it. Here we just print the name with their index. You may want
+    // to give the user the opportunity to choose which device they'd prefer.
+    for (ma_uint32 iDevice = 0; iDevice < playbackCount; iDevice += 1) {
+        printf("%d - %s\n", iDevice, pPlaybackInfos[iDevice].name);
     }
 
     ma_device_config config = ma_device_config_init(ma_device_type_playback);
-    config.playback.pDeviceID = &pPlaybackDeviceInfos[chosenPlaybackDeviceIndex].id;
+    config.playback.pDeviceID = &pPlaybackInfos[chosenPlaybackDeviceIndex].id;
     config.playback.format    = MY_FORMAT;
     config.playback.channels  = MY_CHANNEL_COUNT;
     config.sampleRate         = MY_SAMPLE_RATE;
@@ -389,213 +218,210 @@
 
 
 
-Building
-========
+2. Building
+===========
 miniaudio should work cleanly out of the box without the need to download or install any dependencies. See below for platform-specific details.
 
 
-Windows
--------
+2.1. Windows
+------------
 The Windows build should compile cleanly on all popular compilers without the need to configure any include paths nor link to any libraries.
 
-macOS and iOS
--------------
+2.2. macOS and iOS
+------------------
 The macOS build should compile cleanly without the need to download any dependencies nor 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. Compiling through the command line requires
-linking to -lpthread and -lm.
+linking to `-lpthread` and `-lm`.
 
-Linux
------
-The Linux build only requires linking to -ldl, -lpthread and -lm. You do not need any development packages.
+2.3. Linux
+----------
+The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any development packages.
 
-BSD
----
-The BSD build only requires linking to -lpthread and -lm. NetBSD uses audio(4), OpenBSD uses sndio and FreeBSD uses OSS.
+2.4. BSD
+--------
+The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses sndio and FreeBSD uses OSS.
 
-Android
--------
+2.5. Android
+------------
 AAudio is the highest priority backend on Android. This should work 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+.
 
-Emscripten
-----------
+2.6. Emscripten
+---------------
 The Emscripten build emits Web Audio JavaScript directly and should Just Work without any configuration. You cannot use -std=c* compiler flags, nor -ansi.
 
 
-Build Options
--------------
-#define these options before including miniaudio.h.
+2.7. Build Options
+------------------
+`#define` these options before including miniaudio.h.
 
-#define MA_NO_WASAPI
-  Disables the WASAPI backend.
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | Option               | Description                                                                                                                      |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_WASAPI         | Disables the WASAPI backend.                                                                                                     |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_DSOUND         | Disables the DirectSound backend.                                                                                                |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_WINMM          | Disables the WinMM backend.                                                                                                      |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_ALSA           | Disables the ALSA backend.                                                                                                       |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_PULSEAUDIO     | Disables the PulseAudio backend.                                                                                                 |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_JACK           | Disables the JACK backend.                                                                                                       |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_COREAUDIO      | Disables the Core Audio backend.                                                                                                 |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_SNDIO          | Disables the sndio backend.                                                                                                      |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_AUDIO4         | Disables the audio(4) backend.                                                                                                   |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_OSS            | Disables the OSS backend.                                                                                                        |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_AAUDIO         | Disables the AAudio backend.                                                                                                     |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_OPENSL         | Disables the OpenSL|ES backend.                                                                                                  |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_WEBAUDIO       | Disables the Web Audio backend.                                                                                                  |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_NULL           | Disables the null backend.                                                                                                       |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_DECODING       | Disables decoding APIs.                                                                                                          |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_ENCODING       | Disables encoding APIs.                                                                                                          |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_WAV            | Disables the built-in WAV decoder and encoder.                                                                                   |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_FLAC           | Disables the built-in FLAC decoder.                                                                                              |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_MP3            | Disables the built-in MP3 decoder.                                                                                               |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_DEVICE_IO      | Disables playback and recording. This will disable ma_context and ma_device APIs. This is useful if you only want to use         |
+    |                      | miniaudio's data conversion and/or decoding APIs.                                                                                |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_THREADING      | Disables the ma_thread, ma_mutex, ma_semaphore and ma_event APIs. This option is useful if you only need to use miniaudio for    |
+    |                      | data conversion, decoding and/or encoding. Some families of APIs require threading which means the following options must also   |
+    |                      | be set:                                                                                                                          |
+    |                      |                                                                                                                                  |
+    |                      |     ```                                                                                                                          |
+    |                      |     MA_NO_DEVICE_IO                                                                                                              |
+    |                      |     ```                                                                                                                          |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_GENERATION     | Disables generation APIs such a ma_waveform and ma_noise.                                                                        |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_SSE2           | Disables SSE2 optimizations.                                                                                                     |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_AVX2           | Disables AVX2 optimizations.                                                                                                     |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_AVX512         | Disables AVX-512 optimizations.                                                                                                  |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_NO_NEON           | Disables NEON optimizations.                                                                                                     |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_LOG_LEVEL [level] | Sets the logging level. Set level to one of the following:                                                                       |
+    |                      |                                                                                                                                  |
+    |                      |     ```                                                                                                                          |
+    |                      |     MA_LOG_LEVEL_VERBOSE                                                                                                         |
+    |                      |     MA_LOG_LEVEL_INFO                                                                                                            |
+    |                      |     MA_LOG_LEVEL_WARNING                                                                                                         |
+    |                      |     MA_LOG_LEVEL_ERROR                                                                                                           |
+    |                      |     ```                                                                                                                          |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_DEBUG_OUTPUT      | Enable printf() debug output.                                                                                                    |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_COINIT_VALUE      | Windows only. The value to pass to internal calls to `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`.                     |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_API               | Controls how public APIs should be decorated. Defaults to `extern`.                                                              |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
+    | MA_DLL               | If set, configures MA_API to either import or export APIs depending on whether or not the implementation is being defined. If    |
+    |                      | defining the implementation, MA_API will be configured to export. Otherwise it will be configured to import. This has no effect  |
+    |                      | if MA_API is defined externally.                                                                                                 |
+    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
 
-#define MA_NO_DSOUND
-  Disables the DirectSound backend.
 
-#define MA_NO_WINMM
-  Disables the WinMM backend.
-
-#define MA_NO_ALSA
-  Disables the ALSA backend.
-
-#define MA_NO_PULSEAUDIO
-  Disables the PulseAudio backend.
-
-#define MA_NO_JACK
-  Disables the JACK backend.
-
-#define MA_NO_COREAUDIO
-  Disables the Core Audio backend.
-
-#define MA_NO_SNDIO
-  Disables the sndio backend.
-
-#define MA_NO_AUDIO4
-  Disables the audio(4) backend.
-
-#define MA_NO_OSS
-  Disables the OSS backend.
-
-#define MA_NO_AAUDIO
-  Disables the AAudio backend.
-
-#define MA_NO_OPENSL
-  Disables the OpenSL|ES backend.
-
-#define MA_NO_WEBAUDIO
-  Disables the Web Audio backend.
-
-#define MA_NO_NULL
-  Disables the null backend.
-
-#define MA_NO_DECODING
-  Disables decoding APIs.
-
-#define MA_NO_ENCODING
-  Disables encoding APIs.
-
-#define MA_NO_WAV
-  Disables the built-in WAV decoder and encoder.
-
-#define MA_NO_FLAC
-  Disables the built-in FLAC decoder.
-
-#define MA_NO_MP3
-  Disables the built-in MP3 decoder.
-
-#define MA_NO_DEVICE_IO
-  Disables playback and recording. This will disable ma_context and ma_device APIs. This is useful if you only want to use miniaudio's data conversion and/or
-  decoding APIs.
-
-#define MA_NO_GENERATION
-  Disables generation APIs such a ma_waveform and ma_noise.
-
-#define MA_NO_SSE2
-  Disables SSE2 optimizations.
-
-#define MA_NO_AVX2
-  Disables AVX2 optimizations.
-
-#define MA_NO_AVX512
-  Disables AVX-512 optimizations.
-
-#define MA_NO_NEON
-  Disables NEON optimizations.
-
-#define MA_LOG_LEVEL <Level>
-  Sets the logging level. Set level to one of the following:
-    MA_LOG_LEVEL_VERBOSE
-    MA_LOG_LEVEL_INFO
-    MA_LOG_LEVEL_WARNING
-    MA_LOG_LEVEL_ERROR
-
-#define MA_DEBUG_OUTPUT
-  Enable printf() debug output.
-
-#define MA_COINIT_VALUE
-  Windows only. The value to pass to internal calls to CoInitializeEx(). Defaults to COINIT_MULTITHREADED.
-
-#define MA_API
-  Controls how public APIs should be decorated. Defaults to `extern`.
-
-#define MA_DLL
-  If set, configures MA_API to either import or export APIs depending on whether or not the implementation is being defined. If defining the implementation,
-  MA_API will be configured to export. Otherwise it will be configured to import. This has no effect if MA_API is defined externally.
-    
-
-
-
-Definitions
-===========
+3. Definitions
+==============
 This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity in the use of terms throughout the audio space, so this
 section is intended to clarify how miniaudio uses each term.
 
-Sample
-------
+3.1. Sample
+-----------
 A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit floating point number.
 
-Frame / PCM Frame
------------------
+3.2. Frame / PCM Frame
+----------------------
 A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2 samples, a mono frame is 1 sample, a 5.1 surround sound frame
 is 6 samples, etc. The terms "frame" and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame. If ever miniaudio
 needs to refer to a compressed frame, such as a FLAC frame, it will always clarify what it's referring to with something like "FLAC frame".
 
-Channel
--------
+3.3. Channel
+------------
 A stream of monaural audio that is emitted from an individual speaker in a speaker system, or received from an individual microphone in a microphone system. A
 stereo stream has two channels (a left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio systems refer to a channel as
 a complex audio stream that's mixed with other channels to produce the final mix - this is completely different to miniaudio's use of the term "channel" and
 should not be confused.
 
-Sample Rate
------------
+3.4. Sample Rate
+----------------
 The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number of PCM frames that are processed per second.
 
-Formats
--------
+3.5. Formats
+------------
 Throughout miniaudio you will see references to different sample formats:
 
-    |---------------|----------------------------------------|---------------------------|
+    +---------------+----------------------------------------+---------------------------+
     | Symbol        | Description                            | Range                     |
-    |---------------|----------------------------------------|---------------------------|
+    +---------------+----------------------------------------+---------------------------+
     | ma_format_f32 | 32-bit floating point                  | [-1, 1]                   |
     | ma_format_s16 | 16-bit signed integer                  | [-32768, 32767]           |
     | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607]       |
     | ma_format_s32 | 32-bit signed integer                  | [-2147483648, 2147483647] |
     | ma_format_u8  | 8-bit unsigned integer                 | [0, 255]                  |
-    |---------------|----------------------------------------|---------------------------|
+    +---------------+----------------------------------------+---------------------------+
 
 All formats are native-endian.
 
 
 
-Decoding
-========
-The `ma_decoder` API is used for reading audio files. To enable a decoder you must #include the header of the relevant backend library before the
-implementation of miniaudio. You can find copies of these in the "extras" folder in the miniaudio repository (https://github.com/dr-soft/miniaudio).
+4. Decoding
+===========
+The `ma_decoder` API is used for reading audio files. The following formats are supported:
 
-The table below are the supported decoding backends:
+    +---------+------------------+----------+
+    | Format  | Decoding Backend | Built-In |
+    +---------+------------------+----------+
+    | WAV     | dr_wav           | Yes      |
+    | MP3     | dr_mp3           | Yes      |
+    | FLAC    | dr_flac          | Yes      |
+    | Vorbis  | stb_vorbis       | No       |
+    +---------+------------------+----------+
 
-    |--------|-----------------|
-    | Type   | Backend Library |
-    |--------|-----------------|
-    | WAV    | dr_wav.h        |
-    | FLAC   | dr_flac.h       |
-    | MP3    | dr_mp3.h        |
-    | Vorbis | stb_vorbis.c    |
-    |--------|-----------------|
+Vorbis is supported via stb_vorbis which can be enabled by including the header section before the implementation of miniaudio, like the following:
 
-The code below is an example of how to enable decoding backends:
-
     ```c
-    #include "dr_flac.h"    // Enables FLAC decoding.
-    #include "dr_mp3.h"     // Enables MP3 decoding.
-    #include "dr_wav.h"     // Enables WAV decoding.
+    #define STB_VORBIS_HEADER_ONLY
+    #include "extras/stb_vorbis.c"    // Enables Vorbis decoding.
 
     #define MINIAUDIO_IMPLEMENTATION
     #include "miniaudio.h"
+
+    // The stb_vorbis implementation must come after the implementation of miniaudio.
+    #undef STB_VORBIS_HEADER_ONLY
+    #include "extras/stb_vorbis.c"
     ```
 
+A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/mackron/miniaudio).
+
+Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the built-in decoders by specifying one or more of the
+following options before the miniaudio implementation:
+
+    ```c
+    #define MA_NO_WAV
+    #define MA_NO_MP3
+    #define MA_NO_FLAC
+    ```
+
+Disabling built-in decoding libraries is useful if you use these libraries independantly of the `ma_decoder` API.
+
 A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with `ma_decoder_init_memory()`, or from data delivered via callbacks
 with `ma_decoder_init()`. Here is an example for loading a decoder from a file:
 
@@ -662,26 +488,13 @@
 
 
 
-Encoding
-========
-The `ma_encoding` API is used for writing audio files. To enable an encoder you must #include the header of the relevant backend library before the
-implementation of miniaudio. You can find copies of these in the "extras" folder in the miniaudio repository (https://github.com/dr-soft/miniaudio).
+5. Encoding
+===========
+The `ma_encoding` API is used for writing audio files. The only supported output format is WAV which is achieved via dr_wav which is amalgamated into the
+implementation section of miniaudio. This can be disabled by specifying the following option before the implementation of miniaudio:
 
-The table below are the supported encoding backends:
-
-    |--------|-----------------|
-    | Type   | Backend Library |
-    |--------|-----------------|
-    | WAV    | dr_wav.h        |
-    |--------|-----------------|
-
-The code below is an example of how to enable encoding backends:
-
     ```c
-    #include "dr_wav.h"     // Enables WAV encoding.
-
-    #define MINIAUDIO_IMPLEMENTATION
-    #include "miniaudio.h"
+    #define MA_NO_WAV
     ```
 
 An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data delivered via callbacks with `ma_encoder_init()`. Below is an
@@ -703,11 +516,11 @@
 When initializing an encoder you must specify a config which is initialized with `ma_encoder_config_init()`. Here you must specify the file type, the output
 sample format, output channel count and output sample rate. The following file types are supported:
 
-    |------------------------|-------------|
+    +------------------------+-------------+
     | Enum                   | Description |
-    |------------------------|-------------|
+    +------------------------+-------------+
     | ma_resource_format_wav | WAV         |
-    |------------------------|-------------|
+    +------------------------+-------------+
 
 If the format, channel count or sample rate is not supported by the output file type an error will be returned. The encoder will not perform data conversion so
 you will need to convert it before outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the example below:
@@ -719,30 +532,37 @@
 Encoders must be uninitialized with `ma_encoder_uninit()`.
 
 
+6. Data Conversion
+==================
+A data conversion API is included with miniaudio which supports the majority of data conversion requirements. This supports conversion between sample formats,
+channel counts (with channel mapping) and sample rates.
 
-Sample Format Conversion
-========================
+
+6.1. Sample Format Conversion
+-----------------------------
 Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and `ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()`
 to convert between two specific formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use `ma_convert_pcm_frames_format()` to convert
 PCM frames where you want to specify the frame count and channel count as a variable instead of the total sample count.
 
-Dithering
----------
+
+6.1.1. Dithering
+----------------
 Dithering can be set using the ditherMode parameter.
 
 The different dithering modes include the following, in order of efficiency:
 
-    |-----------|--------------------------|
+    +-----------+--------------------------+
     | Type      | Enum Token               |
-    |-----------|--------------------------|
+    +-----------+--------------------------+
     | None      | ma_dither_mode_none      |
     | Rectangle | ma_dither_mode_rectangle |
     | Triangle  | ma_dither_mode_triangle  |
-    |-----------|--------------------------|
+    +-----------+--------------------------+
 
 Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be ignored for conversions where dithering is not needed.
 Dithering is available for the following conversions:
 
+    ```
     s16 -> u8
     s24 -> u8
     s32 -> u8
@@ -750,18 +570,26 @@
     s24 -> s16
     s32 -> s16
     f32 -> s16
+    ```
 
 Note that it is not an error to pass something other than ma_dither_mode_none for conversions where dither is not used. It will just be ignored.
 
 
 
-Channel Conversion
-==================
+6.2. Channel Conversion
+-----------------------
 Channel conversion is used for channel rearrangement and conversion from one channel count to another. The `ma_channel_converter` API is used for channel
 conversion. Below is an example of initializing a simple channel converter which converts from mono to stereo.
 
     ```c
-    ma_channel_converter_config config = ma_channel_converter_config_init(ma_format, 1, NULL, 2, NULL, ma_channel_mix_mode_default, NULL);
+    ma_channel_converter_config config = ma_channel_converter_config_init(
+        ma_format,                      // Sample format
+        1,                              // Input channels
+        NULL,                           // Input channel map
+        2,                              // Output channels
+        NULL,                           // Output channel map
+        ma_channel_mix_mode_default);   // The mixing algorithm to use when combining channels.
+
     result = ma_channel_converter_init(&config, &converter);
     if (result != MA_SUCCESS) {
         // Error.
@@ -779,15 +607,12 @@
 
 It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM frames.
 
-The only formats supported are `ma_format_s16` and `ma_format_f32`. If you need another format you need to convert your data manually which you can do with
-`ma_pcm_convert()`, etc.
-
 Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported.
 
 
-Channel Mapping
----------------
-In addition to converting from one channel count to another, like the example above, The channel converter can also be used to rearrange channels. When
+6.2.1. Channel Mapping
+----------------------
+In addition to converting from one channel count to another, like the example above, the channel converter can also be used to rearrange channels. When
 initializing the channel converter, you can optionally pass in channel maps for both the input and output frames. If the channel counts are the same, and each
 channel map contains the same channel positions with the exception that they're in a different order, a simple shuffling of the channels will be performed. If,
 however, there is not a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed based on a mixing mode which is
@@ -809,9 +634,9 @@
 Predefined channel maps can be retrieved with `ma_get_standard_channel_map()`. This takes a `ma_standard_channel_map` enum as it's first parameter, which can
 be one of the following:
 
-    |-----------------------------------|-----------------------------------------------------------|
+    +-----------------------------------+-----------------------------------------------------------+
     | Name                              | Description                                               |
-    |-----------------------------------|-----------------------------------------------------------|
+    +-----------------------------------+-----------------------------------------------------------+
     | ma_standard_channel_map_default   | Default channel map used by miniaudio. See below.         |
     | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps.    |
     | ma_standard_channel_map_alsa      | Default ALSA channel map.                                 |
@@ -819,72 +644,78 @@
     | ma_standard_channel_map_flac      | FLAC channel map.                                         |
     | ma_standard_channel_map_vorbis    | Vorbis channel map.                                       |
     | ma_standard_channel_map_sound4    | FreeBSD's sound(4).                                       |
-    | ma_standard_channel_map_sndio     | sndio channel map. www.sndio.org/tips.html                |
+    | ma_standard_channel_map_sndio     | sndio channel map. http://www.sndio.org/tips.html.        |
     | ma_standard_channel_map_webaudio  | https://webaudio.github.io/web-audio-api/#ChannelOrdering | 
-    |-----------------------------------|-----------------------------------------------------------|
+    +-----------------------------------+-----------------------------------------------------------+
 
 Below are the channel maps used by default in miniaudio (ma_standard_channel_map_default):
 
-    |---------------|------------------------------|
-    | Channel Count | Mapping                      |
-    |---------------|------------------------------|
-    | 1 (Mono)      | 0: MA_CHANNEL_MONO           |
-    |---------------|------------------------------|
-    | 2 (Stereo)    | 0: MA_CHANNEL_FRONT_LEFT     |
-    |               | 1: MA_CHANNEL_FRONT_RIGHT    |
-    |---------------|------------------------------|
-    | 3             | 0: MA_CHANNEL_FRONT_LEFT     |
-    |               | 1: MA_CHANNEL_FRONT_RIGHT    |
-    |               | 2: MA_CHANNEL_FRONT_CENTER   |
-    |---------------|------------------------------|
-    | 4 (Surround)  | 0: MA_CHANNEL_FRONT_LEFT     |
-    |               | 1: MA_CHANNEL_FRONT_RIGHT    |
-    |               | 2: MA_CHANNEL_FRONT_CENTER   |
-    |               | 3: MA_CHANNEL_BACK_CENTER    |
-    |---------------|------------------------------|
-    | 5             | 0: MA_CHANNEL_FRONT_LEFT     |
-    |               | 1: MA_CHANNEL_FRONT_RIGHT    |
-    |               | 2: MA_CHANNEL_FRONT_CENTER   |
-    |               | 3: MA_CHANNEL_BACK_LEFT      |
-    |               | 4: MA_CHANNEL_BACK_RIGHT     |
-    |---------------|------------------------------|
-    | 6 (5.1)       | 0: MA_CHANNEL_FRONT_LEFT     |
-    |               | 1: MA_CHANNEL_FRONT_RIGHT    |
-    |               | 2: MA_CHANNEL_FRONT_CENTER   |
-    |               | 3: MA_CHANNEL_LFE            |
-    |               | 4: MA_CHANNEL_SIDE_LEFT      |
-    |               | 5: MA_CHANNEL_SIDE_RIGHT     |
-    |---------------|------------------------------|
-    | 7             | 0: MA_CHANNEL_FRONT_LEFT     |
-    |               | 1: MA_CHANNEL_FRONT_RIGHT    |
-    |               | 2: MA_CHANNEL_FRONT_CENTER   |
-    |               | 3: MA_CHANNEL_LFE            |
-    |               | 4: MA_CHANNEL_BACK_CENTER    |
-    |               | 4: MA_CHANNEL_SIDE_LEFT      |
-    |               | 5: MA_CHANNEL_SIDE_RIGHT     |
-    |---------------|------------------------------|
-    | 8 (7.1)       | 0: MA_CHANNEL_FRONT_LEFT     |
-    |               | 1: MA_CHANNEL_FRONT_RIGHT    |
-    |               | 2: MA_CHANNEL_FRONT_CENTER   |
-    |               | 3: MA_CHANNEL_LFE            |
-    |               | 4: MA_CHANNEL_BACK_LEFT      |
-    |               | 5: MA_CHANNEL_BACK_RIGHT     |
-    |               | 6: MA_CHANNEL_SIDE_LEFT      |
-    |               | 7: MA_CHANNEL_SIDE_RIGHT     |
-    |---------------|------------------------------|
-    | Other         | All channels set to 0. This  |
-    |               | is equivalent to the same    |
-    |               | mapping as the device.       |
-    |---------------|------------------------------|
+    +---------------+---------------------------------+
+    | Channel Count | Mapping                         |
+    +---------------+---------------------------------+
+    | 1 (Mono)      | 0: MA_CHANNEL_MONO              |
+    +---------------+---------------------------------+
+    | 2 (Stereo)    | 0: MA_CHANNEL_FRONT_LEFT   <br> |
+    |               | 1: MA_CHANNEL_FRONT_RIGHT       |
+    +---------------+---------------------------------+
+    | 3             | 0: MA_CHANNEL_FRONT_LEFT   <br> |
+    |               | 1: MA_CHANNEL_FRONT_RIGHT  <br> |
+    |               | 2: MA_CHANNEL_FRONT_CENTER      |
+    +---------------+---------------------------------+
+    | 4 (Surround)  | 0: MA_CHANNEL_FRONT_LEFT   <br> |
+    |               | 1: MA_CHANNEL_FRONT_RIGHT  <br> |
+    |               | 2: MA_CHANNEL_FRONT_CENTER <br> |
+    |               | 3: MA_CHANNEL_BACK_CENTER       |
+    +---------------+---------------------------------+
+    | 5             | 0: MA_CHANNEL_FRONT_LEFT   <br> |
+    |               | 1: MA_CHANNEL_FRONT_RIGHT  <br> |
+    |               | 2: MA_CHANNEL_FRONT_CENTER <br> |
+    |               | 3: MA_CHANNEL_BACK_LEFT    <br> |
+    |               | 4: MA_CHANNEL_BACK_RIGHT        |
+    +---------------+---------------------------------+
+    | 6 (5.1)       | 0: MA_CHANNEL_FRONT_LEFT   <br> |
+    |               | 1: MA_CHANNEL_FRONT_RIGHT  <br> |
+    |               | 2: MA_CHANNEL_FRONT_CENTER <br> |
+    |               | 3: MA_CHANNEL_LFE          <br> |
+    |               | 4: MA_CHANNEL_SIDE_LEFT    <br> |
+    |               | 5: MA_CHANNEL_SIDE_RIGHT        |
+    +---------------+---------------------------------+
+    | 7             | 0: MA_CHANNEL_FRONT_LEFT   <br> |
+    |               | 1: MA_CHANNEL_FRONT_RIGHT  <br> |
+    |               | 2: MA_CHANNEL_FRONT_CENTER <br> |
+    |               | 3: MA_CHANNEL_LFE          <br> |
+    |               | 4: MA_CHANNEL_BACK_CENTER  <br> |
+    |               | 4: MA_CHANNEL_SIDE_LEFT    <br> |
+    |               | 5: MA_CHANNEL_SIDE_RIGHT        |
+    +---------------+---------------------------------+
+    | 8 (7.1)       | 0: MA_CHANNEL_FRONT_LEFT   <br> |
+    |               | 1: MA_CHANNEL_FRONT_RIGHT  <br> |
+    |               | 2: MA_CHANNEL_FRONT_CENTER <br> |
+    |               | 3: MA_CHANNEL_LFE          <br> |
+    |               | 4: MA_CHANNEL_BACK_LEFT    <br> |
+    |               | 5: MA_CHANNEL_BACK_RIGHT   <br> |
+    |               | 6: MA_CHANNEL_SIDE_LEFT    <br> |
+    |               | 7: MA_CHANNEL_SIDE_RIGHT        |
+    +---------------+---------------------------------+
+    | Other         | All channels set to 0. This     |
+    |               | is equivalent to the same       |
+    |               | mapping as the device.          |
+    +---------------+---------------------------------+
 
 
 
-Resampling
-==========
+6.3. Resampling
+---------------
 Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something like the following:
 
     ```c
-    ma_resampler_config config = ma_resampler_config_init(ma_format_s16, channels, sampleRateIn, sampleRateOut, ma_resample_algorithm_linear);
+    ma_resampler_config config = ma_resampler_config_init(
+        ma_format_s16,
+        channels,
+        sampleRateIn,
+        sampleRateOut,
+        ma_resample_algorithm_linear);
+
     ma_resampler resampler;
     ma_result result = ma_resampler_init(&config, &resampler);
     if (result != MA_SUCCESS) {
@@ -908,7 +739,8 @@
         // An error occurred...
     }
 
-    // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number of output frames written.
+    // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the
+    // number of output frames written.
     ```
 
 To initialize the resampler you first need to set up a config (`ma_resampler_config`) with `ma_resampler_config_init()`. You need to specify the sample format
@@ -924,12 +756,12 @@
 
 The miniaudio resampler supports multiple algorithms:
 
-    |-----------|------------------------------|
+    +-----------+------------------------------+
     | Algorithm | Enum Token                   |
-    |-----------|------------------------------|
+    +-----------+------------------------------+
     | Linear    | ma_resample_algorithm_linear |
     | Speex     | ma_resample_algorithm_speex  |
-    |-----------|------------------------------|
+    +-----------+------------------------------+
 
 Because Speex is not public domain it is strictly opt-in and the code is stored in separate files. if you opt-in to the Speex backend you will need to consider
 it's license, the text of which can be found in it's source files in "extras/speex_resampler". Details on how to opt-in to the Speex resampler is explained in
@@ -954,15 +786,15 @@
 with `ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`.
 
 
-Resampling Algorithms
----------------------
+6.3.1. Resampling Algorithms
+----------------------------
 The choice of resampling algorithm depends on your situation and requirements. The linear resampler is the most efficient and has the least amount of latency,
 but at the expense of poorer quality. The Speex resampler is higher quality, but slower with more latency. It also performs several heap allocations internally
 for memory management.
 
 
-Linear Resampling
------------------
+6.3.1.1. Linear Resampling
+--------------------------
 The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however, some control over the quality of the linear resampler which
 may make it a suitable option depending on your requirements.
 
@@ -979,18 +811,22 @@
 The API for the linear resampler is the same as the main resampler API, only it's called `ma_linear_resampler`.
 
 
-Speex Resampling
-----------------
+6.3.1.2. Speex Resampling
+-------------------------
 The Speex resampler is made up of third party code which is released under the BSD license. Because it is licensed differently to miniaudio, which is public
 domain, it is strictly opt-in and all of it's code is stored in separate files. If you opt-in to the Speex resampler you must consider the license text in it's
 source files. To opt-in, you must first #include the following file before the implementation of miniaudio.h:
 
+    ```c
     #include "extras/speex_resampler/ma_speex_resampler.h"
+    ```
 
 Both the header and implementation is contained within the same file. The implementation can be included in your program like so:
 
+    ```c
     #define MINIAUDIO_SPEEX_RESAMPLER_IMPLEMENTATION
     #include "extras/speex_resampler/ma_speex_resampler.h"
+    ```
 
 Note that even if you opt-in to the Speex backend, miniaudio won't use it unless you explicitly ask for it in the respective config of the object you are
 initializing. If you try to use the Speex resampler without opting in, initialization of the `ma_resampler` object will fail with `MA_NO_BACKEND`.
@@ -1000,14 +836,22 @@
 
 
 
-General Data Conversion
-=======================
+6.4. General Data Conversion
+----------------------------
 The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and resampling into one operation. This is what miniaudio uses
 internally to convert between the format requested when the device was initialized and the format of the backend's native device. The API for general data
 conversion is very similar to the resampling API. Create a `ma_data_converter` object like this:
 
     ```c
-    ma_data_converter_config config = ma_data_converter_config_init(inputFormat, outputFormat, inputChannels, outputChannels, inputSampleRate, outputSampleRate);
+    ma_data_converter_config config = ma_data_converter_config_init(
+        inputFormat,
+        outputFormat,
+        inputChannels,
+        outputChannels,
+        inputSampleRate,
+        outputSampleRate
+    );
+
     ma_data_converter converter;
     ma_result result = ma_data_converter_init(&config, &converter);
     if (result != MA_SUCCESS) {
@@ -1046,7 +890,8 @@
         // An error occurred...
     }
 
-    // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number of output frames written.
+    // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number
+    // of output frames written.
     ```
 
 The data converter supports multiple channels and is always interleaved (both input and output). The channel count cannot be changed after initialization.
@@ -1071,11 +916,11 @@
 
 
 
-Filtering
-=========
+7. Filtering
+============
 
-Biquad Filtering
-----------------
+7.1. Biquad Filtering
+---------------------
 Biquad filtering is achieved with the `ma_biquad` API. Example:
 
     ```c
@@ -1110,17 +955,17 @@
 result in an error.
 
 
-Low-Pass Filtering
-------------------
+7.2. Low-Pass Filtering
+-----------------------
 Low-pass filtering is achieved with the following APIs:
 
-    |---------|------------------------------------------|
+    +---------+------------------------------------------+
     | API     | Description                              |
-    |---------|------------------------------------------|
+    +---------+------------------------------------------+
     | ma_lpf1 | First order low-pass filter              |
     | ma_lpf2 | Second order low-pass filter             |
     | ma_lpf  | High order low-pass filter (Butterworth) |
-    |---------|------------------------------------------|
+    +---------+------------------------------------------+
 
 Low-pass filter example:
 
@@ -1164,32 +1009,32 @@
 will be applied, followed by a series of second order filters in a chain.
 
 
-High-Pass Filtering
--------------------
+7.3. High-Pass Filtering
+------------------------
 High-pass filtering is achieved with the following APIs:
 
-    |---------|-------------------------------------------|
+    +---------+-------------------------------------------+
     | API     | Description                               |
-    |---------|-------------------------------------------|
+    +---------+-------------------------------------------+
     | ma_hpf1 | First order high-pass filter              |
     | ma_hpf2 | Second order high-pass filter             |
     | ma_hpf  | High order high-pass filter (Butterworth) |
-    |---------|-------------------------------------------|
+    +---------+-------------------------------------------+
 
 High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, `ma_hpf2` and `ma_hpf`. See example code for low-pass filters
 for example usage.
 
 
-Band-Pass Filtering
--------------------
+7.4. Band-Pass Filtering
+------------------------
 Band-pass filtering is achieved with the following APIs:
 
-    |---------|-------------------------------|
+    +---------+-------------------------------+
     | API     | Description                   |
-    |---------|-------------------------------|
+    +---------+-------------------------------+
     | ma_bpf2 | Second order band-pass filter |
     | ma_bpf  | High order band-pass filter   |
-    |---------|-------------------------------|
+    +---------+-------------------------------+
 
 Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and `ma_hpf`. See example code for low-pass filters for example
 usage. Note that the order for band-pass filters must be an even number which means there is no first order band-pass filter, unlike low-pass and high-pass
@@ -1196,50 +1041,50 @@
 filters.
 
 
-Notch Filtering
----------------
+7.5. Notch Filtering
+--------------------
 Notch filtering is achieved with the following APIs:
 
-    |-----------|------------------------------------------|
+    +-----------+------------------------------------------+
     | API       | Description                              |
-    |-----------|------------------------------------------|
+    +-----------+------------------------------------------+
     | ma_notch2 | Second order notching filter             |
-    |-----------|------------------------------------------|
+    +-----------+------------------------------------------+
 
 
-Peaking EQ Filtering
---------------------
+7.6. Peaking EQ Filtering
+-------------------------
 Peaking filtering is achieved with the following APIs:
 
-    |----------|------------------------------------------|
+    +----------+------------------------------------------+
     | API      | Description                              |
-    |----------|------------------------------------------|
+    +----------+------------------------------------------+
     | ma_peak2 | Second order peaking filter              |
-    |----------|------------------------------------------|
+    +----------+------------------------------------------+
 
 
-Low Shelf Filtering
--------------------
+7.7. Low Shelf Filtering
+------------------------
 Low shelf filtering is achieved with the following APIs:
 
-    |-------------|------------------------------------------|
+    +-------------+------------------------------------------+
     | API         | Description                              |
-    |-------------|------------------------------------------|
+    +-------------+------------------------------------------+
     | ma_loshelf2 | Second order low shelf filter            |
-    |-------------|------------------------------------------|
+    +-------------+------------------------------------------+
 
 Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to just turn them down rather than eliminate them entirely.
 
 
-High Shelf Filtering
---------------------
+7.8. High Shelf Filtering
+-------------------------
 High shelf filtering is achieved with the following APIs:
 
-    |-------------|------------------------------------------|
+    +-------------+------------------------------------------+
     | API         | Description                              |
-    |-------------|------------------------------------------|
+    +-------------+------------------------------------------+
     | ma_hishelf2 | Second order high shelf filter           |
-    |-------------|------------------------------------------|
+    +-------------+------------------------------------------+
 
 The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf` instead of `ma_loshelf`. Where a low shelf filter is used to
 adjust the volume of low frequencies, the high shelf filter does the same thing for high frequencies.
@@ -1247,15 +1092,21 @@
 
 
 
-Waveform and Noise Generation
-=============================
-
-Waveforms
----------
+8. Waveform and Noise Generation
+================================
+
+8.1. Waveforms
+--------------
 miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved with the `ma_waveform` API. Example:
 
     ```c
-    ma_waveform_config config = ma_waveform_config_init(FORMAT, CHANNELS, SAMPLE_RATE, ma_waveform_type_sine, amplitude, frequency);
+    ma_waveform_config config = ma_waveform_config_init(
+        FORMAT,
+        CHANNELS,
+        SAMPLE_RATE,
+        ma_waveform_type_sine,
+        amplitude,
+        frequency);
 
     ma_waveform waveform;
     ma_result result = ma_waveform_init(&config, &waveform);
@@ -1271,28 +1122,33 @@
 The amplitude, frequency and sample rate can be changed dynamically with `ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()` and
 `ma_waveform_set_sample_rate()` respectively.
 
-You can reverse the waveform by setting the amplitude to a negative value. You can use this to control whether or not a sawtooth has a positive or negative
+You can invert the waveform by setting the amplitude to a negative value. You can use this to control whether or not a sawtooth has a positive or negative
 ramp, for example.
 
 Below are the supported waveform types:
 
-    |---------------------------|
+    +---------------------------+
     | Enum Name                 |
-    |---------------------------|
+    +---------------------------+
     | ma_waveform_type_sine     |
     | ma_waveform_type_square   |
     | ma_waveform_type_triangle |
     | ma_waveform_type_sawtooth |
-    |---------------------------|
+    +---------------------------+
 
 
 
-Noise
------
+8.2. Noise
+----------
 miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example:
 
     ```c
-    ma_noise_config config = ma_noise_config_init(FORMAT, CHANNELS, ma_noise_type_white, SEED, amplitude);
+    ma_noise_config config = ma_noise_config_init(
+        FORMAT,
+        CHANNELS,
+        ma_noise_type_white,
+        SEED,
+        amplitude);
 
     ma_noise noise;
     ma_result result = ma_noise_init(&config, &noise);
@@ -1317,25 +1173,31 @@
 
 Below are the supported noise types.
 
-    |------------------------|
+    +------------------------+
     | Enum Name              |
-    |------------------------|
+    +------------------------+
     | ma_noise_type_white    |
     | ma_noise_type_pink     |
     | ma_noise_type_brownian |
-    |------------------------|
+    +------------------------+
 
 
 
-Audio Buffers
-=============
-miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can read from both memory that's managed by the application, but
-can also handle the memory management for you internally. The way memory is managed is flexible and should support most use cases.
-
+9. Audio Buffers
+================
+miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can read from memory that's managed by the application, but
+can also handle the memory management for you internally. Memory management is flexible and should support most use cases.
+
 Audio buffers are initialised using the standard configuration system used everywhere in miniaudio:
 
     ```c
-    ma_audio_buffer_config config = ma_audio_buffer_config_init(format, channels, sizeInFrames, pExistingData, &allocationCallbacks);
+    ma_audio_buffer_config config = ma_audio_buffer_config_init(
+        format,
+        channels,
+        sizeInFrames,
+        pExistingData,
+        &allocationCallbacks);
+
     ma_audio_buffer buffer;
     result = ma_audio_buffer_init(&config, &buffer);
     if (result != MA_SUCCESS) {
@@ -1347,13 +1209,20 @@
     ma_audio_buffer_uninit(&buffer);
     ```
 
-In the example above, the memory pointed to by `pExistingData` will _not_ be copied which is how an application can handle memory allocations themselves. If
-you would rather make a copy of the data, use `ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`.
+In the example above, the memory pointed to by `pExistingData` will _not_ be copied and is how an application can do self-managed memory allocation. If you
+would rather make a copy of the data, use `ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`.
 
 Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure _and_ the raw audio data in a contiguous block of memory. That is,
 the raw audio data will be located immediately after the `ma_audio_buffer` structure. To do this, use `ma_audio_buffer_alloc_and_init()`:
 
     ```c
+    ma_audio_buffer_config config = ma_audio_buffer_config_init(
+        format,
+        channels,
+        sizeInFrames,
+        pExistingData,
+        &allocationCallbacks);
+
     ma_audio_buffer* pBuffer
     result = ma_audio_buffer_alloc_and_init(&config, &pBuffer);
     if (result != MA_SUCCESS) {
@@ -1365,13 +1234,13 @@
     ma_audio_buffer_uninit_and_free(&buffer);
     ```
 
-If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it with `ma_audio_buffer_uninit_and_free()`.
+If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it with `ma_audio_buffer_uninit_and_free()`. In the example above,
+the memory pointed to by `pExistingData` will be copied into the buffer, which is contrary to the behavior of `ma_audio_buffer_init()`.
 
-An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the cursor moves forward. It does not automatically loop back to
-the start. To do this, you should inspect the number of frames returned by `ma_audio_buffer_read_pcm_frames()` to determine if the end has been reached, which
-you can know by comparing it with the requested frame count you specified when you called the function. If the return value is less it means the end has been
-reached. In this case you can seem back to the start with `ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an
-audio buffer.
+An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the cursor moves forward. The last parameter (`loop`) can be
+used to determine if the buffer should loop. The return value is the number of frames actually read. If this is less than the number of frames requested it
+means the end has been reached. This should never happen if the `loop` parameter is set to true. If you want to manually loop back to the start, you can do so
+with with `ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an audio buffer.
 
     ```c
     ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping);
@@ -1380,8 +1249,8 @@
     }
     ```
 
-Sometimes you may want to avoid the cost of data movement between the internal buffer and the output buffer as it's just a copy operation. Instead you can use
-memory mapping to retrieve a pointer to a segment of data:
+Sometimes you may want to avoid the cost of data movement between the internal buffer and the output buffer. Instead you can use memory mapping to retrieve a
+pointer to a segment of data:
 
     ```c
     void* pMappedFrames;
@@ -1388,7 +1257,8 @@
     ma_uint64 frameCount = frameCountToTryMapping;
     ma_result result = ma_audio_buffer_map(pAudioBuffer, &pMappedFrames, &frameCount);
     if (result == MA_SUCCESS) {
-        // Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be less due to the end of the buffer being reached.
+        // Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be
+        // less due to the end of the buffer being reached.
         ma_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels);
 
         // You must unmap the buffer.
@@ -1398,12 +1268,13 @@
 
 When you use memory mapping, the read cursor is increment by the frame count passed in to `ma_audio_buffer_unmap()`. If you decide not to process every frame
 you can pass in a value smaller than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is that it does not handle looping
-for you. You can determine if the buffer is at the end for the purpose of looping with `ma_audio_buffer_at_end()`.
+for you. You can determine if the buffer is at the end for the purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of
+`ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END` as an error when returned by `ma_audio_buffer_unmap()`.
 
 
 
-Ring Buffers
-============
+10. Ring Buffers
+================
 miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates
 on bytes, whereas the `ma_pcm_rb` operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around `ma_rb`.
 
@@ -1424,10 +1295,10 @@
 The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because it's the PCM varient of the ring buffer API. For the regular
 ring buffer that operates on bytes you would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes instead of frames. The
 fourth parameter is an optional pre-allocated buffer and the fifth parameter is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation
-routines. Passing in NULL for this results in MA_MALLOC() and MA_FREE() being used.
+routines. Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used.
 
-Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is offset from each other based on the stride. To manage your sub-
-buffers you can use `ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and `ma_pcm_rb_get_subbuffer_ptr()`.
+Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is offset from each other based on the stride. To manage your
+sub-buffers you can use `ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and `ma_pcm_rb_get_subbuffer_ptr()`.
 
 Use 'ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section of the ring buffer. You specify the number of frames you
 need, and on output it will set to what was actually acquired. If the read or write pointer is positioned such that the number of frames requested will require
@@ -1444,9 +1315,9 @@
 there is too little space between the pointers, move the write pointer forward.
 
 You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb` API. This is exactly the same, only you will use the `ma_rb`
-functions instead of `ma_pcm_rb` and instead of frame counts you'll pass around byte counts.
+functions instead of `ma_pcm_rb` and instead of frame counts you will pass around byte counts.
 
-The maximum size of the buffer in bytes is 0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1) due to the most significant bit being used to encode a loop flag and the internally
+The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most significant bit being used to encode a loop flag and the internally
 managed buffers always being aligned to MA_SIMD_ALIGNMENT.
 
 Note that the ring buffer is only thread safe when used by a single consumer thread and single producer thread.
@@ -1453,13 +1324,13 @@
 
 
 
-Backends
-========
+11. Backends
+============
 The following backends are supported by miniaudio.
 
-    |-------------|-----------------------|--------------------------------------------------------|
+    +-------------+-----------------------+--------------------------------------------------------+
     | Name        | Enum Name             | Supported Operating Systems                            |
-    |-------------|-----------------------|--------------------------------------------------------|
+    +-------------+-----------------------+--------------------------------------------------------+
     | WASAPI      | ma_backend_wasapi     | Windows Vista+                                         |
     | DirectSound | ma_backend_dsound     | Windows XP+                                            |
     | WinMM       | ma_backend_winmm      | Windows XP+ (may work on older versions, but untested) |
@@ -1471,30 +1342,28 @@
     | audio(4)    | ma_backend_audio4     | NetBSD, OpenBSD                                        |
     | OSS         | ma_backend_oss        | FreeBSD                                                |
     | AAudio      | ma_backend_aaudio     | Android 8+                                             |
-    | OpenSL|ES   | ma_backend_opensl     | Android (API level 16+)                                |
+    | OpenSL ES   | ma_backend_opensl     | Android (API level 16+)                                |
     | Web Audio   | ma_backend_webaudio   | Web (via Emscripten)                                   |
     | Null        | ma_backend_null       | Cross Platform (not used on Web)                       |
-    |-------------|-----------------------|--------------------------------------------------------|
+    +-------------+-----------------------+--------------------------------------------------------+
 
 Some backends have some nuance details you may want to be aware of.
 
-WASAPI
-------
+11.1. WASAPI
+------------
 - Low-latency shared mode will be disabled when using an application-defined sample rate which is different to the device's native sample rate. To work around
-  this, set wasapi.noAutoConvertSRC to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing when the
-  AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM flag is specified. Setting wasapi.noAutoConvertSRC will result in miniaudio's internal resampler being used instead which
-  will in turn enable the use of low-latency shared mode.
+  this, set `wasapi.noAutoConvertSRC` to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing when the
+  `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC will result in miniaudio's internal resampler being used instead
+  which will in turn enable the use of low-latency shared mode.
 
-PulseAudio
-----------
+11.2. PulseAudio
+----------------
 - If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki:
-    https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling
-  Alternatively, consider using a different backend such as ALSA.
+  https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling. Alternatively, consider using a different backend such as ALSA.
 
-Android
--------
-- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest:
-    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+11.3. Android
+-------------
+- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: `<uses-permission android:name="android.permission.RECORD_AUDIO" />`
 - With OpenSL|ES, only a single ma_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 ma_device_id structure (ma_device_id.aaudio) and pass it to ma_device_init().
@@ -1501,20 +1370,23 @@
 - The backend API will perform resampling where possible. The reason for this as opposed to using miniaudio's built-in resampler is to take advantage of any
   potential device-specific optimizations the driver may implement.
 
-UWP
----
+11.4. UWP
+---------
 - UWP only supports default playback and capture devices.
 - UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest):
-      <Package ...>
-          ...
-          <Capabilities>
-              <DeviceCapability Name="microphone" />
-          </Capabilities>
-      </Package>
 
-Web Audio / Emscripten
-----------------------
-- You cannot use -std=c* compiler flags, nor -ansi. This only applies to the Emscripten build.
+    ```
+    <Package ...>
+        ...
+        <Capabilities>
+            <DeviceCapability Name="microphone" />
+        </Capabilities>
+    </Package>
+    ```
+
+11.5. Web Audio / Emscripten
+----------------------------
+- You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build.
 - The first time a context is initialized it will create a global object called "miniaudio" 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 has implemented a policy in their browsers that prevent automatic media output without first receiving some kind of user input. The following web page
@@ -1523,17 +1395,18 @@
 
 
 
-Miscellaneous Notes
-===================
+12. Miscellaneous Notes
+=======================
 - Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for WASAPI and Core Audio, however other backends such as
   PulseAudio may naturally support it, though not all have been tested.
-- The contents of the output buffer passed into the data callback will always be pre-initialized to zero unless the noPreZeroedOutputBuffer config variable in
-  ma_device_config is set to true, in which case it'll be undefined which will require you to write something to the entire buffer.
-- By default miniaudio will automatically clip samples. This only applies when the playback sample format is configured as ma_format_f32. If you are doing
-  clipping yourself, you can disable this overhead by setting noClip to true in the device config.
+- The contents of the output buffer passed into the data callback will always be pre-initialized to zero unless the `noPreZeroedOutputBuffer` config variable
+  in `ma_device_config` is set to true, in which case it'll be undefined which will require you to write something to the entire buffer.
+- By default miniaudio will automatically clip samples. This only applies when the playback sample format is configured as `ma_format_f32`. If you are doing
+  clipping yourself, you can disable this overhead by setting `noClip` to true in the device config.
 - The sndio backend is currently only enabled on OpenBSD builds.
 - The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can use it.
-- Note that GCC and Clang requires "-msse2", "-mavx2", etc. for SIMD optimizations.
+- Note that GCC and Clang requires `-msse2`, `-mavx2`, etc. for SIMD optimizations.
+- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This is due to 64-bit file APIs not being available.
 */
 
 #ifndef miniaudio_h
@@ -1548,7 +1421,7 @@
 
 #define MA_VERSION_MAJOR    0
 #define MA_VERSION_MINOR    10
-#define MA_VERSION_REVISION 9
+#define MA_VERSION_REVISION 18
 #define MA_VERSION_STRING   MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION)
 
 #if defined(_MSC_VER) && !defined(__clang__)
@@ -1574,11 +1447,7 @@
     #endif
 #else
     #define MA_POSIX
-
-    /* We only use multi-threading with the device IO API, so no need to include these headers otherwise. */
-#if !defined(MA_NO_DEVICE_IO)
     #include <pthread.h>    /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */
-#endif
 
     #ifdef __unix__
         #define MA_UNIX
@@ -1602,56 +1471,34 @@
 
 #include <stddef.h> /* For size_t. */
 
-/* Sized types. Prefer built-in types. Fall back to stdint. */
-#ifdef _MSC_VER
-    #if defined(__clang__)
+/* Sized types. */
+typedef   signed char           ma_int8;
+typedef unsigned char           ma_uint8;
+typedef   signed short          ma_int16;
+typedef unsigned short          ma_uint16;
+typedef   signed int            ma_int32;
+typedef unsigned int            ma_uint32;
+#if defined(_MSC_VER)
+    typedef   signed __int64    ma_int64;
+    typedef unsigned __int64    ma_uint64;
+#else
+    #if defined(__GNUC__)
         #pragma GCC diagnostic push
-        #pragma GCC diagnostic ignored "-Wlanguage-extension-token"
         #pragma GCC diagnostic ignored "-Wlong-long"
-        #pragma GCC diagnostic ignored "-Wc++11-long-long"
+        #if defined(__clang__)
+            #pragma GCC diagnostic ignored "-Wc++11-long-long"
+        #endif
     #endif
-    typedef   signed __int8  ma_int8;
-    typedef unsigned __int8  ma_uint8;
-    typedef   signed __int16 ma_int16;
-    typedef unsigned __int16 ma_uint16;
-    typedef   signed __int32 ma_int32;
-    typedef unsigned __int32 ma_uint32;
-    typedef   signed __int64 ma_int64;
-    typedef unsigned __int64 ma_uint64;
-    #if defined(__clang__)
+    typedef   signed long long  ma_int64;
+    typedef unsigned long long  ma_uint64;
+    #if defined(__GNUC__)
         #pragma GCC diagnostic pop
     #endif
-#else
-    #define MA_HAS_STDINT
-    #include <stdint.h>
-    typedef int8_t   ma_int8;
-    typedef uint8_t  ma_uint8;
-    typedef int16_t  ma_int16;
-    typedef uint16_t ma_uint16;
-    typedef int32_t  ma_int32;
-    typedef uint32_t ma_uint32;
-    typedef int64_t  ma_int64;
-    typedef uint64_t ma_uint64;
 #endif
-
-#ifdef MA_HAS_STDINT
-    typedef uintptr_t ma_uintptr;
+#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
+    typedef ma_uint64           ma_uintptr;
 #else
-    #if defined(_WIN32)
-        #if defined(_WIN64)
-            typedef ma_uint64 ma_uintptr;
-        #else
-            typedef ma_uint32 ma_uintptr;
-        #endif
-    #elif defined(__GNUC__)
-        #if defined(__LP64__)
-            typedef ma_uint64 ma_uintptr;
-        #else
-            typedef ma_uint32 ma_uintptr;
-        #endif
-    #else
-        typedef ma_uint64 ma_uintptr;   /* Fallback. */
-    #endif
+    typedef ma_uint32           ma_uintptr;
 #endif
 
 typedef ma_uint8    ma_bool8;
@@ -2007,6 +1854,67 @@
 } ma_lcg;
 
 
+#ifndef MA_NO_THREADING
+/* Thread priorties should be ordered such that the default priority of the worker thread is 0. */
+typedef enum
+{
+    ma_thread_priority_idle     = -5,
+    ma_thread_priority_lowest   = -4,
+    ma_thread_priority_low      = -3,
+    ma_thread_priority_normal   = -2,
+    ma_thread_priority_high     = -1,
+    ma_thread_priority_highest  =  0,
+    ma_thread_priority_realtime =  1,
+    ma_thread_priority_default  =  0
+} ma_thread_priority;
+
+typedef unsigned char ma_spinlock;
+
+#if defined(MA_WIN32)
+typedef ma_handle ma_thread;
+#endif
+#if defined(MA_POSIX)
+typedef pthread_t ma_thread;
+#endif
+
+#if defined(MA_WIN32)
+typedef ma_handle ma_mutex;
+#endif
+#if defined(MA_POSIX)
+typedef pthread_mutex_t ma_mutex;
+#endif
+
+#if defined(MA_WIN32)
+typedef ma_handle ma_event;
+#endif
+#if defined(MA_POSIX)
+typedef struct
+{
+    ma_uint32 value;
+    pthread_mutex_t lock;
+    pthread_cond_t cond;
+} ma_event;
+#endif  /* MA_POSIX */
+
+#if defined(MA_WIN32)
+typedef ma_handle ma_semaphore;
+#endif
+#if defined(MA_POSIX)
+typedef struct
+{
+    int value;
+    pthread_mutex_t lock;
+    pthread_cond_t cond;
+} ma_semaphore;
+#endif  /* MA_POSIX */
+#else
+/* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */
+#ifndef MA_NO_DEVICE_IO
+#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO";
+#endif
+#endif  /* MA_NO_THREADING */
+
+
 /*
 Retrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required.
 */
@@ -2015,7 +1923,7 @@
 /*
 Retrieves the version of miniaudio as a string which can be useful for logging purposes.
 */
-MA_API const char* ma_version_string();
+MA_API const char* ma_version_string(void);
 
 
 /**************************************************************************************************************************************************************
@@ -2551,7 +2459,7 @@
     float weights[MA_MAX_CHANNELS][MA_MAX_CHANNELS];  /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
 } ma_channel_converter_config;
 
-MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel channelMapIn[MA_MAX_CHANNELS], ma_uint32 channelsOut, const ma_channel channelMapOut[MA_MAX_CHANNELS], ma_channel_mix_mode mixingMode);
+MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode);
 
 typedef struct
 {
@@ -2684,15 +2592,26 @@
 
 /*
 Helper for retrieving a standard channel map.
+
+The output channel map buffer must have a capacity of at least `channels`.
 */
-MA_API void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS]);
+MA_API void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel* pChannelMap);
 
 /*
 Copies a channel map.
+
+Both input and output channel map buffers must have a capacity of at at least `channels`.
 */
 MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels);
 
+/*
+Copies a channel map if one is specified, otherwise copies the default channel map.
 
+The output buffer must have a capacity of at least `channels`. If not NULL, the input channel map must also have a capacity of at least `channels`.
+*/
+MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels);
+
+
 /*
 Determines whether or not a channel map is valid.
 
@@ -2702,25 +2621,33 @@
 Invalid channel maps:
   - A channel map with no channels
   - A channel map with more than one channel and a mono channel
+
+The channel map buffer must have a capacity of at least `channels`.
 */
-MA_API ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS]);
+MA_API ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel* pChannelMap);
 
 /*
 Helper for comparing two channel maps for equality.
 
 This assumes the channel count is the same between the two.
+
+Both channels map buffers must have a capacity of at least `channels`.
 */
-MA_API ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel channelMapA[MA_MAX_CHANNELS], const ma_channel channelMapB[MA_MAX_CHANNELS]);
+MA_API ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel* pChannelMapA, const ma_channel* pChannelMapB);
 
 /*
 Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE).
+
+The channel map buffer must have a capacity of at least `channels`.
 */
-MA_API ma_bool32 ma_channel_map_blank(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS]);
+MA_API ma_bool32 ma_channel_map_blank(ma_uint32 channels, const ma_channel* pChannelMap);
 
 /*
 Helper for determining whether or not a channel is present in the given channel map.
+
+The channel map buffer must have a capacity of at least `channels`.
 */
-MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS], ma_channel channelPosition);
+MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition);
 
 
 /************************************************************************************************************************************************************
@@ -2997,58 +2924,7 @@
     ma_backend_null    /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */
 } ma_backend;
 
-/* Thread priorties should be ordered such that the default priority of the worker thread is 0. */
-typedef enum
-{
-    ma_thread_priority_idle     = -5,
-    ma_thread_priority_lowest   = -4,
-    ma_thread_priority_low      = -3,
-    ma_thread_priority_normal   = -2,
-    ma_thread_priority_high     = -1,
-    ma_thread_priority_highest  =  0,
-    ma_thread_priority_realtime =  1,
-    ma_thread_priority_default  =  0
-} ma_thread_priority;
 
-#if defined(MA_WIN32)
-typedef ma_handle ma_thread;
-#endif
-#if defined(MA_POSIX)
-typedef pthread_t ma_thread;
-#endif
-
-#if defined(MA_WIN32)
-typedef ma_handle ma_mutex;
-#endif
-#if defined(MA_POSIX)
-typedef pthread_mutex_t ma_mutex;
-#endif
-
-#if defined(MA_WIN32)
-typedef ma_handle ma_event;
-#endif
-#if defined(MA_POSIX)
-typedef struct
-{
-    ma_uint32 value;
-    pthread_mutex_t lock;
-    pthread_cond_t cond;
-} ma_event;
-#endif
-
-#if defined(MA_WIN32)
-typedef ma_handle ma_semaphore;
-#endif
-#if defined(MA_POSIX)
-typedef struct
-{
-    int value;
-    pthread_mutex_t lock;
-    pthread_cond_t cond;
-} ma_semaphore;
-#endif
-
-
 /*
 The callback for processing audio data from the device.
 
@@ -3318,6 +3194,8 @@
     {
         ma_ios_session_category sessionCategory;
         ma_uint32 sessionCategoryOptions;
+        ma_bool32 noAudioSessionActivate;   /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */
+        ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */
     } coreaudio;
     struct
     {
@@ -3586,6 +3464,8 @@
             ma_proc AudioUnitRender;
             
             /*AudioComponent*/ ma_ptr component;
+            
+            ma_bool32 noAudioSessionDeactivate; /* For tracking whether or not the iOS audio session should be explicitly deactivated. Set from the config in ma_context_init__coreaudio(). */
         } coreaudio;
 #endif
 #ifdef MA_SUPPORT_SNDIO
@@ -3658,7 +3538,14 @@
 #ifdef MA_SUPPORT_OPENSL
         struct
         {
-            int _unused;
+            ma_handle libOpenSLES;
+            ma_handle SL_IID_ENGINE;
+            ma_handle SL_IID_AUDIOIODEVICECAPABILITIES;
+            ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
+            ma_handle SL_IID_RECORD;
+            ma_handle SL_IID_PLAY;
+            ma_handle SL_IID_OUTPUTMIX;
+            ma_proc   slCreateEngine;
         } opensl;
 #endif
 #ifdef MA_SUPPORT_WEBAUDIO
@@ -3759,6 +3646,7 @@
     } resampling;
     struct
     {
+        ma_device_id id;                    /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */
         char name[256];                     /* Maybe temporary. Likely to be replaced with a query API. */
         ma_share_mode shareMode;            /* Set to whatever was passed in when the device was initialized. */
         ma_bool32 usingDefaultFormat     : 1;
@@ -3777,6 +3665,7 @@
     } playback;
     struct
     {
+        ma_device_id id;                    /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */
         char name[256];                     /* Maybe temporary. Likely to be replaced with a query API. */
         ma_share_mode shareMode;            /* Set to whatever was passed in when the device was initialized. */
         ma_bool32 usingDefaultFormat     : 1;
@@ -5192,7 +5081,25 @@
 #endif  /* MA_NO_DEVICE_IO */
 
 
+#ifndef MA_NO_THREADING
+
 /*
+Locks a spinlock.
+*/
+MA_API ma_result ma_spinlock_lock(ma_spinlock* pSpinlock);
+
+/*
+Locks a spinlock, but does not yield() when looping.
+*/
+MA_API ma_result ma_spinlock_lock_noyield(ma_spinlock* pSpinlock);
+
+/*
+Unlocks a spinlock.
+*/
+MA_API ma_result ma_spinlock_unlock(ma_spinlock* pSpinlock);
+
+
+/*
 Creates a mutex.
 
 A mutex must be created from a valid context. A mutex is initially unlocked.
@@ -5234,6 +5141,7 @@
 Signals the specified auto-reset event.
 */
 MA_API ma_result ma_event_signal(ma_event* pEvent);
+#endif  /* MA_NO_THREADING */
 
 
 /************************************************************************************************************************************************************
@@ -5294,31 +5202,31 @@
 
 Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place.
 */
-MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint32 sampleCount, float factor);
-MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint32 sampleCount, float factor);
-MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint32 sampleCount, float factor);
-MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint32 sampleCount, float factor);
-MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint32 sampleCount, float factor);
+MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor);
+MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor);
+MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor);
+MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor);
+MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor);
 
-MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint32 sampleCount, float factor);
-MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint32 sampleCount, float factor);
-MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint32 sampleCount, float factor);
-MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint32 sampleCount, float factor);
-MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint32 sampleCount, float factor);
+MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor);
+MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor);
+MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor);
+MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor);
+MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor);
 
-MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
-MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
-MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
-MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
-MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
-MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor);
+MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
+MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
+MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
+MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
+MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
+MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor);
 
-MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
-MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
-MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
-MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
-MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
-MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor);
+MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
+MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
+MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
+MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
+MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
+MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor);
 
 
 /*
@@ -5340,7 +5248,9 @@
     ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex);
     ma_result (* onMap)(ma_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount);   /* Returns MA_AT_END if the end has been reached. This should be considered successful. */
     ma_result (* onUnmap)(ma_data_source* pDataSource, ma_uint64 frameCount);
-    ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels);
+    ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
+    ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor);
+    ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength);
 } ma_data_source_callbacks;
 
 MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead, ma_bool32 loop);   /* Must support pFramesOut = NULL in which case a forward seek should be performed. */
@@ -5347,8 +5257,10 @@
 MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked, ma_bool32 loop); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount); */
 MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex);
 MA_API ma_result ma_data_source_map(ma_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount);
-MA_API ma_result ma_data_source_unmap(ma_data_source* pDataSource, ma_uint64 frameCount);   /* Returns MA_AT_END if the end has been reached. This should be considered successful. */
-MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels);
+MA_API ma_result ma_data_source_unmap(ma_data_source* pDataSource, ma_uint64 frameCount);       /* Returns MA_AT_END if the end has been reached. This should be considered successful. */
+MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
+MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor);
+MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength);    /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */
 
 
 typedef struct
@@ -5385,25 +5297,10 @@
 MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount);
 MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount);    /* Returns MA_AT_END if the end has been reached. This should be considered successful. */
 MA_API ma_result ma_audio_buffer_at_end(ma_audio_buffer* pAudioBuffer);
+MA_API ma_result ma_audio_buffer_get_available_frames(ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames);
 
 
 
-typedef enum
-{
-    ma_seek_origin_start,
-    ma_seek_origin_current,
-    ma_seek_origin_end  /* Not used by decoders. */
-} ma_seek_origin;
-
-#if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)
-typedef enum
-{
-    ma_resource_format_wav
-} ma_resource_format;
-#endif
-
-
-
 /************************************************************************************************************************************************************
 
 VFS
@@ -5419,6 +5316,13 @@
 #define MA_OPEN_MODE_READ   0x00000001
 #define MA_OPEN_MODE_WRITE  0x00000002
 
+typedef enum
+{
+    ma_seek_origin_start,
+    ma_seek_origin_current,
+    ma_seek_origin_end  /* Not used by decoders. */
+} ma_seek_origin;
+
 typedef struct
 {
     ma_uint64 sizeInBytes;
@@ -5456,6 +5360,14 @@
 
 
 
+
+#if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)
+typedef enum
+{
+    ma_resource_format_wav
+} ma_resource_format;
+#endif
+
 /************************************************************************************************************************************************************
 
 Decoding
@@ -5504,7 +5416,8 @@
     ma_decoder_read_proc onRead;
     ma_decoder_seek_proc onSeek;
     void* pUserData;
-    ma_uint64  readPointer; /* Used for returning back to a previous position after analysing the stream or whatnot. */
+    ma_uint64  readPointerInBytes;          /* In internal encoded data. */
+    ma_uint64  readPointerInPCMFrames;      /* In output sample rate. Used for keeping track of how many frames are available for decoding. */
     ma_format  internalFormat;
     ma_uint32  internalChannels;
     ma_uint32  internalSampleRate;
@@ -5579,6 +5492,11 @@
 MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder);
 
 /*
+Retrieves the current position of the read cursor in PCM frames.
+*/
+MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor);
+
+/*
 Retrieves the length of the decoder in PCM frames.
 
 Do not call this on streams of an undefined length, such as internet radio.
@@ -5609,6 +5527,17 @@
 MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex);
 
 /*
+Retrieves the number of frames that can be read before reaching the end.
+
+This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in
+particular ensuring you do not call it on streams of an undefined length, such as internet radio.
+
+If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be
+returned.
+*/
+MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames);
+
+/*
 Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input,
 pConfig should be set to what you want. On output it will be set to what you got.
 */
@@ -5771,6 +5700,9 @@
 *************************************************************************************************************************************************************
 ************************************************************************************************************************************************************/
 #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
+#ifndef miniaudio_c
+#define miniaudio_c
+
 #include <assert.h>
 #include <limits.h> /* For INT_MAX */
 #include <math.h>   /* sin(), etc. */
@@ -5785,8 +5717,10 @@
 #ifdef MA_WIN32
 #include <windows.h>
 #else
-#include <stdlib.h> /* For malloc(), free(), wcstombs(). */
-#include <string.h> /* For memset() */
+#include <stdlib.h>     /* For malloc(), free(), wcstombs(). */
+#include <string.h>     /* For memset() */
+#include <sched.h>
+#include <sys/time.h>   /* select() (used for ma_sleep()). */
 #endif
 
 #include <sys/stat.h>   /* For fstat(), etc. */
@@ -6223,7 +6157,74 @@
 }
 
 
+#if !defined(MA_EMSCRIPTEN)
+#ifdef MA_WIN32
+static void ma_sleep__win32(ma_uint32 milliseconds)
+{
+    Sleep((DWORD)milliseconds);
+}
+#endif
+#ifdef MA_POSIX
+static void ma_sleep__posix(ma_uint32 milliseconds)
+{
+#ifdef MA_EMSCRIPTEN
+    (void)milliseconds;
+    MA_ASSERT(MA_FALSE);  /* The Emscripten build should never sleep. */
+#else
+    #if _POSIX_C_SOURCE >= 199309L
+        struct timespec ts;
+        ts.tv_sec  = milliseconds / 1000;
+        ts.tv_nsec = milliseconds % 1000 * 1000000;
+        nanosleep(&ts, NULL);
+    #else
+        struct timeval tv;
+        tv.tv_sec  = milliseconds / 1000;
+        tv.tv_usec = milliseconds % 1000 * 1000;
+        select(0, NULL, NULL, NULL, &tv);
+    #endif
+#endif
+}
+#endif
 
+static void ma_sleep(ma_uint32 milliseconds)
+{
+#ifdef MA_WIN32
+    ma_sleep__win32(milliseconds);
+#endif
+#ifdef MA_POSIX
+    ma_sleep__posix(milliseconds);
+#endif
+}
+#endif
+
+static MA_INLINE void ma_yield()
+{
+#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
+    /* x86/x64 */
+    #if defined(_MSC_VER) && !defined(__clang__)
+        #if _MSC_VER >= 1400
+            _mm_pause();
+        #else
+            __asm pause;
+        #endif
+    #else
+        __asm__ __volatile__ ("pause");
+    #endif
+#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__)
+    /* ARM */
+    #if defined(_MSC_VER)
+        /* Apparently there is a __yield() intrinsic that's compatible with ARM, but I cannot find documentation for it nor can I find where it's declared. */
+        __yield();
+    #else
+        __asm__ __volatile__ ("yield"); /* ARMv6K/ARMv6T2 and above. */
+    #endif
+#else
+    /* Unknown or unsupported architecture. No-op. */
+#endif
+}
+
+
+
 #ifndef MA_COINIT_VALUE
 #define MA_COINIT_VALUE    0   /* 0 = COINIT_MULTITHREADED */
 #endif
@@ -6341,7 +6342,7 @@
     }
 }
 
-MA_API const char* ma_version_string()
+MA_API const char* ma_version_string(void)
 {
     return MA_VERSION_STRING;
 }
@@ -7612,32 +7613,32 @@
 #if defined(__cplusplus)
 extern "C" {
 #endif
-#if defined(__GNUC__)
-    #pragma GCC diagnostic push
-    #pragma GCC diagnostic ignored "-Wlong-long"
-    #if defined(__clang__)
-        #pragma GCC diagnostic ignored "-Wc++11-long-long"
-    #endif
-#endif
-typedef   signed char      c89atomic_int8;
-typedef unsigned char      c89atomic_uint8;
-typedef   signed short     c89atomic_int16;
-typedef unsigned short     c89atomic_uint16;
-typedef   signed int       c89atomic_int32;
-typedef unsigned int       c89atomic_uint32;
+typedef   signed char           c89atomic_int8;
+typedef unsigned char           c89atomic_uint8;
+typedef   signed short          c89atomic_int16;
+typedef unsigned short          c89atomic_uint16;
+typedef   signed int            c89atomic_int32;
+typedef unsigned int            c89atomic_uint32;
 #if defined(_MSC_VER)
-typedef   signed __int64   c89atomic_int64;
-typedef unsigned __int64   c89atomic_uint64;
+    typedef   signed __int64    c89atomic_int64;
+    typedef unsigned __int64    c89atomic_uint64;
 #else
-typedef unsigned long long c89atomic_int64;
-typedef unsigned long long c89atomic_uint64;
+    #if defined(__GNUC__)
+        #pragma GCC diagnostic push
+        #pragma GCC diagnostic ignored "-Wlong-long"
+        #if defined(__clang__)
+            #pragma GCC diagnostic ignored "-Wc++11-long-long"
+        #endif
+    #endif
+    typedef   signed long long  c89atomic_int64;
+    typedef unsigned long long  c89atomic_uint64;
+    #if defined(__GNUC__)
+        #pragma GCC diagnostic pop
+    #endif
 #endif
-#if defined(__GNUC__)
-    #pragma GCC diagnostic pop
-#endif
-typedef int                c89atomic_memory_order;
-typedef unsigned char      c89atomic_bool;
-typedef unsigned char      c89atomic_flag;
+typedef int                     c89atomic_memory_order;
+typedef unsigned char           c89atomic_bool;
+typedef unsigned char           c89atomic_flag;
 #if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT)
 #ifdef _WIN32
 #ifdef _WIN64
@@ -7722,6 +7723,7 @@
             static C89ATOMIC_INLINE void __stdcall c89atomic_thread_fence(int order)
             {
                 volatile c89atomic_uint32 barrier;
+                (void)order;
                 __asm {
                     xchg barrier, eax
                 }
@@ -7860,7 +7862,7 @@
         volatile c89atomic_uint8 newValue;
         do {
             oldValue = *dst;
-            newValue = oldValue - src;
+            newValue = (c89atomic_uint8)(oldValue - src);
         } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
         (void)order;
         return oldValue;
@@ -7871,7 +7873,7 @@
         volatile c89atomic_uint16 newValue;
         do {
             oldValue = *dst;
-            newValue = oldValue - src;
+            newValue = (c89atomic_uint16)(oldValue - src);
         } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
         (void)order;
         return oldValue;
@@ -7904,7 +7906,7 @@
         volatile c89atomic_uint8 newValue;
         do {
             oldValue = *dst;
-            newValue = oldValue & src;
+            newValue = (c89atomic_uint8)(oldValue & src);
         } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
         (void)order;
         return oldValue;
@@ -7915,7 +7917,7 @@
         volatile c89atomic_uint16 newValue;
         do {
             oldValue = *dst;
-            newValue = oldValue & src;
+            newValue = (c89atomic_uint16)(oldValue & src);
         } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
         (void)order;
         return oldValue;
@@ -7948,7 +7950,7 @@
         volatile c89atomic_uint8 newValue;
         do {
             oldValue = *dst;
-            newValue = oldValue ^ src;
+            newValue = (c89atomic_uint8)(oldValue ^ src);
         } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
         (void)order;
         return oldValue;
@@ -7959,7 +7961,7 @@
         volatile c89atomic_uint16 newValue;
         do {
             oldValue = *dst;
-            newValue = oldValue ^ src;
+            newValue = (c89atomic_uint16)(oldValue ^ src);
         } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
         (void)order;
         return oldValue;
@@ -7992,7 +7994,7 @@
         volatile c89atomic_uint8 newValue;
         do {
             oldValue = *dst;
-            newValue = oldValue | src;
+            newValue = (c89atomic_uint8)(oldValue | src);
         } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
         (void)order;
         return oldValue;
@@ -8003,7 +8005,7 @@
         volatile c89atomic_uint16 newValue;
         do {
             oldValue = *dst;
-            newValue = oldValue | src;
+            newValue = (c89atomic_uint16)(oldValue | src);
         } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
         (void)order;
         return oldValue;
@@ -8358,6 +8360,7 @@
 /* c89atomic.h end */
 
 
+
 static void* ma__malloc_default(size_t sz, void* pUserData)
 {
     (void)pUserData;
@@ -8540,6 +8543,7 @@
 Threading
 
 *******************************************************************************/
+#ifndef MA_NO_THREADING
 #ifdef MA_WIN32
     #define MA_THREADCALL WINAPI
     typedef unsigned long ma_thread_result;
@@ -8549,6 +8553,47 @@
 #endif
 typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData);
 
+static MA_INLINE ma_result ma_spinlock_lock_ex(ma_spinlock* pSpinlock, ma_bool32 yield)
+{
+    if (pSpinlock == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    for (;;) {
+        if (c89atomic_flag_test_and_set_explicit(pSpinlock, c89atomic_memory_order_acquire) == 0) {
+            break;
+        }
+
+        while (c89atomic_load_explicit_8(pSpinlock, c89atomic_memory_order_relaxed) == 1) {
+            if (yield) {
+                ma_yield();
+            }
+        }
+    }
+
+    return MA_SUCCESS;
+}
+
+MA_API ma_result ma_spinlock_lock(ma_spinlock* pSpinlock)
+{
+    return ma_spinlock_lock_ex(pSpinlock, MA_TRUE);
+}
+
+MA_API ma_result ma_spinlock_lock_noyield(ma_spinlock* pSpinlock)
+{
+    return ma_spinlock_lock_ex(pSpinlock, MA_FALSE);
+}
+
+MA_API ma_result ma_spinlock_unlock(ma_spinlock* pSpinlock)
+{
+    if (pSpinlock == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    c89atomic_flag_clear_explicit(pSpinlock, c89atomic_memory_order_release);
+    return MA_SUCCESS;
+}
+
 #ifdef MA_WIN32
 static int ma_thread_priority_to_win32(ma_thread_priority priority)
 {
@@ -8581,12 +8626,7 @@
     WaitForSingleObject((HANDLE)*pThread, INFINITE);
 }
 
-static void ma_sleep__win32(ma_uint32 milliseconds)
-{
-    Sleep((DWORD)milliseconds);
-}
 
-
 static ma_result ma_mutex_init__win32(ma_mutex* pMutex)
 {
     *pMutex = CreateEventW(NULL, FALSE, TRUE, NULL);
@@ -8695,9 +8735,6 @@
 
 
 #ifdef MA_POSIX
-#include <sched.h>
-#include <sys/time.h>
-
 static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)
 {
     int result;
@@ -8759,6 +8796,10 @@
 
         pthread_attr_destroy(&attr);
     }
+#else
+    /* It's the emscripten build. We'll have a few unused parameters. */
+    (void)priority;
+    (void)stackSize;
 #endif
 
     result = pthread_create(pThread, pAttr, entryProc, pData);
@@ -8774,29 +8815,7 @@
     pthread_join(*pThread, NULL);
 }
 
-#if !defined(MA_EMSCRIPTEN)
-static void ma_sleep__posix(ma_uint32 milliseconds)
-{
-#ifdef MA_EMSCRIPTEN
-    (void)milliseconds;
-    MA_ASSERT(MA_FALSE);  /* The Emscripten build should never sleep. */
-#else
-    #if _POSIX_C_SOURCE >= 199309L
-        struct timespec ts;
-        ts.tv_sec  = milliseconds / 1000;
-        ts.tv_nsec = milliseconds % 1000 * 1000000;
-        nanosleep(&ts, NULL);
-    #else
-        struct timeval tv;
-        tv.tv_sec  = milliseconds / 1000;
-        tv.tv_usec = milliseconds % 1000 * 1000;
-        select(0, NULL, NULL, NULL, &tv);
-    #endif
-#endif
-}
-#endif  /* MA_EMSCRIPTEN */
 
-
 static ma_result ma_mutex_init__posix(ma_mutex* pMutex)
 {
     int result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL);
@@ -8974,47 +8993,7 @@
 #endif
 }
 
-#if !defined(MA_EMSCRIPTEN)
-static void ma_sleep(ma_uint32 milliseconds)
-{
-#ifdef MA_WIN32
-    ma_sleep__win32(milliseconds);
-#endif
-#ifdef MA_POSIX
-    ma_sleep__posix(milliseconds);
-#endif
-}
-#endif
 
-#if !defined(MA_EMSCRIPTEN)
-static MA_INLINE void ma_yield()
-{
-#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
-    /* x86/x64 */
-    #if defined(_MSC_VER) && !defined(__clang__)
-        #if _MSC_VER >= 1400
-            _mm_pause();
-        #else
-            __asm pause;
-        #endif
-    #else
-        __asm__ __volatile__ ("pause");
-    #endif
-#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 6) || (defined(_M_ARM) && _M_ARM >= 6)
-    /* ARM */
-    #if defined(_MSC_VER)
-        /* Apparently there is a __yield() intrinsic that's compatible with ARM, but I cannot find documentation for it nor can I find where it's declared. */
-        __yield();
-    #else
-        __asm__ __volatile__ ("yield");
-    #endif
-#else
-    /* Unknown or unsupported architecture. No-op. */
-#endif
-}
-#endif
-
-
 MA_API ma_result ma_mutex_init(ma_mutex* pMutex)
 {
     if (pMutex == NULL) {
@@ -9178,7 +9157,7 @@
 MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore)
 {
     if (pSemaphore == NULL) {
-        MA_ASSERT(MA_FALSE);    /* Fire an assert to the caller is aware of this bug. */
+        MA_ASSERT(MA_FALSE);    /* Fire an assert so the caller is aware of this bug. */
         return MA_INVALID_ARGS;
     }
 
@@ -9193,7 +9172,7 @@
 MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore)
 {
     if (pSemaphore == NULL) {
-        MA_ASSERT(MA_FALSE);    /* Fire an assert to the caller is aware of this bug. */
+        MA_ASSERT(MA_FALSE);    /* Fire an assert so the caller is aware of this bug. */
         return;
     }
 
@@ -9208,7 +9187,7 @@
 MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore)
 {
     if (pSemaphore == NULL) {
-        MA_ASSERT(MA_FALSE);    /* Fire an assert to the caller is aware of this bug. */
+        MA_ASSERT(MA_FALSE);    /* Fire an assert so the caller is aware of this bug. */
         return MA_INVALID_ARGS;
     }
 
@@ -9223,7 +9202,7 @@
 MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore)
 {
     if (pSemaphore == NULL) {
-        MA_ASSERT(MA_FALSE);    /* Fire an assert to the caller is aware of this bug. */
+        MA_ASSERT(MA_FALSE);    /* Fire an assert so the caller is aware of this bug. */
         return MA_INVALID_ARGS;
     }
 
@@ -9234,6 +9213,12 @@
     return ma_semaphore_release__posix(pSemaphore);
 #endif
 }
+#else
+/* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */
+#ifndef MA_NO_DEVICE_IO
+#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO";
+#endif
+#endif  /* MA_NO_THREADING */
 
 
 /************************************************************************************************************************************************************
@@ -10203,7 +10188,7 @@
             break;
         }
 
-        result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInDeviceFormat, pFramesInClientFormat);  /* Safe cast. */
+        result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat, pFramesInClientFormat);  /* Safe cast. */
         if (result != MA_SUCCESS) {
             ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer.", result);
             break;
@@ -10432,7 +10417,7 @@
             }
 
             /* Getting here means a suspend or kill operation has been requested. */
-            c89atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS);
+            c89atomic_exchange_32((c89atomic_uint32*)&pDevice->null_device.operationResult, MA_SUCCESS);
             ma_event_signal(&pDevice->null_device.operationCompletionEvent);
             continue;
         }
@@ -10446,7 +10431,7 @@
             ma_timer_init(&pDevice->null_device.timer);
 
             /* We're done. */
-            c89atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS);
+            c89atomic_exchange_32((c89atomic_uint32*)&pDevice->null_device.operationResult, MA_SUCCESS);
             ma_event_signal(&pDevice->null_device.operationCompletionEvent);
             continue;
         }
@@ -10454,7 +10439,7 @@
         /* Killing the device means we need to get out of this loop so that this thread can terminate. */
         if (pDevice->null_device.operation == MA_DEVICE_OP_KILL__NULL) {
             c89atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);
-            c89atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS);
+            c89atomic_exchange_32((c89atomic_uint32*)&pDevice->null_device.operationResult, MA_SUCCESS);
             ma_event_signal(&pDevice->null_device.operationCompletionEvent);
             break;
         }
@@ -10462,7 +10447,7 @@
         /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */
         if (pDevice->null_device.operation == MA_DEVICE_OP_NONE__NULL) {
             MA_ASSERT(MA_FALSE);  /* <-- Trigger this in debug mode to ensure developers are aware they're doing something wrong (or there's a bug in a miniaudio). */
-            c89atomic_exchange_32(&pDevice->null_device.operationResult, MA_INVALID_OPERATION);
+            c89atomic_exchange_32((c89atomic_uint32*)&pDevice->null_device.operationResult, (c89atomic_uint32)MA_INVALID_OPERATION);
             ma_event_signal(&pDevice->null_device.operationCompletionEvent);
             continue;   /* Continue the loop. Don't terminate. */
         }
@@ -10602,7 +10587,7 @@
         ma_strncpy_s(pDevice->capture.name,  sizeof(pDevice->capture.name),  "NULL Capture Device",  (size_t)-1);
         pDevice->capture.internalFormat             = pConfig->capture.format;
         pDevice->capture.internalChannels           = pConfig->capture.channels;
-        ma_channel_map_copy(pDevice->capture.internalChannelMap, pConfig->capture.channelMap, pConfig->capture.channels);
+        ma_channel_map_copy(pDevice->capture.internalChannelMap, pConfig->capture.channelMap, ma_min(pConfig->capture.channels, MA_MAX_CHANNELS));
         pDevice->capture.internalPeriodSizeInFrames = periodSizeInFrames;
         pDevice->capture.internalPeriods            = pConfig->periods;
     }
@@ -10610,7 +10595,7 @@
         ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "NULL Playback Device", (size_t)-1);
         pDevice->playback.internalFormat             = pConfig->playback.format;
         pDevice->playback.internalChannels           = pConfig->playback.channels;
-        ma_channel_map_copy(pDevice->playback.internalChannelMap, pConfig->playback.channelMap, pConfig->playback.channels);
+        ma_channel_map_copy(pDevice->playback.internalChannelMap, pConfig->playback.channelMap, ma_min(pConfig->playback.channels, MA_MAX_CHANNELS));
         pDevice->playback.internalPeriodSizeInFrames = periodSizeInFrames;
         pDevice->playback.internalPeriods            = pConfig->periods;
     }
@@ -11101,8 +11086,6 @@
 #define WAVE_FORMAT_IEEE_FLOAT  0x0003
 #endif
 
-static GUID MA_GUID_NULL = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
-
 /* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
 static ma_uint8 ma_channel_id_to_ma__win32(DWORD id)
 {
@@ -11159,13 +11142,13 @@
 }
 
 /* Converts a channel mapping to a Win32-style channel mask. */
-static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel channelMap[MA_MAX_CHANNELS], ma_uint32 channels)
+static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels)
 {
     DWORD dwChannelMask = 0;
     ma_uint32 iChannel;
 
     for (iChannel = 0; iChannel < channels; ++iChannel) {
-        dwChannelMask |= ma_channel_id_to_win32(channelMap[iChannel]);
+        dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]);
     }
 
     return dwChannelMask;
@@ -11172,26 +11155,26 @@
 }
 
 /* Converts a Win32-style channel mask to a miniaudio channel map. */
-static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
+static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap)
 {
     if (channels == 1 && dwChannelMask == 0) {
-        channelMap[0] = MA_CHANNEL_MONO;
+        pChannelMap[0] = MA_CHANNEL_MONO;
     } else if (channels == 2 && dwChannelMask == 0) {
-        channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-        channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+        pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+        pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
     } else {
         if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) {
-            channelMap[0] = MA_CHANNEL_MONO;
+            pChannelMap[0] = MA_CHANNEL_MONO;
         } else {
             /* Just iterate over each bit. */
             ma_uint32 iChannel = 0;
             ma_uint32 iBit;
 
-            for (iBit = 0; iBit < 32; ++iBit) {
+            for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {
                 DWORD bitValue = (dwChannelMask & (1UL << iBit));
                 if (bitValue != 0) {
                     /* The bit is set. */
-                    channelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue);
+                    pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue);
                     iChannel += 1;
                 }
             }
@@ -11208,6 +11191,12 @@
 #define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b)
 #endif
 
+static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid)
+{
+    static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
+    return ma_is_guid_equal(guid, &nullguid);
+}
+
 static ma_format ma_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF)
 {
     MA_ASSERT(pWF != NULL);
@@ -11921,16 +11910,38 @@
     return (ULONG)newRefCount;
 }
 
-
 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState)
 {
+    ma_bool32 isThisDevice = MA_FALSE;
+
 #ifdef MA_DEBUG_OUTPUT
     /*printf("IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);*/
 #endif
 
-    (void)pThis;
-    (void)pDeviceID;
-    (void)dwNewState;
+    if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) {
+        return S_OK;
+    }
+
+    /*
+    There have been reports of a hang when a playback device is disconnected. The idea with this code is to explicitly stop the device if we detect
+    that the device is disabled or has been unplugged.
+    */
+    if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) {
+        if (wcscmp(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) {
+            isThisDevice = MA_TRUE;
+        }
+    }
+
+    if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) {
+        if (wcscmp(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) {
+            isThisDevice = MA_TRUE;
+        }
+    }
+
+    if (isThisDevice) {
+        ma_device_stop(pThis->pDevice);
+    }
+    
     return S_OK;
 }
 
@@ -12122,6 +12133,11 @@
                     ma_bool32 found;
                     ma_uint32 iFormat;
 
+                    /* Make sure we don't overflow the channel map. */
+                    if (channels > MA_MAX_CHANNELS) {
+                        channels = MA_MAX_CHANNELS;
+                    }
+
                     ma_get_standard_channel_map(ma_standard_channel_map_microsoft, channels, defaultChannelMap);
 
                     MA_ZERO_OBJECT(&wf);
@@ -12205,6 +12221,8 @@
     MA_ASSERT(pContext           != NULL);
     MA_ASSERT(ppDeviceEnumerator != NULL);
 
+    *ppDeviceEnumerator = NULL; /* Safety. */
+
     hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
     if (FAILED(hr)) {
         return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr));
@@ -13219,6 +13237,8 @@
 static ma_result ma_device_init__wasapi(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
 {
     ma_result result = MA_SUCCESS;
+    HRESULT hr;
+    ma_IMMDeviceEnumerator* pDeviceEnumerator;
 
     (void)pContext;
 
@@ -13387,8 +13407,9 @@
     }
 
     /*
-    We need to get notifications of when the default device changes. We do this through a device enumerator by
-    registering a IMMNotificationClient with it. We only care about this if it's the default device.
+    We need to register a notification client to detect when the device has been disabled, unplugged or re-routed (when the default device changes). When
+    we are connecting to the default device we want to do automatic stream routing when the device is disabled or unplugged. Otherwise we want to just
+    stop the device outright and let the application handle it.
     */
 #ifdef MA_WIN32_DESKTOP
     if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) {
@@ -13398,27 +13419,24 @@
         if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) {
             pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE;
         }
+    }
 
-        if (pDevice->wasapi.allowCaptureAutoStreamRouting || pDevice->wasapi.allowPlaybackAutoStreamRouting) {
-            ma_IMMDeviceEnumerator* pDeviceEnumerator;
-            HRESULT hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
-            if (FAILED(hr)) {
-                ma_device_uninit__wasapi(pDevice);
-                return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr));
-            }
+    hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
+    if (FAILED(hr)) {
+        ma_device_uninit__wasapi(pDevice);
+        return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr));
+    }
 
-            pDevice->wasapi.notificationClient.lpVtbl  = (void*)&g_maNotificationCientVtbl;
-            pDevice->wasapi.notificationClient.counter = 1;
-            pDevice->wasapi.notificationClient.pDevice = pDevice;
+    pDevice->wasapi.notificationClient.lpVtbl  = (void*)&g_maNotificationCientVtbl;
+    pDevice->wasapi.notificationClient.counter = 1;
+    pDevice->wasapi.notificationClient.pDevice = pDevice;
 
-            hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient);
-            if (SUCCEEDED(hr)) {
-                pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator;
-            } else {
-                /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */
-                ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
-            }
-        }
+    hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient);
+    if (SUCCEEDED(hr)) {
+        pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator;
+    } else {
+        /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */
+        ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
     }
 #endif
 
@@ -13514,13 +13532,19 @@
     MA_ASSERT(pDevice != NULL);
 
     /*
-    We need to explicitly signal the capture event in loopback mode to ensure we return from WaitForSingleObject() when nothing is being played. When nothing
+    It's possible for the main loop to get stuck if the device is disconnected.
+    
+    In loopback mode it's possible for WaitForSingleObject() to get stuck in a deadlock when nothing is being played. When nothing
     is being played, the event is never signalled internally by WASAPI which means we will deadlock when stopping the device.
     */
-    if (pDevice->type == ma_device_type_loopback) {
+    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_duplex) {
         SetEvent((HANDLE)pDevice->wasapi.hEventCapture);
     }
 
+    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
+        SetEvent((HANDLE)pDevice->wasapi.hEventPlayback);
+    }
+
     return MA_SUCCESS;
 }
 
@@ -13591,7 +13615,7 @@
                 if (pMappedDeviceBufferPlayback == NULL) {
                     /* WASAPI is weird with exclusive mode. You need to wait on the event _before_ querying the available frames. */
                     if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
-                        if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) {
+                        if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
                             return MA_ERROR;   /* Wait failed. */
                         }
                     }
@@ -13615,7 +13639,7 @@
                     if (framesAvailablePlayback == 0) {
                         /* In exclusive mode we waited at the top. */
                         if (pDevice->playback.shareMode != ma_share_mode_exclusive) {
-                            if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) {
+                            if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
                                 return MA_ERROR;   /* Wait failed. */
                             }
                         }
@@ -13640,7 +13664,7 @@
                     /* Try grabbing some captured data if we haven't already got a mapped buffer. */
                     if (pMappedDeviceBufferCapture == NULL) {
                         if (pDevice->capture.shareMode == ma_share_mode_shared) {
-                            if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) {
+                            if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
                                 return MA_ERROR;   /* Wait failed. */
                             }
                         }
@@ -13657,7 +13681,7 @@
                         if (framesAvailableCapture == 0) {
                             /* In exclusive mode we waited at the top. */
                             if (pDevice->capture.shareMode != ma_share_mode_shared) {
-                                if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) {
+                                if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
                                     return MA_ERROR;   /* Wait failed. */
                                 }
                             }
@@ -13900,7 +13924,7 @@
                 DWORD flagsCapture;    /* Passed to IAudioCaptureClient_GetBuffer(). */
 
                 /* Wait for data to become available first. */
-                if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) {
+                if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
                     exitLoop = MA_TRUE;
                     break;   /* Wait failed. */
                 }
@@ -13998,7 +14022,7 @@
                 ma_uint32 framesAvailablePlayback;
 
                 /* Wait for space to become available first. */
-                if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) {
+                if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
                     exitLoop = MA_TRUE;
                     break;   /* Wait failed. */
                 }
@@ -14086,8 +14110,11 @@
         the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played.
         */
         if (pDevice->wasapi.isStartedPlayback) {
+            /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */
+            DWORD waitTime = pDevice->wasapi.actualPeriodSizeInFramesPlayback / pDevice->playback.internalSampleRate;
+
             if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
-                WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE);
+                WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime);
             } else {
                 ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1;
                 ma_uint32 framesAvailablePlayback;
@@ -14110,7 +14137,7 @@
                     }
                     prevFramesAvaialablePlayback = framesAvailablePlayback;
 
-                    WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE);
+                    WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime);
                     ResetEvent(pDevice->wasapi.hEventPlayback); /* Manual reset. */
                 }
             }
@@ -14858,7 +14885,7 @@
     ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext;
     MA_ASSERT(pData != NULL);
 
-    if ((pData->pDeviceID == NULL || ma_is_guid_equal(pData->pDeviceID->dsound, &MA_GUID_NULL)) && (lpGuid == NULL || ma_is_guid_equal(lpGuid, &MA_GUID_NULL))) {
+    if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) {
         /* Default device. */
         ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
         pData->found = MA_TRUE;
@@ -16217,7 +16244,7 @@
       usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component
       name, and then concatenate the name from the registry.
     */
-    if (!ma_is_guid_equal(&pCaps->NameGuid, &MA_GUID_NULL)) {
+    if (!ma_is_guid_null(&pCaps->NameGuid)) {
         wchar_t guidStrW[256];
         if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) {
             char guidStr[256];
@@ -18866,7 +18893,7 @@
         pDevice->capture.internalFormat              = internalFormat;
         pDevice->capture.internalChannels            = internalChannels;
         pDevice->capture.internalSampleRate          = internalSampleRate;
-        ma_channel_map_copy(pDevice->capture.internalChannelMap, internalChannelMap, internalChannels);
+        ma_channel_map_copy(pDevice->capture.internalChannelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS));
         pDevice->capture.internalPeriodSizeInFrames  = internalPeriodSizeInFrames;
         pDevice->capture.internalPeriods             = internalPeriods;
     } else {
@@ -18875,7 +18902,7 @@
         pDevice->playback.internalFormat             = internalFormat;
         pDevice->playback.internalChannels           = internalChannels;
         pDevice->playback.internalSampleRate         = internalSampleRate;
-        ma_channel_map_copy(pDevice->playback.internalChannelMap, internalChannelMap, internalChannels);
+        ma_channel_map_copy(pDevice->playback.internalChannelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS));
         pDevice->playback.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
         pDevice->playback.internalPeriods            = internalPeriods;
     }
@@ -20577,7 +20604,7 @@
     pDevice->pulse.pulseContextState = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext);
 }
 
-void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
+static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
 {
     ma_pa_sink_info* pInfoOut;
 
@@ -22751,14 +22778,14 @@
     }
 }
 
-static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel channelMap[MA_MAX_CHANNELS])
+static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap)
 {
     MA_ASSERT(pChannelLayout != NULL);
     
     if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
         UInt32 iChannel;
-        for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions; ++iChannel) {
-            channelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);
+        for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) {
+            pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);
         }
     } else
 #if 0
@@ -22767,10 +22794,10 @@
         UInt32 iChannel = 0;
         UInt32 iBit;
         AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap;
-        for (iBit = 0; iBit < 32; ++iBit) {
+        for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) {
             AudioChannelBitmap bit = bitmap & (1 << iBit);
             if (bit != 0) {
-                channelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit);
+                pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit);
             }
         }
     } else
@@ -22780,7 +22807,8 @@
         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);
+        UInt32 channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), channelMapCap);
+
         switch (pChannelLayout->mChannelLayoutTag)
         {
             case kAudioChannelLayoutTag_Mono:
@@ -22792,28 +22820,28 @@
             case kAudioChannelLayoutTag_Binaural:
             case kAudioChannelLayoutTag_Ambisonic_B_Format:
             {
-                ma_get_standard_channel_map(ma_standard_channel_map_default, channelCount, channelMap);
+                ma_get_standard_channel_map(ma_standard_channel_map_default, channelCount, pChannelMap);
             } break;
             
             case kAudioChannelLayoutTag_Octagonal:
             {
-                channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
-                channelMap[6] = MA_CHANNEL_SIDE_LEFT;
+                pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
+                pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
             } /* Intentional fallthrough. */
             case kAudioChannelLayoutTag_Hexagonal:
             {
-                channelMap[5] = MA_CHANNEL_BACK_CENTER;
+                pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
             } /* Intentional fallthrough. */
             case kAudioChannelLayoutTag_Pentagonal:
             {
-                channelMap[4] = MA_CHANNEL_FRONT_CENTER;
+                pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
             } /* Intentional fallghrough. */
             case kAudioChannelLayoutTag_Quadraphonic:
             {
-                channelMap[3] = MA_CHANNEL_BACK_RIGHT;
-                channelMap[2] = MA_CHANNEL_BACK_LEFT;
-                channelMap[1] = MA_CHANNEL_RIGHT;
-                channelMap[0] = MA_CHANNEL_LEFT;
+                pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
+                pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+                pChannelMap[1] = MA_CHANNEL_RIGHT;
+                pChannelMap[0] = MA_CHANNEL_LEFT;
             } break;
             
             /* TODO: Add support for more tags here. */
@@ -22820,7 +22848,7 @@
         
             default:
             {
-                ma_get_standard_channel_map(ma_standard_channel_map_default, channelCount, channelMap);
+                ma_get_standard_channel_map(ma_standard_channel_map_default, channelCount, pChannelMap);
             } break;
         }
     }
@@ -23093,7 +23121,7 @@
 }
 
 #if 0
-static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel channelMap[MA_MAX_CHANNELS])
+static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
 {
     AudioChannelLayout* pChannelLayout;
     ma_result result;
@@ -23105,7 +23133,7 @@
         return result;  /* Rather than always failing here, would it be more robust to simply assume a default? */
     }
     
-    result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, channelMap);
+    result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
     if (result != MA_SUCCESS) {
         ma_free(pChannelLayout, &pContext->allocationCallbacks);
         return result;
@@ -23585,7 +23613,7 @@
     return MA_SUCCESS;
 }
 
-static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel channelMap[MA_MAX_CHANNELS])
+static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
 {
     AudioUnitScope deviceScope;
     AudioUnitElement deviceBus;
@@ -23620,7 +23648,7 @@
         return ma_result_from_OSStatus(status);
     }
     
-    result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, channelMap);
+    result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
     if (result != MA_SUCCESS) {
         ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks);
         return result;
@@ -23641,6 +23669,15 @@
     return strcmp(pID0->coreaudio, pID1->coreaudio) == 0;
 }
 
+#if !defined(MA_APPLE_DESKTOP)
+static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo)
+{
+    MA_ZERO_OBJECT(pInfo);
+    ma_strncpy_s(pInfo->name,         sizeof(pInfo->name),         [pPortDesc.portName UTF8String], (size_t)-1);
+    ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID      UTF8String], (size_t)-1);
+}
+#endif
+
 static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
 {
 #if defined(MA_APPLE_DESKTOP)
@@ -23680,19 +23717,22 @@
     
     ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
 #else
-    /* Only supporting default devices on non-Desktop platforms. */
     ma_device_info info;
+    NSArray *pInputs  = [[[AVAudioSession sharedInstance] currentRoute] inputs];
+    NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
     
-    MA_ZERO_OBJECT(&info);
-    ma_strncpy_s(info.name, sizeof(info.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
-    if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
-        return MA_SUCCESS;
+    for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
+        ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
+        if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
+            return MA_SUCCESS;
+        }
     }
     
-    MA_ZERO_OBJECT(&info);
-    ma_strncpy_s(info.name, sizeof(info.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
-    if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
-        return MA_SUCCESS;
+    for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
+        ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
+        if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
+            return MA_SUCCESS;
+        }
     }
 #endif
     
@@ -23808,12 +23848,41 @@
         AudioUnitElement formatElement;
         AudioStreamBasicDescription bestFormat;
         UInt32 propSize;
-
-        if (deviceType == ma_device_type_playback) {
-            ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
+        
+        /* We want to ensure we use a consistent device name to device enumeration. */
+        if (pDeviceID != NULL) {
+            ma_bool32 found = MA_FALSE;
+            if (deviceType == ma_device_type_playback) {
+                NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
+                for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
+                    if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
+                        ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
+                        found = MA_TRUE;
+                        break;
+                    }
+                }
+            } else {
+                NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
+                for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
+                    if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
+                        ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
+                        found = MA_TRUE;
+                        break;
+                    }
+                }
+            }
+            
+            if (!found) {
+                return MA_DOES_NOT_EXIST;
+            }
         } else {
-            ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
+            if (deviceType == ma_device_type_playback) {
+                ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
+            } else {
+                ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
+            }
         }
+        
     
         /*
         Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is
@@ -23927,6 +23996,7 @@
         }
     } else {
         /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */
+        MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS);   /* This should heve been validated at initialization time. */
         
         /*
         For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
@@ -24049,6 +24119,7 @@
         }
     } else {
         /* This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This assumes each buffer is the same size. */
+        MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS);    /* This should have been validated at initialization time. */
         
         /*
         For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
@@ -24166,6 +24237,7 @@
 }
 
 #if defined(MA_APPLE_DESKTOP)
+static ma_spinlock g_DeviceTrackingInitLock_CoreAudio = 0;  /* A spinlock for mutal exclusion of the init/uninit of the global tracking data. Initialization to 0 is what we need. */
 static ma_uint32   g_DeviceTrackingInitCounter_CoreAudio = 0;
 static ma_mutex    g_DeviceTrackingMutex_CoreAudio;
 static ma_device** g_ppTrackedDevices_CoreAudio = NULL;
@@ -24249,7 +24321,8 @@
 {
     MA_ASSERT(pContext != NULL);
     
-    if (c89atomic_fetch_add_32(&g_DeviceTrackingInitCounter_CoreAudio, 1) == 0) {
+    ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
+    {
         AudioObjectPropertyAddress propAddress;
         propAddress.mScope    = kAudioObjectPropertyScopeGlobal;
         propAddress.mElement  = kAudioObjectPropertyElementMaster;
@@ -24262,7 +24335,8 @@
         propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
         ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
     }
-    
+    ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
+
     return MA_SUCCESS;
 }
 
@@ -24270,7 +24344,8 @@
 {
     MA_ASSERT(pContext != NULL);
     
-    if (c89atomic_fetch_sub_32(&g_DeviceTrackingInitCounter_CoreAudio, 1) == 1) {
+    ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
+    {
         AudioObjectPropertyAddress propAddress;
         propAddress.mScope    = kAudioObjectPropertyScopeGlobal;
         propAddress.mElement  = kAudioObjectPropertyElementMaster;
@@ -24287,6 +24362,7 @@
         
         ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio);
     }
+    ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
     
     return MA_SUCCESS;
 }
@@ -24293,15 +24369,8 @@
 
 static ma_result ma_device__track__coreaudio(ma_device* pDevice)
 {
-    ma_result result;
-
     MA_ASSERT(pDevice != NULL);
     
-    result = ma_context__init_device_tracking__coreaudio(pDevice->pContext);
-    if (result != MA_SUCCESS) {
-        return result;
-    }
-    
     ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
     {
         /* Allocate memory if required. */
@@ -24336,8 +24405,6 @@
 
 static ma_result ma_device__untrack__coreaudio(ma_device* pDevice)
 {
-    ma_result result;
-    
     MA_ASSERT(pDevice != NULL);
     
     ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
@@ -24366,11 +24433,6 @@
     }
     ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
 
-    result = ma_context__uninit_device_tracking__coreaudio(pDevice->pContext);
-    if (result != MA_SUCCESS) {
-        return result;
-    }
-    
     return MA_SUCCESS;
 }
 #endif
@@ -24622,6 +24684,28 @@
         ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
         return ma_result_from_OSStatus(result);
     }
+#else
+    /*
+    For some reason it looks like Apple is only allowing selection of the input device. There does not appear to be any way to change
+    the default output route. I have no idea why this is like this, but for now we'll only be able to configure capture devices.
+    */
+    if (pDeviceID != NULL) {
+        if (deviceType == ma_device_type_capture) {
+            ma_bool32 found = MA_FALSE;
+            NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
+            for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
+                if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
+                    [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil];
+                    found = MA_TRUE;
+                    break;
+                }
+            }
+            
+            if (found == MA_FALSE) {
+                return MA_DOES_NOT_EXIST;
+            }
+        }
+    }
 #endif
     
     /*
@@ -24724,6 +24808,11 @@
         pData->channelsOut = bestFormat.mChannelsPerFrame;
         pData->sampleRateOut = bestFormat.mSampleRate;
     }
+
+    /* Clamp the channel count for safety. */
+    if (pData->channelsOut > MA_MAX_CHANNELS) {
+        pData->channelsOut = MA_MAX_CHANNELS;
+    }
     
     /*
     Internal channel map. This is weird in my testing. If I use the AudioObject to get the
@@ -24733,11 +24822,11 @@
     I'm going to fall back to a default assumption in these cases.
     */
 #if defined(MA_APPLE_DESKTOP)
-    result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut);
+    result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut);
     if (result != MA_SUCCESS) {
     #if 0
         /* Try falling back to the channel map from the AudioObject. */
-        result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut);
+        result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut);
         if (result != MA_SUCCESS) {
             return result;
         }
@@ -25207,6 +25296,14 @@
     MA_ASSERT(pContext != NULL);
     MA_ASSERT(pContext->backend == ma_backend_coreaudio);
     
+#if defined(MA_APPLE_MOBILE)
+    if (!pContext->coreaudio.noAudioSessionDeactivate) {
+        if (![[AVAudioSession sharedInstance] setActive:false error:nil]) {
+            return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session.", MA_FAILED_TO_INIT_BACKEND);
+        }
+    }
+#endif
+    
 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
     ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
     ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
@@ -25213,6 +25310,10 @@
     ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
 #endif
 
+#if !defined(MA_APPLE_MOBILE)
+    ma_context__uninit_device_tracking__coreaudio(pContext);
+#endif
+
     (void)pContext;
     return MA_SUCCESS;
 }
@@ -25240,6 +25341,10 @@
 
 static ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma_context* pContext)
 {
+#if !defined(MA_APPLE_MOBILE)
+    ma_result result;
+#endif
+
     MA_ASSERT(pConfig != NULL);
     MA_ASSERT(pContext != NULL);
 
@@ -25275,6 +25380,12 @@
                 }
             }
         }
+        
+        if (!pConfig->coreaudio.noAudioSessionActivate) {
+            if (![pAudioSession setActive:true error:nil]) {
+                return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to activate audio session.", MA_FAILED_TO_INIT_BACKEND);
+            }
+        }
     }
 #endif
     
@@ -25386,15 +25497,29 @@
     
         pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
         if (pContext->coreaudio.component == NULL) {
-    #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
+        #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
             ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
             ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
             ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
-    #endif
+        #endif
             return MA_FAILED_TO_INIT_BACKEND;
         }
     }
+    
+#if !defined(MA_APPLE_MOBILE)
+    result = ma_context__init_device_tracking__coreaudio(pContext);
+    if (result != MA_SUCCESS) {
+    #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
+        ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
+        ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
+        ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
+    #endif
+        return result;
+    }
+#endif
 
+    pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate;
+
     return MA_SUCCESS;
 }
 #endif  /* Core Audio */
@@ -28800,10 +28925,13 @@
 #include <SLES/OpenSLES_Android.h>
 #endif
 
+typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired);
+
 /* OpenSL|ES has one-per-application objects :( */
-SLObjectItf g_maEngineObjectSL = NULL;
-SLEngineItf g_maEngineSL = NULL;
-ma_uint32 g_maOpenSLInitCounter = 0;
+static SLObjectItf g_maEngineObjectSL    = NULL;
+static SLEngineItf g_maEngineSL          = NULL;
+static ma_uint32   g_maOpenSLInitCounter = 0;
+static ma_spinlock g_maOpenSLSpinlock    = 0;   /* For init/uninit. */
 
 #define MA_OPENSL_OBJ(p)         (*((SLObjectItf)(p)))
 #define MA_OPENSL_OUTPUTMIX(p)   (*((SLOutputMixItf)(p)))
@@ -28897,12 +29025,12 @@
 }
 
 /* Converts a channel mapping to an OpenSL-style channel mask. */
-static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel channelMap[MA_MAX_CHANNELS], ma_uint32 channels)
+static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels)
 {
     SLuint32 channelMask = 0;
     ma_uint32 iChannel;
     for (iChannel = 0; iChannel < channels; ++iChannel) {
-        channelMask |= ma_channel_id_to_opensl(channelMap[iChannel]);
+        channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]);
     }
 
     return channelMask;
@@ -28909,25 +29037,25 @@
 }
 
 /* Converts an OpenSL-style channel mask to a miniaudio channel map. */
-static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
+static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap)
 {
     if (channels == 1 && channelMask == 0) {
-        channelMap[0] = MA_CHANNEL_MONO;
+        pChannelMap[0] = MA_CHANNEL_MONO;
     } else if (channels == 2 && channelMask == 0) {
-        channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-        channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+        pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+        pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
     } else {
         if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) {
-            channelMap[0] = MA_CHANNEL_MONO;
+            pChannelMap[0] = MA_CHANNEL_MONO;
         } else {
             /* Just iterate over each bit. */
             ma_uint32 iChannel = 0;
             ma_uint32 iBit;
-            for (iBit = 0; iBit < 32; ++iBit) {
+            for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {
                 SLuint32 bitValue = (channelMask & (1UL << iBit));
                 if (bitValue != 0) {
                     /* The bit is set. */
-                    channelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue);
+                    pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue);
                     iChannel += 1;
                 }
             }
@@ -29019,7 +29147,7 @@
     SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]);
 
     SLAudioIODeviceCapabilitiesItf deviceCaps;
-    SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
+    SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
     if (resultSL != SL_RESULT_SUCCESS) {
         /* The interface may not be supported so just report a default device. */
         goto return_default_device;
@@ -29125,7 +29253,7 @@
     */
 #if 0 && !defined(MA_ANDROID)
     SLAudioIODeviceCapabilitiesItf deviceCaps;
-    SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
+    SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
     if (resultSL != SL_RESULT_SUCCESS) {
         /* The interface may not be supported so just report a default device. */
         goto return_default_device;
@@ -29375,7 +29503,7 @@
     return MA_SUCCESS;
 }
 
-static ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap)
+static ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
 {
     ma_bool32 isFloatingPoint = MA_FALSE;
 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
@@ -29402,7 +29530,7 @@
 
     *pChannels   = pDataFormat->numChannels;
     *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000;
-    ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, pDataFormat->numChannels, pChannelMap);
+    ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap);
 
     return MA_SUCCESS;
 }
@@ -29414,7 +29542,7 @@
     SLresult resultSL;
     ma_uint32 periodSizeInFrames;
     size_t bufferSizeInBytes;
-    const SLInterfaceID itfIDs1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
+    SLInterfaceID itfIDs1[1];
     const SLboolean itfIDsRequired1[] = {SL_BOOLEAN_TRUE};
 #endif
 
@@ -29435,6 +29563,8 @@
     queues).
     */
 #ifdef MA_ANDROID
+    itfIDs1[0] = (SLInterfaceID)pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
+
     /* No exclusive mode with OpenSL|ES. */
     if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
         ((pConfig->deviceType == ma_device_type_capture  || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode  == ma_share_mode_exclusive)) {
@@ -29491,13 +29621,13 @@
             return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.", ma_result_from_OpenSL(resultSL));
         }
 
-        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_IID_RECORD, &pDevice->opensl.pAudioRecorder);
+        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder);
         if (resultSL != SL_RESULT_SUCCESS) {
             ma_device_uninit__opensl(pDevice);
             return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.", ma_result_from_OpenSL(resultSL));
         }
 
-        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture);
+        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture);
         if (resultSL != SL_RESULT_SUCCESS) {
             ma_device_uninit__opensl(pDevice);
             return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", ma_result_from_OpenSL(resultSL));
@@ -29510,7 +29640,7 @@
         }
 
         /* The internal format is determined by the "pcm" object. */
-        ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDevice->capture.internalFormat, &pDevice->capture.internalChannels, &pDevice->capture.internalSampleRate, pDevice->capture.internalChannelMap);
+        ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDevice->capture.internalFormat, &pDevice->capture.internalChannels, &pDevice->capture.internalSampleRate, pDevice->capture.internalChannelMap, ma_countof(pDevice->capture.internalChannelMap));
 
         /* Buffer. */
         periodSizeInFrames = pConfig->periodSizeInFrames;
@@ -29550,7 +29680,7 @@
             return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.", ma_result_from_OpenSL(resultSL));
         }
 
-        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix);
+        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix);
         if (resultSL != SL_RESULT_SUCCESS) {
             ma_device_uninit__opensl(pDevice);
             return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.", ma_result_from_OpenSL(resultSL));
@@ -29594,13 +29724,13 @@
             return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.", ma_result_from_OpenSL(resultSL));
         }
 
-        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_IID_PLAY, &pDevice->opensl.pAudioPlayer);
+        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer);
         if (resultSL != SL_RESULT_SUCCESS) {
             ma_device_uninit__opensl(pDevice);
             return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.", ma_result_from_OpenSL(resultSL));
         }
 
-        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback);
+        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback);
         if (resultSL != SL_RESULT_SUCCESS) {
             ma_device_uninit__opensl(pDevice);
             return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", ma_result_from_OpenSL(resultSL));
@@ -29613,7 +29743,7 @@
         }
 
         /* The internal format is determined by the "pcm" object. */
-        ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDevice->playback.internalFormat, &pDevice->playback.internalChannels, &pDevice->playback.internalSampleRate, pDevice->playback.internalChannelMap);
+        ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDevice->playback.internalFormat, &pDevice->playback.internalChannels, &pDevice->playback.internalSampleRate, pDevice->playback.internalChannelMap, ma_countof(pDevice->playback.internalChannelMap));
 
         /* Buffer. */
         periodSizeInFrames = pConfig->periodSizeInFrames;
@@ -29799,39 +29929,136 @@
     (void)pContext;
 
     /* Uninit global data. */
-    if (g_maOpenSLInitCounter > 0) {
-        if (c89atomic_fetch_sub_32(&g_maOpenSLInitCounter, 1) == 1) {
+    ma_spinlock_lock(&g_maOpenSLSpinlock);
+    {
+        MA_ASSERT(g_maOpenSLInitCounter > 0);   /* If you've triggered this, it means you have ma_context_init/uninit mismatch. Each successful call to ma_context_init() must be matched up with a call to ma_context_uninit(). */
+
+        g_maOpenSLInitCounter -= 1;
+        if (g_maOpenSLInitCounter == 0) {
             (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
         }
     }
+    ma_spinlock_unlock(&g_maOpenSLSpinlock);
 
     return MA_SUCCESS;
 }
 
-static ma_result ma_context_init__opensl(const ma_context_config* pConfig, ma_context* pContext)
+static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle)
 {
-    MA_ASSERT(pContext != NULL);
+    /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */
+    ma_handle* p = (ma_handle*)ma_dlsym(pContext, pContext->opensl.libOpenSLES, pName);
+    if (p == NULL) {
+        ma_post_log_messagef(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Cannot find symbol %s", pName);
+        return MA_NO_BACKEND;
+    }
 
-    (void)pConfig;
+    *pHandle = *p;
+    return MA_SUCCESS;
+}
 
-    /* Initialize global data first if applicable. */
-    if (c89atomic_fetch_add_32(&g_maOpenSLInitCounter, 1) == 0) {
-        SLresult resultSL = slCreateEngine(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL);
+static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext)
+{
+    g_maOpenSLInitCounter += 1;
+    if (g_maOpenSLInitCounter == 1) {
+        SLresult resultSL;
+
+        resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL);
         if (resultSL != SL_RESULT_SUCCESS) {
-            c89atomic_fetch_sub_32(&g_maOpenSLInitCounter, 1);
+            g_maOpenSLInitCounter -= 1;
             return ma_result_from_OpenSL(resultSL);
         }
 
         (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE);
 
-        resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_ENGINE, &g_maEngineSL);
+        resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL);
         if (resultSL != SL_RESULT_SUCCESS) {
             (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
-            c89atomic_fetch_sub_32(&g_maOpenSLInitCounter, 1);
+            g_maOpenSLInitCounter -= 1;
             return ma_result_from_OpenSL(resultSL);
         }
     }
 
+    return MA_SUCCESS;
+}
+
+static ma_result ma_context_init__opensl(const ma_context_config* pConfig, ma_context* pContext)
+{
+    ma_result result;
+    size_t i;
+    const char* libOpenSLESNames[] = {
+        "libOpenSLES.so"
+    };
+
+    MA_ASSERT(pContext != NULL);
+
+    (void)pConfig;
+
+    /*
+    Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One
+    report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime
+    and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any
+    references to the symbols and will hopefully skip the checks.
+    */
+    for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) {
+        pContext->opensl.libOpenSLES = ma_dlopen(pContext, libOpenSLESNames[i]);
+        if (pContext->opensl.libOpenSLES != NULL) {
+            break;
+        }
+    }
+
+    if (pContext->opensl.libOpenSLES == NULL) {
+        return MA_NO_BACKEND;   /* Couldn't find libOpenSLES.so */
+    }
+
+    result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE);
+    if (result != MA_SUCCESS) {
+        return result;
+    }
+
+    result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES);
+    if (result != MA_SUCCESS) {
+        return result;
+    }
+
+    result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE);
+    if (result != MA_SUCCESS) {
+        return result;
+    }
+
+    result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD);
+    if (result != MA_SUCCESS) {
+        return result;
+    }
+
+    result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY);
+    if (result != MA_SUCCESS) {
+        return result;
+    }
+
+    result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX);
+    if (result != MA_SUCCESS) {
+        return result;
+    }
+
+    pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(pContext, pContext->opensl.libOpenSLES, "slCreateEngine");
+    if (pContext->opensl.slCreateEngine == NULL) {
+        ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Cannot find symbol slCreateEngine.");
+        return MA_NO_BACKEND;
+    }
+
+
+    /* Initialize global data first if applicable. */
+    ma_spinlock_lock(&g_maOpenSLSpinlock);
+    {
+        result = ma_context_init_engine_nolock__opensl(pContext);
+    }
+    ma_spinlock_unlock(&g_maOpenSLSpinlock);
+
+    if (result != MA_SUCCESS) {
+        return result;  /* Failed to initialize the OpenSL engine. */
+    }
+
+
     pContext->isBackendAsynchronous = MA_TRUE;
 
     pContext->onUninit        = ma_context_uninit__opensl;
@@ -30051,10 +30278,22 @@
         return MA_NO_DEVICE;
     }
 
-    /* Try calculating an appropriate buffer size. */
+    /*
+    Try calculating an appropriate buffer size. There have been reports of the default buffer size being too small on some browsers. If we're using default buffer size, we'll make sure
+    the period size is a big biffer than our standard defaults.
+    */
     internalPeriodSizeInFrames = pConfig->periodSizeInFrames;
     if (internalPeriodSizeInFrames == 0) {
-        internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pConfig->sampleRate);
+        ma_uint32 periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
+        if (pDevice->usingDefaultBufferSize) {
+            if (pConfig->performanceProfile == ma_performance_profile_low_latency) {
+                periodSizeInMilliseconds = 33;  /* 1 frame @ 30 FPS */
+            } else {
+                periodSizeInMilliseconds = 333;
+            }
+        }
+
+        internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, pConfig->sampleRate);
     }
 
     /* The size of the buffer must be a power of 2 and between 256 and 16384. */
@@ -30462,8 +30701,8 @@
     if (channelMap[0] != MA_CHANNEL_NONE) {
         ma_uint32 iChannel;
 
-        if (channels == 0) {
-            return MA_FALSE;   /* No channels. */
+        if (channels == 0 || channels > MA_MAX_CHANNELS) {
+            return MA_FALSE;   /* Channel count out of range. */
         }
 
         /* A channel cannot be present in the channel map more than once. */
@@ -30495,6 +30734,7 @@
             pDevice->capture.channels = pDevice->capture.internalChannels;
         }
         if (pDevice->capture.usingDefaultChannelMap) {
+            MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS);
             if (pDevice->capture.internalChannels == pDevice->capture.channels) {
                 ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels);
             } else {
@@ -30511,6 +30751,7 @@
             pDevice->playback.channels = pDevice->playback.internalChannels;
         }
         if (pDevice->playback.usingDefaultChannelMap) {
+            MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS);
             if (pDevice->playback.internalChannels == pDevice->playback.channels) {
                 ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels);
             } else {
@@ -30527,7 +30768,7 @@
         }
     }
 
-    /* PCM converters. */
+    /* Data converters. */
     if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
         /* Converting from internal device format to client format. */
         ma_data_converter_config converterConfig = ma_data_converter_config_init_default();
@@ -30534,11 +30775,11 @@
         converterConfig.formatIn                   = pDevice->capture.internalFormat;
         converterConfig.channelsIn                 = pDevice->capture.internalChannels;
         converterConfig.sampleRateIn               = pDevice->capture.internalSampleRate;
-        ma_channel_map_copy(converterConfig.channelMapIn, pDevice->capture.internalChannelMap, pDevice->capture.internalChannels);
+        ma_channel_map_copy(converterConfig.channelMapIn, pDevice->capture.internalChannelMap, ma_min(pDevice->capture.internalChannels, MA_MAX_CHANNELS));
         converterConfig.formatOut                  = pDevice->capture.format;
         converterConfig.channelsOut                = pDevice->capture.channels;
         converterConfig.sampleRateOut              = pDevice->sampleRate;
-        ma_channel_map_copy(converterConfig.channelMapOut, pDevice->capture.channelMap, pDevice->capture.channels);
+        ma_channel_map_copy(converterConfig.channelMapOut, pDevice->capture.channelMap, ma_min(pDevice->capture.channels, MA_MAX_CHANNELS));
         converterConfig.resampling.allowDynamicSampleRate = MA_FALSE;
         converterConfig.resampling.algorithm       = pDevice->resampling.algorithm;
         converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
@@ -30556,11 +30797,11 @@
         converterConfig.formatIn                   = pDevice->playback.format;
         converterConfig.channelsIn                 = pDevice->playback.channels;
         converterConfig.sampleRateIn               = pDevice->sampleRate;
-        ma_channel_map_copy(converterConfig.channelMapIn, pDevice->playback.channelMap, pDevice->playback.channels);
+        ma_channel_map_copy(converterConfig.channelMapIn, pDevice->playback.channelMap, ma_min(pDevice->playback.channels, MA_MAX_CHANNELS));
         converterConfig.formatOut                  = pDevice->playback.internalFormat;
         converterConfig.channelsOut                = pDevice->playback.internalChannels;
         converterConfig.sampleRateOut              = pDevice->playback.internalSampleRate;
-        ma_channel_map_copy(converterConfig.channelMapOut, pDevice->playback.internalChannelMap, pDevice->playback.internalChannels);
+        ma_channel_map_copy(converterConfig.channelMapOut, pDevice->playback.internalChannelMap, ma_min(pDevice->playback.internalChannels, MA_MAX_CHANNELS));
         converterConfig.resampling.allowDynamicSampleRate = MA_FALSE;
         converterConfig.resampling.algorithm       = pDevice->resampling.algorithm;
         converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
@@ -31266,6 +31507,13 @@
         }
     }
 
+    if (config.playback.pDeviceID != NULL) {
+        MA_COPY_MEMORY(&pDevice->playback.id, config.playback.pDeviceID, sizeof(pDevice->playback.id));
+    }
+    if (config.capture.pDeviceID != NULL) {
+        MA_COPY_MEMORY(&pDevice->capture.id, config.capture.pDeviceID, sizeof(pDevice->capture.id));
+    }
+
     pDevice->noPreZeroedOutputBuffer = config.noPreZeroedOutputBuffer;
     pDevice->noClip = config.noClip;
     pDevice->masterVolumeFactor = 1;
@@ -31324,8 +31572,9 @@
         pDevice->usingDefaultBufferSize = MA_TRUE;
     }
     
+    MA_ASSERT(config.capture.channels  <= MA_MAX_CHANNELS);
+    MA_ASSERT(config.playback.channels <= MA_MAX_CHANNELS);
 
-
     pDevice->type = config.deviceType;
     pDevice->sampleRate = config.sampleRate;
     pDevice->resampling.algorithm       = config.resampling.algorithm;
@@ -31636,7 +31885,6 @@
         ma_device__set_state(pDevice, MA_STATE_STOPPING);
 
         /* There's no need to wake up the thread like we do when starting. */
-
         if (pDevice->pContext->onDeviceStop) {
             result = pDevice->pContext->onDeviceStop(pDevice);
         } else {
@@ -31792,9 +32040,9 @@
 }
 
 
-MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint32 sampleCount, float factor)
+MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor)
 {
-    ma_uint32 iSample;
+    ma_uint64 iSample;
 
     if (pSamplesOut == NULL || pSamplesIn == NULL) {
         return;
@@ -31805,9 +32053,9 @@
     }
 }
 
-MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint32 sampleCount, float factor)
+MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor)
 {
-    ma_uint32 iSample;
+    ma_uint64 iSample;
 
     if (pSamplesOut == NULL || pSamplesIn == NULL) {
         return;
@@ -31818,9 +32066,9 @@
     }
 }
 
-MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint32 sampleCount, float factor)
+MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor)
 {
-    ma_uint32 iSample;
+    ma_uint64 iSample;
     ma_uint8* pSamplesOut8;
     ma_uint8* pSamplesIn8;
 
@@ -31843,9 +32091,9 @@
     }
 }
 
-MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint32 sampleCount, float factor)
+MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor)
 {
-    ma_uint32 iSample;
+    ma_uint64 iSample;
 
     if (pSamplesOut == NULL || pSamplesIn == NULL) {
         return;
@@ -31856,9 +32104,9 @@
     }
 }
 
-MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint32 sampleCount, float factor)
+MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor)
 {
-    ma_uint32 iSample;
+    ma_uint64 iSample;
 
     if (pSamplesOut == NULL || pSamplesIn == NULL) {
         return;
@@ -31869,57 +32117,57 @@
     }
 }
 
-MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint32 sampleCount, float factor)
+MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor)
 {
     ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor);
 }
 
-MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint32 sampleCount, float factor)
+MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor)
 {
     ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor);
 }
 
-MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint32 sampleCount, float factor)
+MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor)
 {
     ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor);
 }
 
-MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint32 sampleCount, float factor)
+MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor)
 {
     ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor);
 }
 
-MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint32 sampleCount, float factor)
+MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor)
 {
     ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor);
 }
 
-MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
+MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
 {
     ma_copy_and_apply_volume_factor_u8(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
 }
 
-MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
+MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
 {
     ma_copy_and_apply_volume_factor_s16(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
 }
 
-MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
+MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
 {
     ma_copy_and_apply_volume_factor_s24(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
 }
 
-MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
+MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
 {
     ma_copy_and_apply_volume_factor_s32(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
 }
 
-MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
+MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
 {
     ma_copy_and_apply_volume_factor_f32(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
 }
 
-MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor)
+MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor)
 {
     switch (format)
     {
@@ -31932,32 +32180,32 @@
     }
 }
 
-MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
+MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
 {
     ma_copy_and_apply_volume_factor_pcm_frames_u8(pPCMFrames, pPCMFrames, frameCount, channels, factor);
 }
 
-MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
+MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
 {
     ma_copy_and_apply_volume_factor_pcm_frames_s16(pPCMFrames, pPCMFrames, frameCount, channels, factor);
 }
 
-MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
+MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
 {
     ma_copy_and_apply_volume_factor_pcm_frames_s24(pPCMFrames, pPCMFrames, frameCount, channels, factor);
 }
 
-MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
+MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
 {
     ma_copy_and_apply_volume_factor_pcm_frames_s32(pPCMFrames, pPCMFrames, frameCount, channels, factor);
 }
 
-MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
+MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
 {
     ma_copy_and_apply_volume_factor_pcm_frames_f32(pPCMFrames, pPCMFrames, frameCount, channels, factor);
 }
 
-MA_API void ma_apply_volume_factor_pcm_frames(void* pPCMFrames, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor)
+MA_API void ma_apply_volume_factor_pcm_frames(void* pPCMFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor)
 {
     ma_copy_and_apply_volume_factor_pcm_frames(pPCMFrames, pPCMFrames, frameCount, format, channels, factor);
 }
@@ -34420,6 +34668,10 @@
         return MA_INVALID_ARGS;
     }
 
+    if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) {
+        return MA_INVALID_ARGS;
+    }
+
     return ma_biquad_reinit(pConfig, pBQ);
 }
 
@@ -34627,6 +34879,10 @@
         return MA_INVALID_ARGS;
     }
 
+    if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) {
+        return MA_INVALID_ARGS;
+    }
+
     return ma_lpf1_reinit(pConfig, pLPF);
 }
 
@@ -35129,6 +35385,10 @@
         return MA_INVALID_ARGS;
     }
 
+    if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) {
+        return MA_INVALID_ARGS;
+    }
+
     return ma_hpf1_reinit(pConfig, pHPF);
 }
 
@@ -36484,6 +36744,10 @@
         return MA_INVALID_ARGS;
     }
 
+    if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) {
+        return MA_INVALID_ARGS;
+    }
+
     pResampler->config = *pConfig;
 
     /* Setting the rate will set up the filter and time advances for us. */
@@ -37668,15 +37932,20 @@
     return contribution;
 }
 
-MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel channelMapIn[MA_MAX_CHANNELS], ma_uint32 channelsOut, const ma_channel channelMapOut[MA_MAX_CHANNELS], ma_channel_mix_mode mixingMode)
+MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode)
 {
     ma_channel_converter_config config;
+
+    /* Channel counts need to be clamped. */
+    channelsIn  = ma_min(channelsIn,  ma_countof(config.channelMapIn));
+    channelsOut = ma_min(channelsOut, ma_countof(config.channelMapOut));
+
     MA_ZERO_OBJECT(&config);
     config.format      = format;
     config.channelsIn  = channelsIn;
     config.channelsOut = channelsOut;
-    ma_channel_map_copy(config.channelMapIn,  channelMapIn,  channelsIn);
-    ma_channel_map_copy(config.channelMapOut, channelMapOut, channelsOut);
+    ma_channel_map_copy_or_default(config.channelMapIn,  pChannelMapIn,  channelsIn);
+    ma_channel_map_copy_or_default(config.channelMapOut, pChannelMapOut, channelsOut);
     config.mixingMode  = mixingMode;
 
     return config;
@@ -37719,6 +37988,12 @@
         return MA_INVALID_ARGS;
     }
 
+    /* Basic validation for channel counts. */
+    if (pConfig->channelsIn  < MA_MIN_CHANNELS || pConfig->channelsIn  > MA_MAX_CHANNELS ||
+        pConfig->channelsOut < MA_MIN_CHANNELS || pConfig->channelsOut > MA_MAX_CHANNELS) {
+        return MA_INVALID_ARGS;
+    }
+
     if (!ma_channel_map_valid(pConfig->channelsIn, pConfig->channelMapIn)) {
         return MA_INVALID_ARGS; /* Invalid input channel map. */
     }
@@ -38352,7 +38627,7 @@
                 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
                     for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
                         ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut];
-                        s += (pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
+                        s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
 
                         pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s);
                     }
@@ -38436,11 +38711,11 @@
 MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
 {
     ma_data_converter_config config = ma_data_converter_config_init_default();
-    config.formatIn = formatIn;
-    config.formatOut = formatOut;
-    config.channelsIn = channelsIn;
-    config.channelsOut = channelsOut;
-    config.sampleRateIn = sampleRateIn;
+    config.formatIn      = formatIn;
+    config.formatOut     = formatOut;
+    config.channelsIn    = ma_min(channelsIn,  MA_MAX_CHANNELS);
+    config.channelsOut   = ma_min(channelsOut, MA_MAX_CHANNELS);
+    config.sampleRateIn  = sampleRateIn;
     config.sampleRateOut = sampleRateOut;
     
     return config;
@@ -39340,7 +39615,7 @@
 Channel Maps
 
 **************************************************************************************************************************************************************/
-static void ma_get_standard_channel_map_microsoft(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
+static void ma_get_standard_channel_map_microsoft(ma_uint32 channels, ma_channel* pChannelMap)
 {
     /* Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */
     switch (channels)
@@ -39347,20 +39622,20 @@
     {
         case 1:
         {
-            channelMap[0] = MA_CHANNEL_MONO;
+            pChannelMap[0] = MA_CHANNEL_MONO;
         } break;
 
         case 2:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
         } break;
 
         case 3: /* Not defined, but best guess. */
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
         } break;
 
         case 4:
@@ -39367,60 +39642,60 @@
         {
 #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP
             /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[3] = MA_CHANNEL_BACK_CENTER;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[3] = MA_CHANNEL_BACK_CENTER;
 #else
             /* Quad. */
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_LEFT;
-            channelMap[3] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
 #endif
         } break;
 
         case 5: /* Not defined, but best guess. */
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[3] = MA_CHANNEL_BACK_LEFT;
-            channelMap[4] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[3] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[4] = MA_CHANNEL_BACK_RIGHT;
         } break;
 
         case 6:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[3] = MA_CHANNEL_LFE;
-            channelMap[4] = MA_CHANNEL_SIDE_LEFT;
-            channelMap[5] = MA_CHANNEL_SIDE_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[3] = MA_CHANNEL_LFE;
+            pChannelMap[4] = MA_CHANNEL_SIDE_LEFT;
+            pChannelMap[5] = MA_CHANNEL_SIDE_RIGHT;
         } break;
 
         case 7: /* Not defined, but best guess. */
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[3] = MA_CHANNEL_LFE;
-            channelMap[4] = MA_CHANNEL_BACK_CENTER;
-            channelMap[5] = MA_CHANNEL_SIDE_LEFT;
-            channelMap[6] = MA_CHANNEL_SIDE_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[3] = MA_CHANNEL_LFE;
+            pChannelMap[4] = MA_CHANNEL_BACK_CENTER;
+            pChannelMap[5] = MA_CHANNEL_SIDE_LEFT;
+            pChannelMap[6] = MA_CHANNEL_SIDE_RIGHT;
         } break;
 
         case 8:
         default:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[3] = MA_CHANNEL_LFE;
-            channelMap[4] = MA_CHANNEL_BACK_LEFT;
-            channelMap[5] = MA_CHANNEL_BACK_RIGHT;
-            channelMap[6] = MA_CHANNEL_SIDE_LEFT;
-            channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[3] = MA_CHANNEL_LFE;
+            pChannelMap[4] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[5] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
+            pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
         } break;
     }
 
@@ -39427,83 +39702,87 @@
     /* Remainder. */
     if (channels > 8) {
         ma_uint32 iChannel;
-        for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
-            channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
+        for (iChannel = 8; iChannel < channels; ++iChannel) {
+            if (iChannel < MA_MAX_CHANNELS) {
+                pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
+            } else {
+                pChannelMap[iChannel] = MA_CHANNEL_NONE;
+            }
         }
     }
 }
 
-static void ma_get_standard_channel_map_alsa(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
+static void ma_get_standard_channel_map_alsa(ma_uint32 channels, ma_channel* pChannelMap)
 {
     switch (channels)
     {
         case 1:
         {
-            channelMap[0] = MA_CHANNEL_MONO;
+            pChannelMap[0] = MA_CHANNEL_MONO;
         } break;
 
         case 2:
         {
-            channelMap[0] = MA_CHANNEL_LEFT;
-            channelMap[1] = MA_CHANNEL_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_LEFT;
+            pChannelMap[1] = MA_CHANNEL_RIGHT;
         } break;
 
         case 3:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
         } break;
 
         case 4:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_LEFT;
-            channelMap[3] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
         } break;
 
         case 5:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_LEFT;
-            channelMap[3] = MA_CHANNEL_BACK_RIGHT;
-            channelMap[4] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
         } break;
 
         case 6:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_LEFT;
-            channelMap[3] = MA_CHANNEL_BACK_RIGHT;
-            channelMap[4] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[5] = MA_CHANNEL_LFE;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[5] = MA_CHANNEL_LFE;
         } break;
 
         case 7:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_LEFT;
-            channelMap[3] = MA_CHANNEL_BACK_RIGHT;
-            channelMap[4] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[5] = MA_CHANNEL_LFE;
-            channelMap[6] = MA_CHANNEL_BACK_CENTER;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[5] = MA_CHANNEL_LFE;
+            pChannelMap[6] = MA_CHANNEL_BACK_CENTER;
         } break;
 
         case 8:
         default:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_LEFT;
-            channelMap[3] = MA_CHANNEL_BACK_RIGHT;
-            channelMap[4] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[5] = MA_CHANNEL_LFE;
-            channelMap[6] = MA_CHANNEL_SIDE_LEFT;
-            channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[5] = MA_CHANNEL_LFE;
+            pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
+            pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
         } break;
     }
 
@@ -39510,59 +39789,63 @@
     /* Remainder. */
     if (channels > 8) {
         ma_uint32 iChannel;
-        for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
-            channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
+        for (iChannel = 8; iChannel < channels; ++iChannel) {
+            if (iChannel < MA_MAX_CHANNELS) {
+                pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
+            } else {
+                pChannelMap[iChannel] = MA_CHANNEL_NONE;
+            }
         }
     }
 }
 
-static void ma_get_standard_channel_map_rfc3551(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
+static void ma_get_standard_channel_map_rfc3551(ma_uint32 channels, ma_channel* pChannelMap)
 {
     switch (channels)
     {
         case 1:
         {
-            channelMap[0] = MA_CHANNEL_MONO;
+            pChannelMap[0] = MA_CHANNEL_MONO;
         } break;
 
         case 2:
         {
-            channelMap[0] = MA_CHANNEL_LEFT;
-            channelMap[1] = MA_CHANNEL_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_LEFT;
+            pChannelMap[1] = MA_CHANNEL_RIGHT;
         } break;
 
         case 3:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
         } break;
 
         case 4:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[3] = MA_CHANNEL_BACK_CENTER;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[3] = MA_CHANNEL_BACK_CENTER;
         } break;
 
         case 5:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[3] = MA_CHANNEL_BACK_LEFT;
-            channelMap[4] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[3] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[4] = MA_CHANNEL_BACK_RIGHT;
         } break;
 
         case 6:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_SIDE_LEFT;
-            channelMap[2] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[3] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[4] = MA_CHANNEL_SIDE_RIGHT;
-            channelMap[5] = MA_CHANNEL_BACK_CENTER;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_SIDE_LEFT;
+            pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[3] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[4] = MA_CHANNEL_SIDE_RIGHT;
+            pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
         } break;
     }
 
@@ -39569,83 +39852,87 @@
     /* Remainder. */
     if (channels > 8) {
         ma_uint32 iChannel;
-        for (iChannel = 6; iChannel < MA_MAX_CHANNELS; ++iChannel) {
-            channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6));
+        for (iChannel = 6; iChannel < channels; ++iChannel) {
+            if (iChannel < MA_MAX_CHANNELS) {
+                pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6));
+            } else {
+                pChannelMap[iChannel] = MA_CHANNEL_NONE;
+            }
         }
     }
 }
 
-static void ma_get_standard_channel_map_flac(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
+static void ma_get_standard_channel_map_flac(ma_uint32 channels, ma_channel* pChannelMap)
 {
     switch (channels)
     {
         case 1:
         {
-            channelMap[0] = MA_CHANNEL_MONO;
+            pChannelMap[0] = MA_CHANNEL_MONO;
         } break;
 
         case 2:
         {
-            channelMap[0] = MA_CHANNEL_LEFT;
-            channelMap[1] = MA_CHANNEL_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_LEFT;
+            pChannelMap[1] = MA_CHANNEL_RIGHT;
         } break;
 
         case 3:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
         } break;
 
         case 4:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_LEFT;
-            channelMap[3] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
         } break;
 
         case 5:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[3] = MA_CHANNEL_BACK_LEFT;
-            channelMap[4] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[3] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[4] = MA_CHANNEL_BACK_RIGHT;
         } break;
 
         case 6:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[3] = MA_CHANNEL_LFE;
-            channelMap[4] = MA_CHANNEL_BACK_LEFT;
-            channelMap[5] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[3] = MA_CHANNEL_LFE;
+            pChannelMap[4] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[5] = MA_CHANNEL_BACK_RIGHT;
         } break;
 
         case 7:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[3] = MA_CHANNEL_LFE;
-            channelMap[4] = MA_CHANNEL_BACK_CENTER;
-            channelMap[5] = MA_CHANNEL_SIDE_LEFT;
-            channelMap[6] = MA_CHANNEL_SIDE_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[3] = MA_CHANNEL_LFE;
+            pChannelMap[4] = MA_CHANNEL_BACK_CENTER;
+            pChannelMap[5] = MA_CHANNEL_SIDE_LEFT;
+            pChannelMap[6] = MA_CHANNEL_SIDE_RIGHT;
         } break;
 
         case 8:
         default:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[3] = MA_CHANNEL_LFE;
-            channelMap[4] = MA_CHANNEL_BACK_LEFT;
-            channelMap[5] = MA_CHANNEL_BACK_RIGHT;
-            channelMap[6] = MA_CHANNEL_SIDE_LEFT;
-            channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[3] = MA_CHANNEL_LFE;
+            pChannelMap[4] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[5] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
+            pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
         } break;
     }
 
@@ -39652,13 +39939,17 @@
     /* Remainder. */
     if (channels > 8) {
         ma_uint32 iChannel;
-        for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
-            channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
+        for (iChannel = 8; iChannel < channels; ++iChannel) {
+            if (iChannel < MA_MAX_CHANNELS) {
+                pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
+            } else {
+                pChannelMap[iChannel] = MA_CHANNEL_NONE;
+            }
         }
     }
 }
 
-static void ma_get_standard_channel_map_vorbis(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
+static void ma_get_standard_channel_map_vorbis(ma_uint32 channels, ma_channel* pChannelMap)
 {
     /* In Vorbis' type 0 channel mapping, the first two channels are not always the standard left/right - it will have the center speaker where the right usually goes. Why?! */
     switch (channels)
@@ -39665,71 +39956,71 @@
     {
         case 1:
         {
-            channelMap[0] = MA_CHANNEL_MONO;
+            pChannelMap[0] = MA_CHANNEL_MONO;
         } break;
 
         case 2:
         {
-            channelMap[0] = MA_CHANNEL_LEFT;
-            channelMap[1] = MA_CHANNEL_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_LEFT;
+            pChannelMap[1] = MA_CHANNEL_RIGHT;
         } break;
 
         case 3:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
         } break;
 
         case 4:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_LEFT;
-            channelMap[3] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
         } break;
 
         case 5:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[3] = MA_CHANNEL_BACK_LEFT;
-            channelMap[4] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[3] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[4] = MA_CHANNEL_BACK_RIGHT;
         } break;
 
         case 6:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[3] = MA_CHANNEL_BACK_LEFT;
-            channelMap[4] = MA_CHANNEL_BACK_RIGHT;
-            channelMap[5] = MA_CHANNEL_LFE;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[3] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[4] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[5] = MA_CHANNEL_LFE;
         } break;
 
         case 7:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[3] = MA_CHANNEL_SIDE_LEFT;
-            channelMap[4] = MA_CHANNEL_SIDE_RIGHT;
-            channelMap[5] = MA_CHANNEL_BACK_CENTER;
-            channelMap[6] = MA_CHANNEL_LFE;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[3] = MA_CHANNEL_SIDE_LEFT;
+            pChannelMap[4] = MA_CHANNEL_SIDE_RIGHT;
+            pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
+            pChannelMap[6] = MA_CHANNEL_LFE;
         } break;
 
         case 8:
         default:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[3] = MA_CHANNEL_SIDE_LEFT;
-            channelMap[4] = MA_CHANNEL_SIDE_RIGHT;
-            channelMap[5] = MA_CHANNEL_BACK_LEFT;
-            channelMap[6] = MA_CHANNEL_BACK_RIGHT;
-            channelMap[7] = MA_CHANNEL_LFE;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[3] = MA_CHANNEL_SIDE_LEFT;
+            pChannelMap[4] = MA_CHANNEL_SIDE_RIGHT;
+            pChannelMap[5] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[6] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[7] = MA_CHANNEL_LFE;
         } break;
     }
 
@@ -39736,83 +40027,87 @@
     /* Remainder. */
     if (channels > 8) {
         ma_uint32 iChannel;
-        for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
-            channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
+        for (iChannel = 8; iChannel < channels; ++iChannel) {
+            if (iChannel < MA_MAX_CHANNELS) {
+                pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
+            } else {
+                pChannelMap[iChannel] = MA_CHANNEL_NONE;
+            }
         }
     }
 }
 
-static void ma_get_standard_channel_map_sound4(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
+static void ma_get_standard_channel_map_sound4(ma_uint32 channels, ma_channel* pChannelMap)
 {
     switch (channels)
     {
         case 1:
         {
-            channelMap[0] = MA_CHANNEL_MONO;
+            pChannelMap[0] = MA_CHANNEL_MONO;
         } break;
 
         case 2:
         {
-            channelMap[0] = MA_CHANNEL_LEFT;
-            channelMap[1] = MA_CHANNEL_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_LEFT;
+            pChannelMap[1] = MA_CHANNEL_RIGHT;
         } break;
 
         case 3:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_CENTER;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_CENTER;
         } break;
 
         case 4:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_LEFT;
-            channelMap[3] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
         } break;
 
         case 5:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_LEFT;
-            channelMap[3] = MA_CHANNEL_BACK_RIGHT;
-            channelMap[4] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
         } break;
 
         case 6:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_LEFT;
-            channelMap[3] = MA_CHANNEL_BACK_RIGHT;
-            channelMap[4] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[5] = MA_CHANNEL_LFE;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[5] = MA_CHANNEL_LFE;
         } break;
 
         case 7:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_LEFT;
-            channelMap[3] = MA_CHANNEL_BACK_RIGHT;
-            channelMap[4] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[5] = MA_CHANNEL_BACK_CENTER;
-            channelMap[6] = MA_CHANNEL_LFE;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
+            pChannelMap[6] = MA_CHANNEL_LFE;
         } break;
 
         case 8:
         default:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_LEFT;
-            channelMap[3] = MA_CHANNEL_BACK_RIGHT;
-            channelMap[4] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[5] = MA_CHANNEL_LFE;
-            channelMap[6] = MA_CHANNEL_SIDE_LEFT;
-            channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[5] = MA_CHANNEL_LFE;
+            pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
+            pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
         } break;
     }
 
@@ -39820,59 +40115,63 @@
     if (channels > 8) {
         ma_uint32 iChannel;
         for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
-            channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
+            if (iChannel < MA_MAX_CHANNELS) {
+                pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
+            } else {
+                pChannelMap[iChannel] = MA_CHANNEL_NONE;
+            }
         }
     }
 }
 
-static void ma_get_standard_channel_map_sndio(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
+static void ma_get_standard_channel_map_sndio(ma_uint32 channels, ma_channel* pChannelMap)
 {
     switch (channels)
     {
         case 1:
         {
-            channelMap[0] = MA_CHANNEL_MONO;
+            pChannelMap[0] = MA_CHANNEL_MONO;
         } break;
 
         case 2:
         {
-            channelMap[0] = MA_CHANNEL_LEFT;
-            channelMap[1] = MA_CHANNEL_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_LEFT;
+            pChannelMap[1] = MA_CHANNEL_RIGHT;
         } break;
 
         case 3:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
         } break;
 
         case 4:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_LEFT;
-            channelMap[3] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
         } break;
 
         case 5:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_LEFT;
-            channelMap[3] = MA_CHANNEL_BACK_RIGHT;
-            channelMap[4] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
         } break;
 
         case 6:
         default:
         {
-            channelMap[0] = MA_CHANNEL_FRONT_LEFT;
-            channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
-            channelMap[2] = MA_CHANNEL_BACK_LEFT;
-            channelMap[3] = MA_CHANNEL_BACK_RIGHT;
-            channelMap[4] = MA_CHANNEL_FRONT_CENTER;
-            channelMap[5] = MA_CHANNEL_LFE;
+            pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
+            pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
+            pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
+            pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
+            pChannelMap[5] = MA_CHANNEL_LFE;
         } break;
     }
 
@@ -39879,50 +40178,54 @@
     /* Remainder. */
     if (channels > 6) {
         ma_uint32 iChannel;
-        for (iChannel = 6; iChannel < MA_MAX_CHANNELS; ++iChannel) {
-            channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6));
+        for (iChannel = 6; iChannel < channels && iChannel < MA_MAX_CHANNELS; ++iChannel) {
+            if (iChannel < MA_MAX_CHANNELS) {
+                pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6));
+            } else {
+                pChannelMap[iChannel] = MA_CHANNEL_NONE;
+            }
         }
     }
 }
 
-MA_API void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
+MA_API void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel* pChannelMap)
 {
     switch (standardChannelMap)
     {
         case ma_standard_channel_map_alsa:
         {
-            ma_get_standard_channel_map_alsa(channels, channelMap);
+            ma_get_standard_channel_map_alsa(channels, pChannelMap);
         } break;
 
         case ma_standard_channel_map_rfc3551:
         {
-            ma_get_standard_channel_map_rfc3551(channels, channelMap);
+            ma_get_standard_channel_map_rfc3551(channels, pChannelMap);
         } break;
 
         case ma_standard_channel_map_flac:
         {
-            ma_get_standard_channel_map_flac(channels, channelMap);
+            ma_get_standard_channel_map_flac(channels, pChannelMap);
         } break;
 
         case ma_standard_channel_map_vorbis:
         {
-            ma_get_standard_channel_map_vorbis(channels, channelMap);
+            ma_get_standard_channel_map_vorbis(channels, pChannelMap);
         } break;
 
         case ma_standard_channel_map_sound4:
         {
-            ma_get_standard_channel_map_sound4(channels, channelMap);
+            ma_get_standard_channel_map_sound4(channels, pChannelMap);
         } break;
         
         case ma_standard_channel_map_sndio:
         {
-            ma_get_standard_channel_map_sndio(channels, channelMap);
+            ma_get_standard_channel_map_sndio(channels, pChannelMap);
         } break;
 
         case ma_standard_channel_map_microsoft:
         default:
         {
-            ma_get_standard_channel_map_microsoft(channels, channelMap);
+            ma_get_standard_channel_map_microsoft(channels, pChannelMap);
         } break;
     }
 }
@@ -39934,9 +40237,22 @@
     }
 }
 
-MA_API ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS])
+MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels)
 {
-    if (channelMap == NULL) {
+    if (pOut == NULL || channels == 0) {
+        return;
+    }
+
+    if (pIn != NULL) {
+        ma_channel_map_copy(pOut, pIn, channels);
+    } else {
+        ma_get_standard_channel_map(ma_standard_channel_map_default, channels, pOut);
+    }
+}
+
+MA_API ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel* pChannelMap)
+{
+    if (pChannelMap == NULL) {
         return MA_FALSE;
     }
 
@@ -39949,7 +40265,7 @@
     if (channels > 1) {
         ma_uint32 iChannel;
         for (iChannel = 0; iChannel < channels; ++iChannel) {
-            if (channelMap[iChannel] == MA_CHANNEL_MONO) {
+            if (pChannelMap[iChannel] == MA_CHANNEL_MONO) {
                 return MA_FALSE;
             }
         }
@@ -39958,20 +40274,16 @@
     return MA_TRUE;
 }
 
-MA_API ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel channelMapA[MA_MAX_CHANNELS], const ma_channel channelMapB[MA_MAX_CHANNELS])
+MA_API ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel* pChannelMapA, const ma_channel* pChannelMapB)
 {
     ma_uint32 iChannel;
 
-    if (channelMapA == channelMapB) {
-        return MA_FALSE;
+    if (pChannelMapA == pChannelMapB) {
+        return MA_TRUE;
     }
 
-    if (channels == 0 || channels > MA_MAX_CHANNELS) {
-        return MA_FALSE;
-    }
-
     for (iChannel = 0; iChannel < channels; ++iChannel) {
-        if (channelMapA[iChannel] != channelMapB[iChannel]) {
+        if (pChannelMapA[iChannel] != pChannelMapB[iChannel]) {
             return MA_FALSE;
         }
     }
@@ -39979,12 +40291,12 @@
     return MA_TRUE;
 }
 
-MA_API ma_bool32 ma_channel_map_blank(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS])
+MA_API ma_bool32 ma_channel_map_blank(ma_uint32 channels, const ma_channel* pChannelMap)
 {
     ma_uint32 iChannel;
 
     for (iChannel = 0; iChannel < channels; ++iChannel) {
-        if (channelMap[iChannel] != MA_CHANNEL_NONE) {
+        if (pChannelMap[iChannel] != MA_CHANNEL_NONE) {
             return MA_FALSE;
         }
     }
@@ -39992,11 +40304,12 @@
     return MA_TRUE;
 }
 
-MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS], ma_channel channelPosition)
+MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition)
 {
     ma_uint32 iChannel;
+
     for (iChannel = 0; iChannel < channels; ++iChannel) {
-        if (channelMap[iChannel] == channelPosition) {
+        if (pChannelMap[iChannel] == channelPosition) {
             return MA_TRUE;
         }
     }
@@ -40916,7 +41229,8 @@
     } else {
         ma_format format;
         ma_uint32 channels;
-        if (ma_data_source_get_data_format(pDataSource, &format, &channels) != MA_SUCCESS) {
+        ma_uint32 sampleRate;
+        if (ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate) != MA_SUCCESS) {
             return pCallbacks->onRead(pDataSource, pFramesOut, frameCount, pFramesRead); /* We don't have a way to retrieve the data format which means we don't know how to offset the output buffer. Just read as much as we can. */
         } else {
             ma_result result = MA_SUCCESS;
@@ -40935,7 +41249,7 @@
                 If we encounted an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is
                 not necessarily considered an error. 
                 */
-                if (result != MA_SUCCESS) {
+                if (result != MA_SUCCESS && result != MA_AT_END) {
                     break;
                 }
 
@@ -40943,7 +41257,7 @@
                 We can determine if we've reached the end by checking the return value of the onRead() callback. If it's less than what we requested it means
                 we've reached the end. To loop back to the start, all we need to do is seek back to the first frame.
                 */
-                if (framesProcessed < framesRemaining) {
+                if (framesProcessed < framesRemaining || result == MA_AT_END) {
                     if (ma_data_source_seek_to_pcm_frame(pDataSource, 0) != MA_SUCCESS) {
                         break;
                     }
@@ -40954,7 +41268,10 @@
                 }
             }
 
-            *pFramesRead = totalFramesProcessed;
+            if (pFramesRead != NULL) {
+                *pFramesRead = totalFramesProcessed;
+            }
+
             return result;
         }
     }
@@ -40995,11 +41312,12 @@
     return pCallbacks->onUnmap(pDataSource, frameCount);
 }
 
-MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels)
+MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
 {
     ma_result result;
     ma_format format;
     ma_uint32 channels;
+    ma_uint32 sampleRate;
     ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
 
     if (pCallbacks == NULL || pCallbacks->onGetDataFormat == NULL) {
@@ -41006,7 +41324,7 @@
         return MA_INVALID_ARGS;
     }
 
-    result = pCallbacks->onGetDataFormat(pDataSource, &format, &channels);
+    result = pCallbacks->onGetDataFormat(pDataSource, &format, &channels, &sampleRate);
     if (result != MA_SUCCESS) {
         return result;
     }
@@ -41017,12 +41335,57 @@
     if (pChannels != NULL) {
         *pChannels = channels;
     }
+    if (pSampleRate != NULL) {
+        *pSampleRate = sampleRate;
+    }
 
     return MA_SUCCESS;
 }
 
+MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
+{
+    ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
 
+    if (pCursor == NULL) {
+        return MA_INVALID_ARGS;
+    }
 
+    *pCursor = 0;
+
+    if (pCallbacks == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    if (pCallbacks->onGetCursor == NULL) {
+        return MA_NOT_IMPLEMENTED;
+    }
+
+    return pCallbacks->onGetCursor(pDataSource, pCursor);
+}
+
+MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
+{
+    ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
+
+    if (pLength == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    *pLength = 0;
+
+    if (pCallbacks == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    if (pCallbacks->onGetLength == NULL) {
+        return MA_NOT_IMPLEMENTED;
+    }
+
+    return pCallbacks->onGetLength(pDataSource, pLength);
+}
+
+
+
 MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks)
 {
     ma_audio_buffer_config config;
@@ -41068,16 +41431,35 @@
     return ma_audio_buffer_unmap((ma_audio_buffer*)pDataSource, frameCount);
 }
 
-static ma_result ma_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels)
+static ma_result ma_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
 {
     ma_audio_buffer* pAudioBuffer = (ma_audio_buffer*)pDataSource;
 
-    *pFormat   = pAudioBuffer->format;
-    *pChannels = pAudioBuffer->channels;
+    *pFormat     = pAudioBuffer->format;
+    *pChannels   = pAudioBuffer->channels;
+    *pSampleRate = 0;   /* There is no notion of a sample rate with audio buffers. */
 
     return MA_SUCCESS;
 }
 
+static ma_result ma_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
+{
+    ma_audio_buffer* pAudioBuffer = (ma_audio_buffer*)pDataSource;
+
+    *pCursor = pAudioBuffer->cursor;
+
+    return MA_SUCCESS;
+}
+
+static ma_result ma_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
+{
+    ma_audio_buffer* pAudioBuffer = (ma_audio_buffer*)pDataSource;
+
+    *pLength = pAudioBuffer->sizeInFrames;
+
+    return MA_SUCCESS;
+}
+
 static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer)
 {
     if (pAudioBuffer == NULL) {
@@ -41099,6 +41481,8 @@
     pAudioBuffer->ds.onMap           = ma_audio_buffer__data_source_on_map;
     pAudioBuffer->ds.onUnmap         = ma_audio_buffer__data_source_on_unmap;
     pAudioBuffer->ds.onGetDataFormat = ma_audio_buffer__data_source_on_get_data_format;
+    pAudioBuffer->ds.onGetCursor     = ma_audio_buffer__data_source_on_get_cursor;
+    pAudioBuffer->ds.onGetLength     = ma_audio_buffer__data_source_on_get_length;
     pAudioBuffer->format             = pConfig->format;
     pAudioBuffer->channels           = pConfig->channels;
     pAudioBuffer->cursor             = 0;
@@ -41261,7 +41645,7 @@
         MA_ASSERT(pAudioBuffer->cursor < pAudioBuffer->sizeInFrames);
     }
 
-    return frameCount;
+    return totalFramesRead;
 }
 
 MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex)
@@ -41339,8 +41723,29 @@
     return pAudioBuffer->cursor == pAudioBuffer->sizeInFrames;
 }
 
+MA_API ma_result ma_audio_buffer_get_available_frames(ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames)
+{
+    if (pAvailableFrames == NULL) {
+        return MA_INVALID_ARGS;
+    }
 
+    *pAvailableFrames = 0;
 
+    if (pAudioBuffer == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    if (pAudioBuffer->sizeInFrames <= pAudioBuffer->cursor) {
+        *pAvailableFrames = 0;
+    } else {
+        *pAvailableFrames = pAudioBuffer->sizeInFrames - pAudioBuffer->cursor;
+    }
+
+    return MA_SUCCESS;
+}
+
+
+
 /**************************************************************************************************************************************************************
 
 VFS
@@ -41719,9 +42124,6 @@
     return result;
 }
 
-#if !defined(WINVER) || WINVER <= 0x0502
-WINBASEAPI BOOL WINAPI SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, LARGE_INTEGER* pNewFilePointer, DWORD dwMoveMethod);
-#endif
 
 static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
 {
@@ -41741,7 +42143,16 @@
         dwMoveMethod = FILE_BEGIN;
     }
 
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+    /* No SetFilePointerEx() so restrict to 31 bits. */
+    if (origin > 0x7FFFFFFF) {
+        return MA_OUT_OF_RANGE;
+    }
+
+    result = SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod);
+#else
     result = SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod);
+#endif
     if (result == 0) {
         return ma_result_from_GetLastError(GetLastError());
     }
@@ -41754,12 +42165,20 @@
     LARGE_INTEGER liZero;
     LARGE_INTEGER liTell;
     BOOL result;
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+    LONG tell;
+#endif
 
     (void)pVFS;
 
     liZero.QuadPart = 0;
 
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+    result = SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT);
+    liTell.QuadPart = tell;
+#else
     result = SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT);
+#endif
     if (result == 0) {
         return ma_result_from_GetLastError(GetLastError());
     }
@@ -42146,12 +42565,10 @@
 
 /**************************************************************************************************************************************************************
 
-Decoding
+Decoding and Encoding Headers. These are auto-generated from a tool.
 
 **************************************************************************************************************************************************************/
-#ifndef MA_NO_DECODING
-
-#ifndef MA_NO_WAV
+#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
 /* dr_wav_h begin */
 #ifndef dr_wav_h
 #define dr_wav_h
@@ -42162,42 +42579,41 @@
 #define DRWAV_XSTRINGIFY(x)     DRWAV_STRINGIFY(x)
 #define DRWAV_VERSION_MAJOR     0
 #define DRWAV_VERSION_MINOR     12
-#define DRWAV_VERSION_REVISION  6
+#define DRWAV_VERSION_REVISION  10
 #define DRWAV_VERSION_STRING    DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)
 #include <stddef.h>
-#ifdef _MSC_VER
-    #if defined(__clang__)
+typedef   signed char           drwav_int8;
+typedef unsigned char           drwav_uint8;
+typedef   signed short          drwav_int16;
+typedef unsigned short          drwav_uint16;
+typedef   signed int            drwav_int32;
+typedef unsigned int            drwav_uint32;
+#if defined(_MSC_VER)
+    typedef   signed __int64    drwav_int64;
+    typedef unsigned __int64    drwav_uint64;
+#else
+    #if defined(__GNUC__)
         #pragma GCC diagnostic push
-        #pragma GCC diagnostic ignored "-Wlanguage-extension-token"
         #pragma GCC diagnostic ignored "-Wlong-long"
-        #pragma GCC diagnostic ignored "-Wc++11-long-long"
+        #if defined(__clang__)
+            #pragma GCC diagnostic ignored "-Wc++11-long-long"
+        #endif
     #endif
-    typedef   signed __int8  drwav_int8;
-    typedef unsigned __int8  drwav_uint8;
-    typedef   signed __int16 drwav_int16;
-    typedef unsigned __int16 drwav_uint16;
-    typedef   signed __int32 drwav_int32;
-    typedef unsigned __int32 drwav_uint32;
-    typedef   signed __int64 drwav_int64;
-    typedef unsigned __int64 drwav_uint64;
-    #if defined(__clang__)
+    typedef   signed long long  drwav_int64;
+    typedef unsigned long long  drwav_uint64;
+    #if defined(__GNUC__)
         #pragma GCC diagnostic pop
     #endif
+#endif
+#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
+    typedef drwav_uint64        drwav_uintptr;
 #else
-    #include <stdint.h>
-    typedef int8_t           drwav_int8;
-    typedef uint8_t          drwav_uint8;
-    typedef int16_t          drwav_int16;
-    typedef uint16_t         drwav_uint16;
-    typedef int32_t          drwav_int32;
-    typedef uint32_t         drwav_uint32;
-    typedef int64_t          drwav_int64;
-    typedef uint64_t         drwav_uint64;
+    typedef drwav_uint32        drwav_uintptr;
 #endif
-typedef drwav_uint8          drwav_bool8;
-typedef drwav_uint32         drwav_bool32;
-#define DRWAV_TRUE           1
-#define DRWAV_FALSE          0
+typedef drwav_uint8             drwav_bool8;
+typedef drwav_uint32            drwav_bool32;
+#define DRWAV_TRUE              1
+#define DRWAV_FALSE             0
 #if !defined(DRWAV_API)
     #if defined(DRWAV_DLL)
         #if defined(_WIN32)
@@ -42293,7 +42709,7 @@
 #endif
 #define DRWAV_SEQUENTIAL            0x00000001
 DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision);
-DRWAV_API const char* drwav_version_string();
+DRWAV_API const char* drwav_version_string(void);
 typedef enum
 {
     drwav_seek_origin_start,
@@ -42524,7 +42940,7 @@
 /* dr_wav_h end */
 #endif  /* MA_NO_WAV */
 
-#ifndef MA_NO_FLAC
+#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
 /* dr_flac_h begin */
 #ifndef dr_flac_h
 #define dr_flac_h
@@ -42535,42 +42951,41 @@
 #define DRFLAC_XSTRINGIFY(x)     DRFLAC_STRINGIFY(x)
 #define DRFLAC_VERSION_MAJOR     0
 #define DRFLAC_VERSION_MINOR     12
-#define DRFLAC_VERSION_REVISION  14
+#define DRFLAC_VERSION_REVISION  19
 #define DRFLAC_VERSION_STRING    DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION)
 #include <stddef.h>
-#ifdef _MSC_VER
-    #if defined(__clang__)
+typedef   signed char           drflac_int8;
+typedef unsigned char           drflac_uint8;
+typedef   signed short          drflac_int16;
+typedef unsigned short          drflac_uint16;
+typedef   signed int            drflac_int32;
+typedef unsigned int            drflac_uint32;
+#if defined(_MSC_VER)
+    typedef   signed __int64    drflac_int64;
+    typedef unsigned __int64    drflac_uint64;
+#else
+    #if defined(__GNUC__)
         #pragma GCC diagnostic push
-        #pragma GCC diagnostic ignored "-Wlanguage-extension-token"
         #pragma GCC diagnostic ignored "-Wlong-long"
-        #pragma GCC diagnostic ignored "-Wc++11-long-long"
+        #if defined(__clang__)
+            #pragma GCC diagnostic ignored "-Wc++11-long-long"
+        #endif
     #endif
-    typedef   signed __int8  drflac_int8;
-    typedef unsigned __int8  drflac_uint8;
-    typedef   signed __int16 drflac_int16;
-    typedef unsigned __int16 drflac_uint16;
-    typedef   signed __int32 drflac_int32;
-    typedef unsigned __int32 drflac_uint32;
-    typedef   signed __int64 drflac_int64;
-    typedef unsigned __int64 drflac_uint64;
-    #if defined(__clang__)
+    typedef   signed long long  drflac_int64;
+    typedef unsigned long long  drflac_uint64;
+    #if defined(__GNUC__)
         #pragma GCC diagnostic pop
     #endif
+#endif
+#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
+    typedef drflac_uint64       drflac_uintptr;
 #else
-    #include <stdint.h>
-    typedef int8_t           drflac_int8;
-    typedef uint8_t          drflac_uint8;
-    typedef int16_t          drflac_int16;
-    typedef uint16_t         drflac_uint16;
-    typedef int32_t          drflac_int32;
-    typedef uint32_t         drflac_uint32;
-    typedef int64_t          drflac_int64;
-    typedef uint64_t         drflac_uint64;
+    typedef drflac_uint32       drflac_uintptr;
 #endif
-typedef drflac_uint8         drflac_bool8;
-typedef drflac_uint32        drflac_bool32;
-#define DRFLAC_TRUE          1
-#define DRFLAC_FALSE         0
+typedef drflac_uint8            drflac_bool8;
+typedef drflac_uint32           drflac_bool32;
+#define DRFLAC_TRUE             1
+#define DRFLAC_FALSE            0
 #if !defined(DRFLAC_API)
     #if defined(DRFLAC_DLL)
         #if defined(_WIN32)
@@ -42613,7 +43028,7 @@
     #define DRFLAC_DEPRECATED
 #endif
 DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision);
-DRFLAC_API const char* drflac_version_string();
+DRFLAC_API const char* drflac_version_string(void);
 #ifndef DR_FLAC_BUFFER_SIZE
 #define DR_FLAC_BUFFER_SIZE   4096
 #endif
@@ -42886,7 +43301,7 @@
 /* dr_flac_h end */
 #endif  /* MA_NO_FLAC */
 
-#ifndef MA_NO_MP3
+#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
 /* dr_mp3_h begin */
 #ifndef dr_mp3_h
 #define dr_mp3_h
@@ -42897,42 +43312,41 @@
 #define DRMP3_XSTRINGIFY(x)     DRMP3_STRINGIFY(x)
 #define DRMP3_VERSION_MAJOR     0
 #define DRMP3_VERSION_MINOR     6
-#define DRMP3_VERSION_REVISION  12
+#define DRMP3_VERSION_REVISION  16
 #define DRMP3_VERSION_STRING    DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION)
 #include <stddef.h>
-#ifdef _MSC_VER
-    #if defined(__clang__)
+typedef   signed char           drmp3_int8;
+typedef unsigned char           drmp3_uint8;
+typedef   signed short          drmp3_int16;
+typedef unsigned short          drmp3_uint16;
+typedef   signed int            drmp3_int32;
+typedef unsigned int            drmp3_uint32;
+#if defined(_MSC_VER)
+    typedef   signed __int64    drmp3_int64;
+    typedef unsigned __int64    drmp3_uint64;
+#else
+    #if defined(__GNUC__)
         #pragma GCC diagnostic push
-        #pragma GCC diagnostic ignored "-Wlanguage-extension-token"
         #pragma GCC diagnostic ignored "-Wlong-long"
-        #pragma GCC diagnostic ignored "-Wc++11-long-long"
+        #if defined(__clang__)
+            #pragma GCC diagnostic ignored "-Wc++11-long-long"
+        #endif
     #endif
-    typedef   signed __int8  drmp3_int8;
-    typedef unsigned __int8  drmp3_uint8;
-    typedef   signed __int16 drmp3_int16;
-    typedef unsigned __int16 drmp3_uint16;
-    typedef   signed __int32 drmp3_int32;
-    typedef unsigned __int32 drmp3_uint32;
-    typedef   signed __int64 drmp3_int64;
-    typedef unsigned __int64 drmp3_uint64;
-    #if defined(__clang__)
+    typedef   signed long long  drmp3_int64;
+    typedef unsigned long long  drmp3_uint64;
+    #if defined(__GNUC__)
         #pragma GCC diagnostic pop
     #endif
+#endif
+#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
+    typedef drmp3_uint64        drmp3_uintptr;
 #else
-    #include <stdint.h>
-    typedef int8_t           drmp3_int8;
-    typedef uint8_t          drmp3_uint8;
-    typedef int16_t          drmp3_int16;
-    typedef uint16_t         drmp3_uint16;
-    typedef int32_t          drmp3_int32;
-    typedef uint32_t         drmp3_uint32;
-    typedef int64_t          drmp3_int64;
-    typedef uint64_t         drmp3_uint64;
+    typedef drmp3_uint32        drmp3_uintptr;
 #endif
-typedef drmp3_uint8          drmp3_bool8;
-typedef drmp3_uint32         drmp3_bool32;
-#define DRMP3_TRUE           1
-#define DRMP3_FALSE          0
+typedef drmp3_uint8             drmp3_bool8;
+typedef drmp3_uint32            drmp3_bool32;
+#define DRMP3_TRUE              1
+#define DRMP3_FALSE             0
 #if !defined(DRMP3_API)
     #if defined(DRMP3_DLL)
         #if defined(_WIN32)
@@ -43030,7 +43444,7 @@
     #define DRMP3_INLINE
 #endif
 DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision);
-DRMP3_API const char* drmp3_version_string();
+DRMP3_API const char* drmp3_version_string(void);
 typedef struct
 {
     int frame_bytes, channels, hz, layer, bitrate_kbps;
@@ -43140,6 +43554,13 @@
 #endif  /* MA_NO_MP3 */
 
 
+/**************************************************************************************************************************************************************
+
+Decoding
+
+**************************************************************************************************************************************************************/
+#ifndef MA_NO_DECODING
+
 static size_t ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
 {
     size_t bytesRead;
@@ -43148,7 +43569,7 @@
     MA_ASSERT(pBufferOut != NULL);
 
     bytesRead = pDecoder->onRead(pDecoder, pBufferOut, bytesToRead);
-    pDecoder->readPointer += bytesRead;
+    pDecoder->readPointerInBytes += bytesRead;
 
     return bytesRead;
 }
@@ -43162,9 +43583,9 @@
     wasSuccessful = pDecoder->onSeek(pDecoder, byteOffset, origin);
     if (wasSuccessful) {
         if (origin == ma_seek_origin_start) {
-            pDecoder->readPointer = (ma_uint64)byteOffset;
+            pDecoder->readPointerInBytes = (ma_uint64)byteOffset;
         } else {
-            pDecoder->readPointer += byteOffset;
+            pDecoder->readPointerInBytes += byteOffset;
         }
     }
 
@@ -43176,8 +43597,8 @@
 {
     ma_decoder_config config;
     MA_ZERO_OBJECT(&config);
-    config.format = outputFormat;
-    config.channels = outputChannels;
+    config.format     = outputFormat;
+    config.channels   = ma_min(outputChannels, ma_countof(config.channelMap));
     config.sampleRate = outputSampleRate;
     config.resampling.algorithm = ma_resample_algorithm_linear;
     config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
@@ -43205,7 +43626,19 @@
     ma_data_converter_config converterConfig;
 
     MA_ASSERT(pDecoder != NULL);
+    MA_ASSERT(pConfig  != NULL);
 
+    /* Make sure we're not asking for too many channels. */
+    if (pConfig->channels > MA_MAX_CHANNELS) {
+        return MA_INVALID_ARGS;
+    }
+
+    /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */
+    if (pDecoder->internalChannels > MA_MAX_CHANNELS) {
+        return MA_INVALID_ARGS;
+    }
+
+
     /* Output format. */
     if (pConfig->format == ma_format_unknown) {
         pDecoder->outputFormat = pDecoder->internalFormat;
@@ -43489,13 +43922,20 @@
 
     /*
     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.
+    since it's the only one that's truly lossless. If the internal bits per sample is <= 16 we will decode to ma_format_s16 to keep it more efficient.
     */
-    pDecoder->internalFormat = ma_format_s32;
-    if (pConfig->format == ma_format_s16) {
-        pDecoder->internalFormat = ma_format_s16;
-    } else if (pConfig->format == ma_format_f32) {
-        pDecoder->internalFormat = ma_format_f32;
+    if (pConfig->format == ma_format_unknown) {
+        if (pFlac->bitsPerSample <= 16) {
+            pDecoder->internalFormat = ma_format_s16;
+        } else {
+            pDecoder->internalFormat = ma_format_s32;
+        }
+    } else {
+        if (pConfig->format == ma_format_s16 || pConfig->format == ma_format_f32) {
+            pDecoder->internalFormat = pConfig->format;
+        } else {
+            pDecoder->internalFormat = ma_format_s32;   /* s32 as the baseline to ensure no loss of precision for 24-bit encoded files. */
+        }
     }
 
     pDecoder->internalChannels = pFlac->channels;
@@ -44092,16 +44532,36 @@
     return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex);
 }
 
-static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels)
+static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
 {
     ma_decoder* pDecoder = (ma_decoder*)pDataSource;
 
-    *pFormat   = pDecoder->outputFormat;
-    *pChannels = pDecoder->outputChannels;
+    *pFormat     = pDecoder->outputFormat;
+    *pChannels   = pDecoder->outputChannels;
+    *pSampleRate = pDecoder->outputSampleRate;
 
     return MA_SUCCESS;
 }
 
+static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pLength)
+{
+    ma_decoder* pDecoder = (ma_decoder*)pDataSource;
+
+    return ma_decoder_get_cursor_in_pcm_frames(pDecoder, pLength);
+}
+
+static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
+{
+    ma_decoder* pDecoder = (ma_decoder*)pDataSource;
+
+    *pLength = ma_decoder_get_length_in_pcm_frames(pDecoder);
+    if (*pLength == 0) {
+        return MA_NOT_IMPLEMENTED;
+    }
+
+    return MA_SUCCESS;
+}
+
 static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
 {
     ma_result result;
@@ -44121,6 +44581,8 @@
     pDecoder->ds.onRead          = ma_decoder__data_source_on_read;
     pDecoder->ds.onSeek          = ma_decoder__data_source_on_seek;
     pDecoder->ds.onGetDataFormat = ma_decoder__data_source_on_get_data_format;
+    pDecoder->ds.onGetCursor     = ma_decoder__data_source_on_get_cursor;
+    pDecoder->ds.onGetLength     = ma_decoder__data_source_on_get_length;
 
     pDecoder->onRead    = onRead;
     pDecoder->onSeek    = onSeek;
@@ -44575,6 +45037,16 @@
     return ma_decoder__postinit(&config, pDecoder);
 }
 
+
+#if defined(MA_HAS_WAV)    || \
+    defined(MA_HAS_MP3)    || \
+    defined(MA_HAS_FLAC)   || \
+    defined(MA_HAS_VORBIS) || \
+    defined(MA_HAS_OPUS)
+#define MA_HAS_PATH_API
+#endif
+
+#if defined(MA_HAS_PATH_API)
 static const char* ma_path_file_name(const char* path)
 {
     const char* fileName;
@@ -44741,6 +45213,7 @@
     }
 #endif
 }
+#endif  /* MA_HAS_PATH_API */
 
 
 
@@ -45272,6 +45745,23 @@
     return MA_SUCCESS;
 }
 
+MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor)
+{
+    if (pCursor == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    *pCursor = 0;
+
+    if (pDecoder == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    *pCursor = pDecoder->readPointerInPCMFrames;
+
+    return MA_SUCCESS;
+}
+
 MA_API ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder)
 {
     if (pDecoder == NULL) {
@@ -45307,68 +45797,70 @@
 
     /* Fast path. */
     if (pDecoder->converter.isPassthrough) {
-        return pDecoder->onReadPCMFrames(pDecoder, pFramesOut, frameCount);
-    }
-
-    /*
-    Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we
-    need to run through each sample because we need to ensure it's internal cache is updated.
-    */
-    if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) {
-        return pDecoder->onReadPCMFrames(pDecoder, NULL, frameCount);   /* All decoder backends must support passing in NULL for the output buffer. */
-    }
-
-    /* Slow path. Need to run everything through the data converter. */
-    totalFramesReadOut = 0;
-    totalFramesReadIn  = 0;
-    pRunningFramesOut  = pFramesOut;
+        totalFramesReadOut = pDecoder->onReadPCMFrames(pDecoder, pFramesOut, frameCount);
+    } else {
+        /*
+        Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we
+        need to run through each sample because we need to ensure it's internal cache is updated.
+        */
+        if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) {
+            totalFramesReadOut = pDecoder->onReadPCMFrames(pDecoder, NULL, frameCount);   /* All decoder backends must support passing in NULL for the output buffer. */
+        } else {
+            /* Slow path. Need to run everything through the data converter. */
+            totalFramesReadOut = 0;
+            totalFramesReadIn  = 0;
+            pRunningFramesOut  = pFramesOut;
     
-    while (totalFramesReadOut < frameCount) {
-        ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];  /* In internal format. */
-        ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
-        ma_uint64 framesToReadThisIterationIn;
-        ma_uint64 framesReadThisIterationIn;
-        ma_uint64 framesToReadThisIterationOut;
-        ma_uint64 framesReadThisIterationOut;
-        ma_uint64 requiredInputFrameCount;
+            while (totalFramesReadOut < frameCount) {
+                ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];  /* In internal format. */
+                ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
+                ma_uint64 framesToReadThisIterationIn;
+                ma_uint64 framesReadThisIterationIn;
+                ma_uint64 framesToReadThisIterationOut;
+                ma_uint64 framesReadThisIterationOut;
+                ma_uint64 requiredInputFrameCount;
 
-        framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
-        framesToReadThisIterationIn = framesToReadThisIterationOut;
-        if (framesToReadThisIterationIn > intermediaryBufferCap) {
-            framesToReadThisIterationIn = intermediaryBufferCap;
-        }
+                framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
+                framesToReadThisIterationIn = framesToReadThisIterationOut;
+                if (framesToReadThisIterationIn > intermediaryBufferCap) {
+                    framesToReadThisIterationIn = intermediaryBufferCap;
+                }
 
-        requiredInputFrameCount = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut);
-        if (framesToReadThisIterationIn > requiredInputFrameCount) {
-            framesToReadThisIterationIn = requiredInputFrameCount;
-        }
+                requiredInputFrameCount = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut);
+                if (framesToReadThisIterationIn > requiredInputFrameCount) {
+                    framesToReadThisIterationIn = requiredInputFrameCount;
+                }
 
-        if (requiredInputFrameCount > 0) {
-            framesReadThisIterationIn = pDecoder->onReadPCMFrames(pDecoder, pIntermediaryBuffer, framesToReadThisIterationIn);
-            totalFramesReadIn += framesReadThisIterationIn;
-        }
+                if (requiredInputFrameCount > 0) {
+                    framesReadThisIterationIn = pDecoder->onReadPCMFrames(pDecoder, pIntermediaryBuffer, framesToReadThisIterationIn);
+                    totalFramesReadIn += framesReadThisIterationIn;
+                }
 
-        /*
-        At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any
-        input frames, we still want to try processing frames because there may some output frames generated from cached input data.
-        */
-        framesReadThisIterationOut = framesToReadThisIterationOut;
-        result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
-        if (result != MA_SUCCESS) {
-            break;
-        }
+                /*
+                At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any
+                input frames, we still want to try processing frames because there may some output frames generated from cached input data.
+                */
+                framesReadThisIterationOut = framesToReadThisIterationOut;
+                result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
+                if (result != MA_SUCCESS) {
+                    break;
+                }
 
-        totalFramesReadOut += framesReadThisIterationOut;
+                totalFramesReadOut += framesReadThisIterationOut;
 
-        if (pRunningFramesOut != NULL) {
-            pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
-        }
+                if (pRunningFramesOut != NULL) {
+                    pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
+                }
 
-        if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
-            break;  /* We're done. */
+                if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
+                    break;  /* We're done. */
+                }
+            }
         }
     }
 
+    pDecoder->readPointerInPCMFrames += totalFramesReadOut;
+
     return totalFramesReadOut;
 }
 
@@ -45379,6 +45871,7 @@
     }
 
     if (pDecoder->onSeekToPCMFrame) {
+        ma_result result;
         ma_uint64 internalFrameIndex;
         if (pDecoder->internalSampleRate == pDecoder->outputSampleRate) {
             internalFrameIndex = frameIndex;
@@ -45386,7 +45879,10 @@
             internalFrameIndex = ma_calculate_frame_count_after_resampling(pDecoder->internalSampleRate, pDecoder->outputSampleRate, frameIndex);
         }
 
-        return pDecoder->onSeekToPCMFrame(pDecoder, internalFrameIndex);
+        result = pDecoder->onSeekToPCMFrame(pDecoder, internalFrameIndex);
+        if (result == MA_SUCCESS) {
+            pDecoder->readPointerInPCMFrames = frameIndex;
+        }
     }
 
     /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */
@@ -45393,7 +45889,35 @@
     return MA_INVALID_ARGS;
 }
 
+MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames)
+{
+    ma_uint64 totalFrameCount;
 
+    if (pAvailableFrames == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    *pAvailableFrames = 0;
+
+    if (pDecoder == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    totalFrameCount = ma_decoder_get_length_in_pcm_frames(pDecoder);
+    if (totalFrameCount == 0) {
+        return MA_NOT_IMPLEMENTED;
+    }
+
+    if (totalFrameCount <= pDecoder->readPointerInPCMFrames) {
+        *pAvailableFrames = 0;
+    } else {
+        *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames;
+    }
+
+    return MA_SUCCESS;   /* No frames available. */
+}
+
+
 static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
 {
     ma_uint64 totalFrameCount;
@@ -45833,16 +46357,26 @@
     return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex);
 }
 
-static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels)
+static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
 {
     ma_waveform* pWaveform = (ma_waveform*)pDataSource;
 
-    *pFormat   = pWaveform->config.format;
-    *pChannels = pWaveform->config.channels;
+    *pFormat     = pWaveform->config.format;
+    *pChannels   = pWaveform->config.channels;
+    *pSampleRate = pWaveform->config.sampleRate;
 
     return MA_SUCCESS;
 }
 
+static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
+{
+    ma_waveform* pWaveform = (ma_waveform*)pDataSource;
+
+    *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance);
+
+    return MA_SUCCESS;
+}
+
 MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform)
 {
     if (pWaveform == NULL) {
@@ -45853,9 +46387,11 @@
     pWaveform->ds.onRead          = ma_waveform__data_source_on_read;
     pWaveform->ds.onSeek          = ma_waveform__data_source_on_seek;
     pWaveform->ds.onGetDataFormat = ma_waveform__data_source_on_get_data_format;
-    pWaveform->config  = *pConfig;
-    pWaveform->advance = 1.0 / pWaveform->config.sampleRate;
-    pWaveform->time    = 0;
+    pWaveform->ds.onGetCursor     = ma_waveform__data_source_on_get_cursor;
+    pWaveform->ds.onGetLength     = NULL;   /* Intentionally set to NULL since there's no notion of a length in waveforms. */
+    pWaveform->config             = *pConfig;
+    pWaveform->advance            = 1.0 / pWaveform->config.sampleRate;
+    pWaveform->time               = 0;
 
     return MA_SUCCESS;
 }
@@ -46212,12 +46748,13 @@
     return MA_SUCCESS;
 }
 
-static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels)
+static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
 {
     ma_noise* pNoise = (ma_noise*)pDataSource;
 
-    *pFormat   = pNoise->config.format;
-    *pChannels = pNoise->config.channels;
+    *pFormat     = pNoise->config.format;
+    *pChannels   = pNoise->config.channels;
+    *pSampleRate = 0;   /* There is no notion of sample rate with noise generation. */
 
     return MA_SUCCESS;
 }
@@ -46234,10 +46771,16 @@
         return MA_INVALID_ARGS;
     }
 
+    if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) {
+        return MA_INVALID_ARGS;
+    }
+
     pNoise->ds.onRead          = ma_noise__data_source_on_read;
     pNoise->ds.onSeek          = ma_noise__data_source_on_seek;  /* <-- No-op for noise. */
     pNoise->ds.onGetDataFormat = ma_noise__data_source_on_get_data_format;
-    pNoise->config = *pConfig;
+    pNoise->ds.onGetCursor     = NULL;  /* No notion of a cursor for noise. */
+    pNoise->ds.onGetLength     = NULL;  /* No notion of a length for noise. */
+    pNoise->config             = *pConfig;
     ma_lcg_seed(&pNoise->lcg, pConfig->seed);
 
     if (pNoise->config.type == ma_noise_type_pink) {
@@ -46567,7 +47110,7 @@
 
 ***************************************************************************************************************************************************************
 **************************************************************************************************************************************************************/
-#ifndef MA_NO_WAV
+#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
 #if !defined(DR_WAV_IMPLEMENTATION) && !defined(DRWAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
 /* dr_wav_c begin */
 #ifndef dr_wav_c
@@ -46671,7 +47214,7 @@
         *pRevision = DRWAV_VERSION_REVISION;
     }
 }
-DRWAV_API const char* drwav_version_string()
+DRWAV_API const char* drwav_version_string(void)
 {
     return DRWAV_VERSION_STRING;
 }
@@ -47542,6 +48085,39 @@
 {
     return 24 + dataChunkSize;
 }
+static size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize)
+{
+    DRWAV_ASSERT(pWav          != NULL);
+    DRWAV_ASSERT(pWav->onWrite != NULL);
+    return pWav->onWrite(pWav->pUserData, pData, dataSize);
+}
+static size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value)
+{
+    DRWAV_ASSERT(pWav          != NULL);
+    DRWAV_ASSERT(pWav->onWrite != NULL);
+    if (!drwav__is_little_endian()) {
+        value = drwav__bswap16(value);
+    }
+    return drwav__write(pWav, &value, 2);
+}
+static size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value)
+{
+    DRWAV_ASSERT(pWav          != NULL);
+    DRWAV_ASSERT(pWav->onWrite != NULL);
+    if (!drwav__is_little_endian()) {
+        value = drwav__bswap32(value);
+    }
+    return drwav__write(pWav, &value, 4);
+}
+static size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value)
+{
+    DRWAV_ASSERT(pWav          != NULL);
+    DRWAV_ASSERT(pWav->onWrite != NULL);
+    if (!drwav__is_little_endian()) {
+        value = drwav__bswap64(value);
+    }
+    return drwav__write(pWav, &value, 8);
+}
 static drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
 {
     if (pWav == NULL || onWrite == NULL) {
@@ -47590,39 +48166,39 @@
     pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize;
     if (pFormat->container == drwav_container_riff) {
         drwav_uint32 chunkSizeRIFF = 36 + (drwav_uint32)initialDataChunkSize;
-        runningPos += pWav->onWrite(pWav->pUserData, "RIFF", 4);
-        runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeRIFF, 4);
-        runningPos += pWav->onWrite(pWav->pUserData, "WAVE", 4);
+        runningPos += drwav__write(pWav, "RIFF", 4);
+        runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF);
+        runningPos += drwav__write(pWav, "WAVE", 4);
     } else {
         drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize;
-        runningPos += pWav->onWrite(pWav->pUserData, drwavGUID_W64_RIFF, 16);
-        runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeRIFF, 8);
-        runningPos += pWav->onWrite(pWav->pUserData, drwavGUID_W64_WAVE, 16);
+        runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16);
+        runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF);
+        runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16);
     }
     if (pFormat->container == drwav_container_riff) {
         chunkSizeFMT = 16;
-        runningPos += pWav->onWrite(pWav->pUserData, "fmt ", 4);
-        runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeFMT, 4);
+        runningPos += drwav__write(pWav, "fmt ", 4);
+        runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT);
     } else {
         chunkSizeFMT = 40;
-        runningPos += pWav->onWrite(pWav->pUserData, drwavGUID_W64_FMT, 16);
-        runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeFMT, 8);
+        runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16);
+        runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT);
     }
-    runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.formatTag,      2);
-    runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.channels,       2);
-    runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.sampleRate,     4);
-    runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.avgBytesPerSec, 4);
-    runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.blockAlign,     2);
-    runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.bitsPerSample,  2);
+    runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag);
+    runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels);
+    runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate);
+    runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec);
+    runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign);
+    runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample);
     pWav->dataChunkDataPos = runningPos;
     if (pFormat->container == drwav_container_riff) {
         drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize;
-        runningPos += pWav->onWrite(pWav->pUserData, "data", 4);
-        runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeDATA, 4);
+        runningPos += drwav__write(pWav, "data", 4);
+        runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA);
     } else {
         drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize;
-        runningPos += pWav->onWrite(pWav->pUserData, drwavGUID_W64_DATA, 16);
-        runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeDATA, 8);
+        runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16);
+        runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA);
     }
     if (pFormat->container == drwav_container_riff) {
         if (runningPos != 20 + chunkSizeFMT + 8) {
@@ -48450,26 +49026,26 @@
         }
         if (paddingSize > 0) {
             drwav_uint64 paddingData = 0;
-            pWav->onWrite(pWav->pUserData, &paddingData, paddingSize);
+            drwav__write(pWav, &paddingData, paddingSize);
         }
         if (pWav->onSeek && !pWav->isSequentialWrite) {
             if (pWav->container == drwav_container_riff) {
                 if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) {
                     drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize);
-                    pWav->onWrite(pWav->pUserData, &riffChunkSize, 4);
+                    drwav__write_u32ne_to_le(pWav, riffChunkSize);
                 }
                 if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 4, drwav_seek_origin_start)) {
                     drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize);
-                    pWav->onWrite(pWav->pUserData, &dataChunkSize, 4);
+                    drwav__write_u32ne_to_le(pWav, dataChunkSize);
                 }
             } else {
                 if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) {
                     drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize);
-                    pWav->onWrite(pWav->pUserData, &riffChunkSize, 8);
+                    drwav__write_u64ne_to_le(pWav, riffChunkSize);
                 }
                 if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 16, drwav_seek_origin_start)) {
                     drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize);
-                    pWav->onWrite(pWav->pUserData, &dataChunkSize, 8);
+                    drwav__write_u64ne_to_le(pWav, dataChunkSize);
                 }
             }
         }
@@ -48570,6 +49146,13 @@
     }
     if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
         pWav->compressed.iCurrentPCMFrame = 0;
+        if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+            DRWAV_ZERO_OBJECT(&pWav->msadpcm);
+        } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+            DRWAV_ZERO_OBJECT(&pWav->ima);
+        } else {
+            DRWAV_ASSERT(DRWAV_FALSE);
+        }
     }
     pWav->bytesRemaining = pWav->dataChunkDataSize;
     return DRWAV_TRUE;
@@ -49550,7 +50133,11 @@
         return;
     }
     for (i = 0; i < sampleCount; ++i) {
-        double x = (double)(((drwav_int32)(((drwav_uint32)(pIn[i*3+0]) << 8) | ((drwav_uint32)(pIn[i*3+1]) << 16) | ((drwav_uint32)(pIn[i*3+2])) << 24)) >> 8);
+        double x;
+        drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) <<  8);
+        drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16);
+        drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24);
+        x = (double)((drwav_int32)(a | b | c) >> 8);
         *pOut++ = (float)(x * 0.00000011920928955078125);
     }
 }
@@ -50240,7 +50827,7 @@
 #endif  /* DRWAV_IMPLEMENTATION */
 #endif  /* MA_NO_WAV */
 
-#ifndef MA_NO_FLAC
+#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
 #if !defined(DR_FLAC_IMPLEMENTATION) && !defined(DRFLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
 /* dr_flac_c begin */
 #ifndef dr_flac_c
@@ -50408,7 +50995,7 @@
     return DRFLAC_FALSE;
 #endif
 }
-#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64))
+#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) && !defined(__clang__)
     #define DRFLAC_HAS_LZCNT_INTRINSIC
 #elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
     #define DRFLAC_HAS_LZCNT_INTRINSIC
@@ -50419,7 +51006,7 @@
         #endif
     #endif
 #endif
-#if defined(_MSC_VER) && _MSC_VER >= 1400
+#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__)
     #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
     #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
     #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
@@ -50547,7 +51134,7 @@
         *pRevision = DRFLAC_VERSION_REVISION;
     }
 }
-DRFLAC_API const char* drflac_version_string()
+DRFLAC_API const char* drflac_version_string(void)
 {
     return DRFLAC_VERSION_STRING;
 }
@@ -50620,7 +51207,7 @@
 static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n)
 {
 #ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC
-    #if defined(_MSC_VER)
+    #if defined(_MSC_VER) && !defined(__clang__)
         return _byteswap_ushort(n);
     #elif defined(__GNUC__) || defined(__clang__)
         return __builtin_bswap16(n);
@@ -50635,7 +51222,7 @@
 static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n)
 {
 #ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC
-    #if defined(_MSC_VER)
+    #if defined(_MSC_VER) && !defined(__clang__)
         return _byteswap_ulong(n);
     #elif defined(__GNUC__) || defined(__clang__)
         #if defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRFLAC_64BIT)
@@ -50664,7 +51251,7 @@
 static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n)
 {
 #ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC
-    #if defined(_MSC_VER)
+    #if defined(_MSC_VER) && !defined(__clang__)
         return _byteswap_uint64(n);
     #elif defined(__GNUC__) || defined(__clang__)
         return __builtin_bswap64(n);
@@ -51104,7 +51691,6 @@
 static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult)
 {
     drflac_uint32 result;
-    drflac_uint32 signbit;
     DRFLAC_ASSERT(bs != NULL);
     DRFLAC_ASSERT(pResult != NULL);
     DRFLAC_ASSERT(bitCount > 0);
@@ -51112,8 +51698,11 @@
     if (!drflac__read_uint32(bs, bitCount, &result)) {
         return DRFLAC_FALSE;
     }
-    signbit = ((result >> (bitCount-1)) & 0x01);
-    result |= (~signbit + 1) << bitCount;
+    if (bitCount < 32) {
+        drflac_uint32 signbit;
+        signbit = ((result >> (bitCount-1)) & 0x01);
+        result |= (~signbit + 1) << bitCount;
+    }
     *pResult = (drflac_int32)result;
     return DRFLAC_TRUE;
 }
@@ -51280,7 +51869,7 @@
 #if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
 #define DRFLAC_IMPLEMENT_CLZ_LZCNT
 #endif
-#if  defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86))
+#if  defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(__clang__)
 #define DRFLAC_IMPLEMENT_CLZ_MSVC
 #endif
 static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x)
@@ -51327,7 +51916,7 @@
 }
 static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x)
 {
-#if defined(_MSC_VER) && !defined(__clang__)
+#if defined(_MSC_VER)
     #ifdef DRFLAC_64BIT
         return (drflac_uint32)__lzcnt64(x);
     #else
@@ -51339,7 +51928,7 @@
             {
                 drflac_uint64 r;
                 __asm__ __volatile__ (
-                    "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x)
+                    "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
                 );
                 return (drflac_uint32)r;
             }
@@ -51347,7 +51936,7 @@
             {
                 drflac_uint32 r;
                 __asm__ __volatile__ (
-                    "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x)
+                    "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
                 );
                 return r;
             }
@@ -53063,6 +53652,9 @@
     if (!drflac__read_int8(bs, 5, &lpcShift)) {
         return DRFLAC_FALSE;
     }
+    if (lpcShift < 0) {
+        return DRFLAC_FALSE;
+    }
     DRFLAC_ZERO_MEMORY(coefficients, sizeof(coefficients));
     for (i = 0; i < lpcOrder; ++i) {
         if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) {
@@ -58386,7 +58978,7 @@
 #endif  /* DRFLAC_IMPLEMENTATION */
 #endif  /* MA_NO_FLAC */
 
-#ifndef MA_NO_MP3
+#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
 #if !defined(DR_MP3_IMPLEMENTATION) && !defined(DRMP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
 /* dr_mp3_c begin */
 #ifndef dr_mp3_c
@@ -58406,7 +58998,7 @@
         *pRevision = DRMP3_VERSION_REVISION;
     }
 }
-DRMP3_API const char* drmp3_version_string()
+DRMP3_API const char* drmp3_version_string(void)
 {
     return DRMP3_VERSION_STRING;
 }
@@ -60284,7 +60876,9 @@
         drmp3dec_frame_info info;
         if (pMP3->dataSize < DRMP3_MIN_DATA_CHUNK_SIZE) {
             size_t bytesRead;
-            memmove(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
+            if (pMP3->pData != NULL) {
+                memmove(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
+            }
             pMP3->dataConsumed = 0;
             if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) {
                 drmp3_uint8* pNewData;
@@ -61122,7 +61716,7 @@
             if (framesJustRead == 0) {
                 break;
             }
-            drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels);
+            drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels);
             totalPCMFramesRead += framesJustRead;
         }
         return totalPCMFramesRead;
@@ -61626,20 +62220,198 @@
 #if defined(_MSC_VER)
     #pragma warning(pop)
 #endif
+
+#endif  /* miniaudio_c */
 #endif  /* MINIAUDIO_IMPLEMENTATION */
 
 /*
-MAJOR CHANGES IN VERSION 0.9
-============================
-Version 0.9 includes major API changes, centered mostly around full-duplex and the rebrand to "miniaudio". Before I go into
-detail about the major changes I would like to apologize. I know it's annoying dealing with breaking API changes, but I think
-it's best to get these changes out of the way now while the library is still relatively young and unknown.
+RELEASE NOTES - VERSION 0.10.x
+==============================
+Version 0.10 includes major API changes and refactoring, mostly concerned with the data conversion system. Data conversion is performed internally to convert
+audio data between the format requested when initializing the `ma_device` object and the format of the internal device used by the backend. The same applies
+to the `ma_decoder` object. The previous design has several design flaws and missing features which necessitated a complete redesign.
 
-There's been a lot of refactoring with this release so there's a good chance a few bugs have been introduced. I apologize in
-advance for this. You may want to hold off on upgrading for the short term if you're worried. If mini_al v0.8.14 works for
-you, and you don't need full-duplex support, you can avoid upgrading (though you won't be getting future bug fixes).
 
+Changes to Data Conversion
+--------------------------
+The previous data conversion system used callbacks to deliver input data for conversion. This design works well in some specific situations, but in other
+situations it has some major readability and maintenance issues. The decision was made to replace this with a more iterative approach where you just pass in a
+pointer to the input data directly rather than dealing with a callback.
 
+The following are the data conversion APIs that have been removed and their replacements:
+
+  - ma_format_converter -> ma_convert_pcm_frames_format()
+  - ma_channel_router   -> ma_channel_converter
+  - ma_src              -> ma_resampler
+  - ma_pcm_converter    -> ma_data_converter
+
+The previous conversion APIs accepted a callback in their configs. There are no longer any callbacks to deal with. Instead you just pass the data into the
+`*_process_pcm_frames()` function as a pointer to a buffer.
+
+The simplest aspect of data conversion is sample format conversion. To convert between two formats, just call `ma_convert_pcm_frames_format()`. Channel
+conversion is also simple which you can do with `ma_channel_converter` via `ma_channel_converter_process_pcm_frames()`.
+
+Resampling is more complicated because the number of output frames that are processed is different to the number of input frames that are consumed. When you
+call `ma_resampler_process_pcm_frames()` you need to pass in the number of input frames available for processing and the number of output frames you want to
+output. Upon returning they will receive the number of input frames that were consumed and the number of output frames that were generated.
+
+The `ma_data_converter` API is a wrapper around format, channel and sample rate conversion and handles all of the data conversion you'll need which probably
+makes it the best option if you need to do data conversion.
+
+In addition to changes to the API design, a few other changes have been made to the data conversion pipeline:
+
+  - The sinc resampler has been removed. This was completely broken and never actually worked properly.
+  - The linear resampler now uses low-pass filtering to remove aliasing. The quality of the low-pass filter can be controlled via the resampler config with the
+    `lpfOrder` option, which has a maximum value of MA_MAX_FILTER_ORDER.
+  - Data conversion now supports s16 natively which runs through a fixed point pipeline. Previously everything needed to be converted to floating point before
+    processing, whereas now both s16 and f32 are natively supported. Other formats still require conversion to either s16 or f32 prior to processing, however
+    `ma_data_converter` will handle this for you.
+
+
+Custom Memory Allocators
+------------------------
+miniaudio has always supported macro level customization for memory allocation via MA_MALLOC, MA_REALLOC and MA_FREE, however some scenarios require more
+flexibility by allowing a user data pointer to be passed to the custom allocation routines. Support for this has been added to version 0.10 via the
+`ma_allocation_callbacks` structure. Anything making use of heap allocations has been updated to accept this new structure.
+
+The `ma_context_config` structure has been updated with a new member called `allocationCallbacks`. Leaving this set to it's defaults returned by
+`ma_context_config_init()` will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. Likewise, The `ma_decoder_config` structure has been updated in the same
+way, and leaving everything as-is after `ma_decoder_config_init()` will cause it to use the same defaults.
+
+The following APIs have been updated to take a pointer to a `ma_allocation_callbacks` object. Setting this parameter to NULL will cause it to use defaults.
+Otherwise they will use the relevant callback in the structure.
+
+  - ma_malloc()
+  - ma_realloc()
+  - ma_free()
+  - ma_aligned_malloc()
+  - ma_aligned_free()
+  - ma_rb_init() / ma_rb_init_ex()
+  - ma_pcm_rb_init() / ma_pcm_rb_init_ex()
+
+Note that you can continue to use MA_MALLOC, MA_REALLOC and MA_FREE as per normal. These will continue to be used by default if you do not specify custom
+allocation callbacks.
+
+
+Buffer and Period Configuration Changes
+---------------------------------------
+The way in which the size of the internal buffer and periods are specified in the device configuration have changed. In previous versions, the config variables
+`bufferSizeInFrames` and `bufferSizeInMilliseconds` defined the size of the entire buffer, with the size of a period being the size of this variable divided by
+the period count. This became confusing because people would expect the value of `bufferSizeInFrames` or `bufferSizeInMilliseconds` to independantly determine
+latency, when in fact it was that value divided by the period count that determined it. These variables have been removed and replaced with new ones called
+`periodSizeInFrames` and `periodSizeInMilliseconds`.
+
+These new configuration variables work in the same way as their predecessors in that if one is set to 0, the other will be used, but the main difference is
+that you now set these to you desired latency rather than the size of the entire buffer. The benefit of this is that it's much easier and less confusing to
+configure latency.
+
+The following unused APIs have been removed:
+
+    ma_get_default_buffer_size_in_milliseconds()
+    ma_get_default_buffer_size_in_frames()
+
+The following macros have been removed:
+
+    MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY
+    MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE
+
+
+Other API Changes
+-----------------
+Other less major API changes have also been made in version 0.10.
+
+`ma_device_set_stop_callback()` has been removed. If you require a stop callback, you must now set it via the device config just like the data callback.
+
+The `ma_sine_wave` API has been replaced with a more general API called `ma_waveform`. This supports generation of different types of waveforms, including
+sine, square, triangle and sawtooth. Use `ma_waveform_init()` in place of `ma_sine_wave_init()` to initialize the waveform object. This takes a configuration
+object called `ma_waveform_config` which defines the properties of the waveform. Use `ma_waveform_config_init()` to initialize a `ma_waveform_config` object.
+Use `ma_waveform_read_pcm_frames()` in place of `ma_sine_wave_read_f32()` and `ma_sine_wave_read_f32_ex()`.
+
+`ma_convert_frames()` and `ma_convert_frames_ex()` have been changed. Both of these functions now take a new parameter called `frameCountOut` which specifies
+the size of the output buffer in PCM frames. This has been added for safety. In addition to this, the parameters for `ma_convert_frames_ex()` have changed to
+take a pointer to a `ma_data_converter_config` object to specify the input and output formats to convert between. This was done to make it more flexible, to
+prevent the parameter list getting too long, and to prevent API breakage whenever a new conversion property is added.
+
+`ma_calculate_frame_count_after_src()` has been renamed to `ma_calculate_frame_count_after_resampling()` for consistency with the new `ma_resampler` API.
+
+
+Filters
+-------
+The following filters have been added:
+
+    |-------------|-------------------------------------------------------------------|
+    | API         | Description                                                       |
+    |-------------|-------------------------------------------------------------------|
+    | ma_biquad   | Biquad filter (transposed direct form 2)                          |
+    | ma_lpf1     | First order low-pass filter                                       |
+    | ma_lpf2     | Second order low-pass filter                                      |
+    | ma_lpf      | High order low-pass filter (Butterworth)                          |
+    | ma_hpf1     | First order high-pass filter                                      |
+    | ma_hpf2     | Second order high-pass filter                                     |
+    | ma_hpf      | High order high-pass filter (Butterworth)                         |
+    | ma_bpf2     | Second order band-pass filter                                     |
+    | ma_bpf      | High order band-pass filter                                       |
+    | ma_peak2    | Second order peaking filter                                       |
+    | ma_notch2   | Second order notching filter                                      |
+    | ma_loshelf2 | Second order low shelf filter                                     |
+    | ma_hishelf2 | Second order high shelf filter                                    |
+    |-------------|-------------------------------------------------------------------|
+
+These filters all support 32-bit floating point and 16-bit signed integer formats natively. Other formats need to be converted beforehand.
+
+
+Sine, Square, Triangle and Sawtooth Waveforms
+---------------------------------------------
+Previously miniaudio supported only sine wave generation. This has now been generalized to support sine, square, triangle and sawtooth waveforms. The old
+`ma_sine_wave` API has been removed and replaced with the `ma_waveform` API. Use `ma_waveform_config_init()` to initialize a config object, and then pass it
+into `ma_waveform_init()`. Then use `ma_waveform_read_pcm_frames()` to read PCM data.
+
+
+Noise Generation
+----------------
+A noise generation API has been added. This is used via the `ma_noise` API. Currently white, pink and Brownian noise is supported. The `ma_noise` API is
+similar to the waveform API. Use `ma_noise_config_init()` to initialize a config object, and then pass it into `ma_noise_init()` to initialize a `ma_noise`
+object. Then use `ma_noise_read_pcm_frames()` to read PCM data.
+
+
+Miscellaneous Changes
+---------------------
+The MA_NO_STDIO option has been removed. This would disable file I/O APIs, however this has proven to be too hard to maintain for it's perceived value and was
+therefore removed.
+
+Internal functions have all been made static where possible. If you get warnings about unused functions, please submit a bug report.
+
+The `ma_device` structure is no longer defined as being aligned to MA_SIMD_ALIGNMENT. This resulted in a possible crash when allocating a `ma_device` object on
+the heap, but not aligning it to MA_SIMD_ALIGNMENT. This crash would happen due to the compiler seeing the alignment specified on the structure and assuming it
+was always aligned as such and thinking it was safe to emit alignment-dependant SIMD instructions. Since miniaudio's philosophy is for things to just work,
+this has been removed from all structures.
+
+Results codes have been overhauled. Unnecessary result codes have been removed, and some have been renumbered for organisation purposes. If you are are binding
+maintainer you will need to update your result codes. Support has also been added for retrieving a human readable description of a given result code via the
+`ma_result_description()` API.
+
+ALSA: The automatic format conversion, channel conversion and resampling performed by ALSA is now disabled by default as they were causing some compatibility
+issues with certain devices and configurations. These can be individually enabled via the device config:
+
+    ```c
+    deviceConfig.alsa.noAutoFormat   = MA_TRUE;
+    deviceConfig.alsa.noAutoChannels = MA_TRUE;
+    deviceConfig.alsa.noAutoResample = MA_TRUE;
+    ```
+*/
+
+/*
+RELEASE NOTES - VERSION 0.9.x
+=============================
+Version 0.9 includes major API changes, centered mostly around full-duplex and the rebrand to "miniaudio". Before I go into detail about the major changes I
+would like to apologize. I know it's annoying dealing with breaking API changes, but I think it's best to get these changes out of the way now while the
+library is still relatively young and unknown.
+
+There's been a lot of refactoring with this release so there's a good chance a few bugs have been introduced. I apologize in advance for this. You may want to
+hold off on upgrading for the short term if you're worried. If mini_al v0.8.14 works for you, and you don't need full-duplex support, you can avoid upgrading
+(though you won't be getting future bug fixes).
+
+
 Rebranding to "miniaudio"
 -------------------------
 The decision was made to rename mini_al to miniaudio. Don't worry, it's the same project. The reason for this is simple:
@@ -61647,13 +62419,11 @@
 1) Having the word "audio" in the title makes it immediately clear that the library is related to audio; and
 2) I don't like the look of the underscore.
 
-This rebrand has necessitated a change in namespace from "mal" to "ma". I know this is annoying, and I apologize, but it's
-better to get this out of the road now rather than later. Also, since there are necessary API changes for full-duplex support
-I think it's better to just get the namespace change over and done with at the same time as the full-duplex changes. I'm hoping
-this will be the last of the major API changes. Fingers crossed!
+This rebrand has necessitated a change in namespace from "mal" to "ma". I know this is annoying, and I apologize, but it's better to get this out of the road
+now rather than later. Also, since there are necessary API changes for full-duplex support I think it's better to just get the namespace change over and done
+with at the same time as the full-duplex changes. I'm hoping this will be the last of the major API changes. Fingers crossed!
 
-The implementation define is now "#define MINIAUDIO_IMPLEMENTATION". You can also use "#define MA_IMPLEMENTATION" if that's
-your preference.
+The implementation define is now "#define MINIAUDIO_IMPLEMENTATION". You can also use "#define MA_IMPLEMENTATION" if that's your preference.
 
 
 Full-Duplex Support
@@ -61660,26 +62430,25 @@
 -------------------
 The major feature added to version 0.9 is full-duplex. This has necessitated a few API changes.
 
-1) The data callback has now changed. Previously there was one type of callback for playback and another for capture. I wanted
-   to avoid a third callback just for full-duplex so the decision was made to break this API and unify the callbacks. Now,
-   there is just one callback which is the same for all three modes (playback, capture, duplex). The new callback looks like
-   the following:
+1) The data callback has now changed. Previously there was one type of callback for playback and another for capture. I wanted to avoid a third callback just
+   for full-duplex so the decision was made to break this API and unify the callbacks. Now, there is just one callback which is the same for all three modes
+   (playback, capture, duplex). The new callback looks like the following:
 
        void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
 
-   This callback allows you to move data straight out of the input buffer and into the output buffer in full-duplex mode. In
-   playback-only mode, pInput will be null. Likewise, pOutput will be null in capture-only mode. The sample count is no longer
-   returned from the callback since it's not necessary for miniaudio anymore.
+   This callback allows you to move data straight out of the input buffer and into the output buffer in full-duplex mode. In playback-only mode, pInput will be
+   null. Likewise, pOutput will be null in capture-only mode. The sample count is no longer returned from the callback since it's not necessary for miniaudio
+   anymore.
 
-2) The device config needed to change in order to support full-duplex. Full-duplex requires the ability to allow the client
-   to choose a different PCM format for the playback and capture sides. The old ma_device_config object simply did not allow
-   this and needed to change. With these changes you now specify the device ID, format, channels, channel map and share mode
-   on a per-playback and per-capture basis (see example below). The sample rate must be the same for playback and capture.
+2) The device config needed to change in order to support full-duplex. Full-duplex requires the ability to allow the client to choose a different PCM format
+   for the playback and capture sides. The old ma_device_config object simply did not allow this and needed to change. With these changes you now specify the
+   device ID, format, channels, channel map and share mode on a per-playback and per-capture basis (see example below). The sample rate must be the same for
+   playback and capture.
 
-   Since the device config API has changed I have also decided to take the opportunity to simplify device initialization. Now,
-   the device ID, device type and callback user data are set in the config. ma_device_init() is now simplified down to taking
-   just the context, device config and a pointer to the device object being initialized. The rationale for this change is that
-   it just makes more sense to me that these are set as part of the config like everything else.
+   Since the device config API has changed I have also decided to take the opportunity to simplify device initialization. Now, the device ID, device type and
+   callback user data are set in the config. ma_device_init() is now simplified down to taking just the context, device config and a pointer to the device
+   object being initialized. The rationale for this change is that it just makes more sense to me that these are set as part of the config like everything
+   else.
 
    Example device initialization:
 
@@ -61699,11 +62468,10 @@
            ... handle error ...
        }
 
-   Note that the "onDataCallback" member of ma_device_config has been renamed to "dataCallback". Also, "onStopCallback" has
-   been renamed to "stopCallback".
+   Note that the "onDataCallback" member of ma_device_config has been renamed to "dataCallback". Also, "onStopCallback" has been renamed to "stopCallback".
 
-This is the first pass for full-duplex and there is a known bug. You will hear crackling on the following backends when sample
-rate conversion is required for the playback device:
+This is the first pass for full-duplex and there is a known bug. You will hear crackling on the following backends when sample rate conversion is required for
+the playback device:
   - Core Audio
   - JACK
   - AAudio
@@ -61710,9 +62478,8 @@
   - OpenSL
   - WebAudio
 
-In addition to the above, not all platforms have been absolutely thoroughly tested simply because I lack the hardware for such
-thorough testing. If you experience a bug, an issue report on GitHub or an email would be greatly appreciated (and a sample
-program that reproduces the issue if possible).
+In addition to the above, not all platforms have been absolutely thoroughly tested simply because I lack the hardware for such thorough testing. If you
+experience a bug, an issue report on GitHub or an email would be greatly appreciated (and a sample program that reproduces the issue if possible).
 
 
 Other API Changes
@@ -61735,12 +62502,12 @@
   - mal_src_set_input_sample_rate()
   - mal_src_set_output_sample_rate()
 - Error codes have been rearranged. If you're a binding maintainer you will need to update.
-- The ma_backend enums have been rearranged to priority order. The rationale for this is to simplify automatic backend selection
-  and to make it easier to see the priority. If you're a binding maintainer you will need to update.
-- ma_dsp has been renamed to ma_pcm_converter. The rationale for this change is that I'm expecting "ma_dsp" to conflict with
-  some future planned high-level APIs.
-- For functions that take a pointer/count combo, such as ma_decoder_read_pcm_frames(), the parameter order has changed so that
-  the pointer comes before the count. The rationale for this is to keep it consistent with things like memcpy().
+- The ma_backend enums have been rearranged to priority order. The rationale for this is to simplify automatic backend selection and to make it easier to see
+  the priority. If you're a binding maintainer you will need to update.
+- ma_dsp has been renamed to ma_pcm_converter. The rationale for this change is that I'm expecting "ma_dsp" to conflict with some future planned high-level
+  APIs.
+- For functions that take a pointer/count combo, such as ma_decoder_read_pcm_frames(), the parameter order has changed so that the pointer comes before the
+  count. The rationale for this is to keep it consistent with things like memcpy().
 
 
 Miscellaneous Changes
@@ -61747,23 +62514,82 @@
 ---------------------
 The following miscellaneous changes have also been made.
 
-- The AAudio backend has been added for Android 8 and above. This is Android's new "High-Performance Audio" API. (For the
-  record, this is one of the nicest audio APIs out there, just behind the BSD audio APIs).
+- The AAudio backend has been added for Android 8 and above. This is Android's new "High-Performance Audio" API. (For the record, this is one of the nicest
+  audio APIs out there, just behind the BSD audio APIs).
 - The WebAudio backend has been added. This is based on ScriptProcessorNode. This removes the need for SDL.
-- The SDL and OpenAL backends have been removed. These were originally implemented to add support for platforms for which miniaudio
-  was not explicitly supported. These are no longer needed and have therefore been removed.
-- Device initialization now fails if the requested share mode is not supported. If you ask for exclusive mode, you either get an
-  exclusive mode device, or an error. The rationale for this change is to give the client more control over how to handle cases
-  when the desired shared mode is unavailable.
-- A lock-free ring buffer API has been added. There are two varients of this. "ma_rb" operates on bytes, whereas "ma_pcm_rb"
-  operates on PCM frames.
-- The library is now licensed as a choice of Public Domain (Unlicense) _or_ MIT-0 (No Attribution) which is the same as MIT, but
-  removes the attribution requirement. The rationale for this is to support countries that don't recognize public domain.
+- The SDL and OpenAL backends have been removed. These were originally implemented to add support for platforms for which miniaudio was not explicitly
+  supported. These are no longer needed and have therefore been removed.
+- Device initialization now fails if the requested share mode is not supported. If you ask for exclusive mode, you either get an exclusive mode device, or an
+  error. The rationale for this change is to give the client more control over how to handle cases when the desired shared mode is unavailable.
+- A lock-free ring buffer API has been added. There are two varients of this. "ma_rb" operates on bytes, whereas "ma_pcm_rb" operates on PCM frames.
+- The library is now licensed as a choice of Public Domain (Unlicense) _or_ MIT-0 (No Attribution) which is the same as MIT, but removes the attribution
+  requirement. The rationale for this is to support countries that don't recognize public domain.
 */
 
 /*
 REVISION HISTORY
-================
+================
+v0.10.18 - 2020-08-30
+  - Fix build errors with VC6.
+  - Fix a bug in channel converter for s32 format.
+  - Change channel converter configs to use the default channel map instead of a blank channel map when no channel map is specified when initializing the
+    config. This fixes an issue where the optimized mono expansion path would never get used.
+  - Use a more appropriate default format for FLAC decoders. This will now use ma_format_s16 when the FLAC is encoded as 16-bit.
+  - Update FLAC decoder.
+  - Update links to point to the new repository location (https://github.com/mackron/miniaudio).
+
+v0.10.17 - 2020-08-28
+  - Fix an error where the WAV codec is incorrectly excluded from the build depending on which compile time options are set.
+  - Fix a bug in ma_audio_buffer_read_pcm_frames() where it isn't returning the correct number of frames processed.
+  - Fix compilation error on Android.
+  - Core Audio: Fix a bug with full-duplex mode.
+  - Add ma_decoder_get_cursor_in_pcm_frames().
+  - Update WAV codec.
+
+v0.10.16 - 2020-08-14
+  - WASAPI: Fix a potential crash due to using an uninitialized variable.
+  - OpenSL: Enable runtime linking.
+  - OpenSL: Fix a multithreading bug when initializing and uninitializing multiple contexts at the same time.
+  - iOS: Improvements to device enumeration.
+  - Fix a crash in ma_data_source_read_pcm_frames() when the output frame count parameter is NULL.
+  - Fix a bug in ma_data_source_read_pcm_frames() where looping doesn't work.
+  - Fix some compilation warnings on Windows when both DirectSound and WinMM are disabled.
+  - Fix some compilation warnings when no decoders are enabled.
+  - Add ma_audio_buffer_get_available_frames().
+  - Add ma_decoder_get_available_frames().
+  - Add sample rate to ma_data_source_get_data_format().
+  - Change volume APIs to take 64-bit frame counts.
+  - Updates to documentation.
+
+v0.10.15 - 2020-07-15
+  - Fix a bug when converting bit-masked channel maps to miniaudio channel maps. This affects the WASAPI and OpenSL backends.
+
+v0.10.14 - 2020-07-14
+  - Fix compilation errors on Android.
+  - Fix compilation errors with -march=armv6.
+  - Updates to the documentation.
+
+v0.10.13 - 2020-07-11
+  - Fix some potential buffer overflow errors with channel maps when channel counts are greater than MA_MAX_CHANNELS.
+  - Fix compilation error on Emscripten.
+  - Silence some unused function warnings.
+  - Increase the default buffer size on the Web Audio backend. This fixes glitching issues on some browsers.
+  - Bring FLAC decoder up-to-date with dr_flac.
+  - Bring MP3 decoder up-to-date with dr_mp3.
+
+v0.10.12 - 2020-07-04
+  - Fix compilation errors on the iOS build.
+
+v0.10.11 - 2020-06-28
+  - Fix some bugs with device tracking on Core Audio.
+  - Updates to documentation.
+
+v0.10.10 - 2020-06-26
+  - Add include guard for the implementation section.
+  - Mark ma_device_sink_info_callback() as static.
+  - Fix compilation errors with MA_NO_DECODING and MA_NO_ENCODING.
+  - Fix compilation errors with MA_NO_DEVICE_IO
+
 v0.10.9 - 2020-06-24
   - Amalgamation of dr_wav, dr_flac and dr_mp3. With this change, including the header section of these libraries before the implementation of miniaudio is no
     longer required. Decoding of WAV, FLAC and MP3 should be supported seamlessly without any additional libraries. Decoders can be excluded from the build