diff --git a/src/audio.c b/src/audio.c
index 851880457..4670b99d8 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -340,11 +340,7 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device *pDevice, mal_uint32 frameC
                         framesToReadRightNow = sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS;
                     }
 
-                    // If we're not looping, we need to make sure we flush the internal buffers of the DSP pipeline to ensure we get the
-                    // last few samples.
-                    bool flushDSP = !audioBuffer->looping;
-
-                    mal_uint32 framesJustRead = mal_dsp_read_ex(&audioBuffer->dsp, framesToReadRightNow, tempBuffer, flushDSP, audioBuffer->dsp.pUserData);
+                    mal_uint32 framesJustRead = (mal_uint32)mal_dsp_read(&audioBuffer->dsp, framesToReadRightNow, tempBuffer, audioBuffer->dsp.pUserData);
                     if (framesJustRead > 0) 
                     {
                         float *framesOut = (float *)pFramesOut + (framesRead*device.channels);
@@ -920,13 +916,13 @@ Sound LoadSoundFromWave(Wave wave)
         mal_format formatIn  = ((wave.sampleSize == 8) ? mal_format_u8 : ((wave.sampleSize == 16) ? mal_format_s16 : mal_format_f32));
         mal_uint32 frameCountIn = wave.sampleCount;  // Is wave->sampleCount actually the frame count? That terminology needs to change, if so.
 
-        mal_uint32 frameCount = mal_convert_frames(NULL, DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, formatIn, wave.channels, wave.sampleRate, frameCountIn);
+        mal_uint32 frameCount = (mal_uint32)mal_convert_frames(NULL, DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, formatIn, wave.channels, wave.sampleRate, frameCountIn);
         if (frameCount == 0) TraceLog(LOG_WARNING, "LoadSoundFromWave() : Failed to get frame count for format conversion");
 
         AudioBuffer* audioBuffer = CreateAudioBuffer(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, frameCount, AUDIO_BUFFER_USAGE_STATIC);
         if (audioBuffer == NULL) TraceLog(LOG_WARNING, "LoadSoundFromWave() : Failed to create audio buffer");
 
-        frameCount = mal_convert_frames(audioBuffer->buffer, audioBuffer->dsp.formatConverterIn.config.formatIn, audioBuffer->dsp.formatConverterIn.config.channels, audioBuffer->dsp.src.config.sampleRateIn, wave.data, formatIn, wave.channels, wave.sampleRate, frameCountIn);
+        frameCount = (mal_uint32)mal_convert_frames(audioBuffer->buffer, audioBuffer->dsp.formatConverterIn.config.formatIn, audioBuffer->dsp.formatConverterIn.config.channels, audioBuffer->dsp.src.config.sampleRateIn, wave.data, formatIn, wave.channels, wave.sampleRate, frameCountIn);
         if (frameCount == 0) TraceLog(LOG_WARNING, "LoadSoundFromWave() : Format conversion failed");
 
         sound.audioBuffer = audioBuffer;
@@ -1158,7 +1154,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
 
     mal_uint32 frameCountIn = wave->sampleCount;  // Is wave->sampleCount actually the frame count? That terminology needs to change, if so.
 
-    mal_uint32 frameCount = mal_convert_frames(NULL, formatOut, channels, sampleRate, NULL, formatIn, wave->channels, wave->sampleRate, frameCountIn);
+    mal_uint32 frameCount = (mal_uint32)mal_convert_frames(NULL, formatOut, channels, sampleRate, NULL, formatIn, wave->channels, wave->sampleRate, frameCountIn);
     if (frameCount == 0) 
     {
         TraceLog(LOG_ERROR, "WaveFormat() : Failed to get frame count for format conversion.");
@@ -1167,7 +1163,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
 
     void *data = malloc(frameCount*channels*(sampleSize/8));
 
-    frameCount = mal_convert_frames(data, formatOut, channels, sampleRate, wave->data, formatIn, wave->channels, wave->sampleRate, frameCountIn);
+    frameCount = (mal_uint32)mal_convert_frames(data, formatOut, channels, sampleRate, wave->data, formatIn, wave->channels, wave->sampleRate, frameCountIn);
     if (frameCount == 0) 
     {
         TraceLog(LOG_ERROR, "WaveFormat() : Format conversion failed.");
diff --git a/src/external/mini_al.c b/src/external/mini_al.c
index 7b437851a..1ce944190 100644
--- a/src/external/mini_al.c
+++ b/src/external/mini_al.c
@@ -1,4 +1,4 @@
 // The implementation of mini_al needs to #include windows.h which means it needs to go into
 // it's own translation unit. Not doing this will cause conflicts with CloseWindow(), etc.
-#define MAL_IMPLEMENTATION
+#define MINI_AL_IMPLEMENTATION
 #include "mini_al.h"
\ No newline at end of file
diff --git a/src/external/mini_al.h b/src/external/mini_al.h
index 081a91718..4d05b218f 100644
--- a/src/external/mini_al.h
+++ b/src/external/mini_al.h
@@ -38,7 +38,7 @@
 // USAGE
 // =====
 // mini_al is a single-file library. To use it, do something like the following in one .c file.
-//   #define MAL_IMPLEMENTATION
+//   #define MINI_AL_IMPLEMENTATION
 //   #include "mini_al.h"
 //
 // You can then #include this file in other parts of the program as you would with any other header file.
@@ -62,8 +62,8 @@
 //
 // Building for BSD
 // ----------------
-// The BSD build uses OSS. Requires linking to -lpthread. Also requires linking to -lossaudio on {Open,Net}BSD, but
-// not FreeBSD.
+// The BSD build uses OSS. Requires linking to -lpthread and -lm. Also requires linking to -lossaudio on {Open,Net}BSD,
+// but not FreeBSD.
 //
 // Building for Android
 // --------------------
@@ -189,14 +189,15 @@
 // #define MAL_NO_NULL
 //   Disables the null backend.
 //
-// #define MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS
-//   When a buffer size of 0 is specified when a device is initialized, it will default to a size with
-//   this number of milliseconds worth of data. Note that some backends may adjust this setting if that
-//   particular backend has unusual latency characteristics.
-//
 // #define MAL_DEFAULT_PERIODS
 //   When a period count of 0 is specified when a device is initialized, it will default to this.
 //
+// #define MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY
+// #define MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE
+//   When a buffer size of 0 is specified when a device is initialized it will default to a buffer of this size (depending
+//   on the chosen performance profile) multiplied by a weight which is calculated at run-time. These can be increased or
+//   decreased depending on your specific requirements.
+//
 // #define MAL_NO_DECODING
 //   Disables the decoding APIs.
 //
@@ -238,7 +239,12 @@ extern "C" {
     #define MAL_POSIX
     #include <pthread.h>    // Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types.
 
-    #define MAL_UNIX
+    #ifdef __unix__
+        #define MAL_UNIX
+        #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+            #define MAL_BSD
+        #endif
+    #endif
     #ifdef __linux__
         #define MAL_LINUX
     #endif
@@ -268,7 +274,7 @@ extern "C" {
             #define MAL_SUPPORT_ALSA
         #endif
     #endif
-    #if !defined(MAL_ANDROID) && !defined(MAL_EMSCRIPTEN)
+    #if !defined(MAL_BSD) && !defined(MAL_ANDROID) && !defined(MAL_EMSCRIPTEN)
         #define MAL_SUPPORT_PULSEAUDIO
         #define MAL_SUPPORT_JACK
     #endif
@@ -337,8 +343,10 @@ extern "C" {
             #define MAL_HAS_STDINT
         #endif
     #else
-        #if defined(__has_include) && __has_include(<stdint.h>)
-            #define MAL_HAS_STDINT
+        #if defined(__has_include)
+            #if __has_include(<stdint.h>)
+                #define MAL_HAS_STDINT
+            #endif
         #endif
     #endif
 #endif
@@ -353,7 +361,7 @@ typedef unsigned int                mal_uint32;
     #if defined(_MSC_VER)
     typedef   signed __int64        mal_int64;
     typedef unsigned __int64        mal_uint64;
-    #elif defined(__GNUC__)
+    #else
     typedef   signed long long int  mal_int64;
     typedef unsigned long long int  mal_uint64;
     #endif
@@ -417,8 +425,10 @@ typedef mal_uint16 wchar_t;
 
 #ifdef _MSC_VER
 #define MAL_ALIGN(alignment) __declspec(align(alignment))
-#else
+#elif !defined(__DMC__)
 #define MAL_ALIGN(alignment) __attribute__((aligned(alignment)))
+#else
+#define MAL_ALIGN(alignment)
 #endif
 
 #ifdef _MSC_VER
@@ -540,6 +550,11 @@ typedef struct
 #define MAL_MAX_CHANNELS                                32
 #define MAL_MIN_SAMPLE_RATE                             MAL_SAMPLE_RATE_8000
 #define MAL_MAX_SAMPLE_RATE                             MAL_SAMPLE_RATE_384000
+#define MAL_SRC_SINC_MIN_WINDOW_WIDTH                   2
+#define MAL_SRC_SINC_MAX_WINDOW_WIDTH                   32
+#define MAL_SRC_SINC_DEFAULT_WINDOW_WIDTH               16
+#define MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION            8
+#define MAL_SRC_INPUT_BUFFER_SIZE_IN_SAMPLES            256
 
 typedef mal_uint8 mal_channel;
 #define MAL_CHANNEL_NONE                                0
@@ -630,6 +645,7 @@ typedef int mal_result;
 #define MAL_FAILED_TO_CREATE_THREAD                     -28
 #define MAL_INVALID_DEVICE_CONFIG                       -29
 #define MAL_ACCESS_DENIED                               -30
+#define MAL_TOO_LARGE                                   -31
 
 typedef void       (* mal_log_proc) (mal_context* pContext, mal_device* pDevice, const char* message);
 typedef void       (* mal_recv_proc)(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples);
@@ -677,8 +693,8 @@ typedef enum
 typedef enum
 {
     mal_dither_mode_none = 0,
-    //mal_dither_mode_rectangle,
-    //mal_dither_mode_triangle
+    mal_dither_mode_rectangle,
+    mal_dither_mode_triangle
 } mal_dither_mode;
 
 typedef enum
@@ -691,6 +707,7 @@ typedef enum
     mal_format_s24     = 3,     // Tightly packed. 3 bytes per sample.
     mal_format_s32     = 4,
     mal_format_f32     = 5,
+    mal_format_count
 } mal_format;
 
 typedef enum
@@ -710,6 +727,12 @@ typedef enum
     mal_standard_channel_map_default = mal_standard_channel_map_microsoft
 } mal_standard_channel_map;
 
+typedef enum
+{
+    mal_performance_profile_low_latency = 0,
+    mal_performance_profile_conservative
+} mal_performance_profile;
+
 typedef union
 {
 #ifdef MAL_SUPPORT_WASAPI
@@ -756,8 +779,18 @@ typedef struct
     mal_device_id id;
     char name[256];
 
-    // Detailed info. As much of this is filled as possible with mal_context_get_device_info().
-    // TODO: Implement me.
+    // Detailed info. As much of this is filled as possible with mal_context_get_device_info(). Note that you are allowed to initialize
+    // a device with settings outside of this range, but it just means the data will be converted using mini_al's data conversion
+    // pipeline before sending the data to/from the device. Most programs will need to not worry about these values, but it's provided
+    // here mainly for informational purposes or in the rare case that someone might find it useful.
+    //
+    // These will be set to 0 when returned by mal_context_enumerate_devices() or mal_context_get_devices().
+    mal_uint32 formatCount;
+    mal_format formats[mal_format_count];
+    mal_uint32 minChannels;
+    mal_uint32 maxChannels;
+    mal_uint32 minSampleRate;
+    mal_uint32 maxSampleRate;
 } mal_device_info;
 
 typedef struct
@@ -828,16 +861,24 @@ struct mal_channel_router
 
 
 typedef struct mal_src mal_src;
-typedef mal_uint32 (* mal_src_read_proc)(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, void* pUserData); // Returns the number of frames that were read.
+//typedef mal_uint32 (* mal_src_read_proc)(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, void* pUserData); // Returns the number of frames that were read.
 typedef mal_uint32 (* mal_src_read_deinterleaved_proc)(mal_src* pSRC, mal_uint32 frameCount, void** ppSamplesOut, void* pUserData); // Returns the number of frames that were read.
 
 typedef enum
 {
-    mal_src_algorithm_linear = 0,
+    mal_src_algorithm_sinc = 0,
+    mal_src_algorithm_linear,
     mal_src_algorithm_none,
     mal_src_algorithm_default = mal_src_algorithm_linear
 } mal_src_algorithm;
 
+typedef enum
+{
+    mal_src_sinc_window_function_hann = 0,
+    mal_src_sinc_window_function_rectangular,
+    mal_src_sinc_window_function_default = mal_src_sinc_window_function_hann
+} mal_src_sinc_window_function;
+
 typedef struct
 {
     mal_uint32 sampleRateIn;
@@ -846,21 +887,38 @@ typedef struct
     mal_src_algorithm algorithm;
     mal_src_read_deinterleaved_proc onReadDeinterleaved;
     void* pUserData;
+    union
+    {
+        struct
+        {
+            mal_src_sinc_window_function windowFunction;
+            mal_uint32 windowWidth;
+        } sinc;
+    };
 } mal_src_config;
 
 MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_src
 {
-    MAL_ALIGN(MAL_SIMD_ALIGNMENT) float samplesFromClient[MAL_MAX_CHANNELS][256];
-    mal_src_config config;
-
     union
     {
         struct
         {
-            float t;
+            MAL_ALIGN(MAL_SIMD_ALIGNMENT) float input[MAL_MAX_CHANNELS][MAL_SRC_INPUT_BUFFER_SIZE_IN_SAMPLES];
+            float timeIn;
             mal_uint32 leftoverFrames;
         } linear;
+
+        struct
+        {
+            MAL_ALIGN(MAL_SIMD_ALIGNMENT) float input[MAL_MAX_CHANNELS][MAL_SRC_SINC_MAX_WINDOW_WIDTH*2 + MAL_SRC_INPUT_BUFFER_SIZE_IN_SAMPLES];
+            float timeIn;
+            mal_uint32 inputFrameCount;     // The number of frames sitting in the input buffer, not including the first half of the window.
+            mal_uint32 windowPosInSamples;  // An offset of <input>.
+            float table[MAL_SRC_SINC_MAX_WINDOW_WIDTH * MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION]; // Precomputed lookup table.
+        } sinc;
     };
+
+    mal_src_config config;
 };
 
 typedef struct mal_dsp mal_dsp;
@@ -877,10 +935,19 @@ typedef struct
     mal_uint32 sampleRateOut;
     mal_channel channelMapOut[MAL_MAX_CHANNELS];
     mal_channel_mix_mode channelMixMode;
+    mal_dither_mode ditherMode;
     mal_src_algorithm srcAlgorithm;
     mal_bool32 allowDynamicSampleRate;
     mal_dsp_read_proc onRead;
     void* pUserData;
+    union
+    {
+        struct
+        {
+            mal_src_sinc_window_function windowFunction;
+            mal_uint32 windowWidth;
+        } sinc;
+    };
 } mal_dsp_config;
 
 MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_dsp
@@ -910,6 +977,7 @@ typedef struct
     mal_uint32 bufferSizeInFrames;
     mal_uint32 periods;
     mal_share_mode shareMode;
+    mal_performance_profile performanceProfile;
     mal_recv_proc onRecvCallback;
     mal_send_proc onSendCallback;
     mal_stop_proc onStopCallback;
@@ -1026,7 +1094,11 @@ struct mal_context
             mal_proc snd_pcm_hw_params_set_access;
             mal_proc snd_pcm_hw_params_get_format;
             mal_proc snd_pcm_hw_params_get_channels;
+            mal_proc snd_pcm_hw_params_get_channels_min;
+            mal_proc snd_pcm_hw_params_get_channels_max;
             mal_proc snd_pcm_hw_params_get_rate;
+            mal_proc snd_pcm_hw_params_get_rate_min;
+            mal_proc snd_pcm_hw_params_get_rate_max;
             mal_proc snd_pcm_hw_params_get_buffer_size;
             mal_proc snd_pcm_hw_params_get_periods;
             mal_proc snd_pcm_hw_params_get_access;
@@ -1278,6 +1350,11 @@ struct mal_context
             /*HMODULE*/ mal_handle hUser32DLL;
             mal_proc GetForegroundWindow;
             mal_proc GetDesktopWindow;
+
+            /*HMODULE*/ mal_handle hAdvapi32DLL;
+            mal_proc RegOpenKeyExA;
+            mal_proc RegCloseKey;
+            mal_proc RegQueryValueExA;
         } win32;
 #endif
 #ifdef MAL_POSIX
@@ -1876,7 +1953,7 @@ static MAL_INLINE mal_device_config mal_device_config_init_playback(mal_format f
 void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]);
 
 // Copies a channel map.
-void mal_channel_map_copy(mal_channel* pOut, mal_channel* pIn, mal_uint32 channels);
+void mal_channel_map_copy(mal_channel* pOut, const mal_channel* pIn, mal_uint32 channels);
 
 
 // Determines whether or not a channel map is valid.
@@ -1904,6 +1981,39 @@ mal_bool32 mal_channel_map_contains_channel_position(mal_uint32 channels, const
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 //
 // Format Conversion
+// =================
+// The format converter serves two purposes:
+//   1) Conversion between data formats (u8 to f32, etc.)
+//   2) Interleaving and deinterleaving
+//
+// When initializing a converter, you specify the input and output formats (u8, s16, etc.) and read callbacks. There are two read callbacks - one for
+// interleaved input data (onRead) and another for deinterleaved input data (onReadDeinterleaved). You implement whichever is most convenient for you. You
+// can implement both, but it's not recommended as it just introduces unnecessary complexity.
+//
+// To read data as interleaved samples, use mal_format_converter_read(). Otherwise use mal_format_converter_read_deinterleaved().
+//
+// Dithering
+// ---------
+// The format converter also supports dithering. Dithering can be set using ditherMode variable in the config, like so.
+//
+//   pConfig->ditherMode = mal_dither_mode_rectangle;
+//
+// The different dithering modes include the following, in order of efficiency:
+//   - None:      mal_dither_mode_none
+//   - Rectangle: mal_dither_mode_rectangle
+//   - Triangle:  mal_dither_mode_triangle
+//
+// Note that even if the dither mode is set to something other than mal_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
+//   - f32 -> u8
+//   - s24 -> s16
+//   - s32 -> s16
+//   - f32 -> s16
+//
+// Note that it is not an error to pass something other than mal_dither_mode_none for conversions where dither is not used. It will just be ignored.
 //
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -1917,6 +2027,12 @@ mal_uint64 mal_format_converter_read(mal_format_converter* pConverter, mal_uint6
 mal_uint64 mal_format_converter_read_deinterleaved(mal_format_converter* pConverter, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData);
 
 
+// Helper for initializing a format converter config.
+mal_format_converter_config mal_format_converter_config_init_new();
+mal_format_converter_config mal_format_converter_config_init(mal_format formatIn, mal_format formatOut, mal_uint32 channels, mal_format_converter_read_proc onRead, void* pUserData);
+mal_format_converter_config mal_format_converter_config_init_deinterleaved(mal_format formatIn, mal_format formatOut, mal_uint32 channels, mal_format_converter_read_deinterleaved_proc onReadDeinterleaved, void* pUserData);
+
+
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 //
@@ -1996,7 +2112,6 @@ mal_channel_router_config mal_channel_router_config_init(mal_uint32 channelsIn,
 //
 // Sample Rate Conversion
 // ======================
-// Note that mini_al currently only supports low quality linear sample rate conversion.
 //
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -2017,14 +2132,10 @@ mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOu
 // Returns the number of frames actually read.
 mal_uint64 mal_src_read_deinterleaved(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData);
 
-// The same mal_src_read_deinterleaved() with extra control over whether or not the internal buffers should be flushed at the end.
-//
-// Internally there exists a buffer that keeps track of the previous and next samples for sample rate conversion. The simple
-// version of this function does _not_ flush this buffer because otherwise it causes glitches for streaming based conversion
-// pipelines. The problem, however, is that sometimes you need those last few samples (such as if you're doing a bulk conversion
-// of a static file). Enabling flushing will fix this for you.
-mal_uint64 mal_src_read_deinterleaved_ex(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, mal_bool32 flush, void* pUserData);
 
+// Helper for creating a sample rate conversion config.
+mal_src_config mal_src_config_init_new();
+mal_src_config mal_src_config_init(mal_uint32 sampleRateIn, mal_uint32 sampleRateOut, mal_uint32 channels, mal_src_read_deinterleaved_proc onReadDeinterleaved, void* pUserData);
 
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -2050,16 +2161,8 @@ mal_result mal_dsp_set_input_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut
 mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut);
 
 // Reads a number of frames and runs them through the DSP processor.
-//
-// This this _not_ flush the internal buffers which means you may end up with a few less frames than you may expect. Look at
-// mal_dsp_read_frames_ex() if you want to flush the buffers at the end of the read.
 mal_uint64 mal_dsp_read(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut, void* pUserData);
 
-// The same mal_dsp_read_frames() with extra control over whether or not the internal buffers should be flushed at the end.
-//
-// See documentation for mal_src_read_frames_ex() for an explanation on flushing.
-mal_uint64 mal_dsp_read_ex(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush, void* pUserData);
-
 // Helper for initializing a mal_dsp_config object.
 mal_dsp_config mal_dsp_config_init_new();
 mal_dsp_config mal_dsp_config_init(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_dsp_read_proc onRead, void* pUserData);
@@ -2105,6 +2208,15 @@ void mal_mutex_unlock(mal_mutex* pMutex);
 //
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
+// malloc(). Calls MAL_MALLOC().
+void* mal_malloc(size_t sz);
+
+// realloc(). Calls MAL_REALLOC().
+void* mal_realloc(void* p, size_t sz);
+
+// free(). Calls MAL_FREE().
+void mal_free(void* p);
+
 // Performs an aligned malloc, with the assumption that the alignment is a power of 2.
 void* mal_aligned_malloc(size_t sz, size_t alignment);
 
@@ -2120,6 +2232,21 @@ const char* mal_get_format_name(mal_format format);
 // Blends two frames in floating point format.
 void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint32 channels);
 
+// Calculates a scaling factor relative to speed of the system.
+//
+// This could be useful for dynamically determining the size of a device's internal buffer based on the speed of the system.
+//
+// This is a slow API because it performs a profiling test.
+float mal_calculate_cpu_speed_factor();
+
+// Adjust buffer size based on a scaling factor.
+//
+// This just multiplies the base size by the scaling factor, making sure it's a size of at least 1.
+mal_uint32 mal_scale_buffer_size(mal_uint32 baseBufferSize, float scale);
+
+// Calculates a buffer size in frames for the specified performance profile and scale factor.
+mal_uint32 mal_calculate_default_buffer_size_in_frames(mal_performance_profile performanceProfile, mal_uint32 sampleRate, float scale);
+
 
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -2173,10 +2300,10 @@ typedef mal_result (* mal_decoder_uninit_proc)       (mal_decoder* pDecoder);
 
 typedef struct
 {
-    mal_format  outputFormat;       // Set to 0 or mal_format_unknown to use the stream's internal format.
-    mal_uint32  outputChannels;     // Set to 0 to use the stream's internal channels.
-    mal_uint32  outputSampleRate;   // Set to 0 to use the stream's internal sample rate.
-    mal_channel outputChannelMap[MAL_MAX_CHANNELS];
+    mal_format  format;       // Set to 0 or mal_format_unknown to use the stream's internal format.
+    mal_uint32  channels;     // Set to 0 to use the stream's internal channels.
+    mal_uint32  sampleRate;   // Set to 0 to use the stream's internal sample rate.
+    mal_channel channelMap[MAL_MAX_CHANNELS];
 } mal_decoder_config;
 
 struct mal_decoder
@@ -2211,12 +2338,14 @@ mal_result mal_decoder_init_wav(mal_decoder_read_proc onRead, mal_decoder_seek_p
 mal_result mal_decoder_init_flac(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
 mal_result mal_decoder_init_vorbis(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
 mal_result mal_decoder_init_mp3(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
+mal_result mal_decoder_init_raw(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfigIn, const mal_decoder_config* pConfigOut, mal_decoder* pDecoder);
 
 mal_result mal_decoder_init_memory(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
 mal_result mal_decoder_init_memory_wav(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
 mal_result mal_decoder_init_memory_flac(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
 mal_result mal_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
 mal_result mal_decoder_init_memory_mp3(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
+mal_result mal_decoder_init_memory_raw(const void* pData, size_t dataSize, const mal_decoder_config* pConfigIn, const mal_decoder_config* pConfigOut, mal_decoder* pDecoder);
 
 #ifndef MAL_NO_STDIO
 mal_result mal_decoder_init_file(const char* pFilePath, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
@@ -2228,6 +2357,14 @@ mal_result mal_decoder_uninit(mal_decoder* pDecoder);
 mal_uint64 mal_decoder_read(mal_decoder* pDecoder, mal_uint64 frameCount, void* pFramesOut);
 mal_result mal_decoder_seek_to_frame(mal_decoder* pDecoder, mal_uint64 frameIndex);
 
+
+// Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with mal_free(). On input,
+// pConfig should be set to what you want. On output it will be set to what you got.
+#ifndef MAL_NO_STDIO
+mal_result mal_decode_file(const char* pFilePath, mal_decoder_config* pConfig, mal_uint64* pFrameCountOut, void** ppDataOut);
+#endif
+mal_result mal_decode_memory(const void* pData, size_t dataSize, mal_decoder_config* pConfig, mal_uint64* pFrameCountOut, void** ppDataOut);
+
 #endif  // MAL_NO_DECODING
 
 
@@ -2255,14 +2392,14 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSignWave, mal_uint64 count, float*
 #endif  //mini_al_h
 
 
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 //
 // IMPLEMENTATION
 //
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-#ifdef MAL_IMPLEMENTATION
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#if defined(MINI_AL_IMPLEMENTATION) || defined(MAL_IMPLEMENTATION)
 #include <assert.h>
 #include <limits.h> // For INT_MAX
 #include <math.h>   // sin(), etc.
@@ -2323,7 +2460,7 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSignWave, mal_uint64 count, float*
 
 // Intrinsics Support
 #if defined(MAL_X64) || defined(MAL_X86)
-    #if defined(_MSC_VER)
+    #if defined(_MSC_VER) && !defined(__clang__)
         // MSVC.
         #if !defined(MAL_NO_SSE2)   // Assume all MSVC compilers support SSE2 intrinsics.
             #define MAL_SUPPORT_SSE2
@@ -2348,7 +2485,7 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSignWave, mal_uint64 count, float*
     #endif
 
     // If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include.
-    #if !defined(__GNUC__) && defined(__has_include)
+    #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
         #if !defined(MAL_SUPPORT_SSE2)   && !defined(MAL_NO_SSE2)   && __has_include(<emmintrin.h>)
             #define MAL_SUPPORT_SSE2
         #endif
@@ -2375,7 +2512,7 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSignWave, mal_uint64 count, float*
     #endif
 
     // Fall back to looking for the #include file.
-    #if !defined(__GNUC__) && defined(__has_include)
+    #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
         #if !defined(MAL_SUPPORT_NEON) && !defined(MAL_NO_NEON) && __has_include(<arm_neon.h>)
             #define MAL_SUPPORT_NEON
         #endif
@@ -2388,7 +2525,7 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSignWave, mal_uint64 count, float*
 
 
 #if defined(MAL_X64) || defined(MAL_X86)
-    #if defined(_MSC_VER)
+    #if defined(_MSC_VER) && !defined(__clang__)
         #if _MSC_VER >= 1400
             #include <intrin.h>
             static MAL_INLINE void mal_cpuid(int info[4], int fid)
@@ -2407,22 +2544,11 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSignWave, mal_uint64 count, float*
         #else
             #define MAL_NO_XGETBV
         #endif
-    #elif defined(__GNUC__) || defined(__clang__)
+    #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MAL_ANDROID)
         static MAL_INLINE void mal_cpuid(int info[4], int fid)
         {
-            __asm__ (
-                "movl %[fid], %%eax\n\t"
-                "cpuid\n\t"
-                "movl %%eax, %[info0]\n\t"
-                "movl %%ebx, %[info1]\n\t"
-                "movl %%ecx, %[info2]\n\t"
-                "movl %%edx, %[info3]\n\t"
-                : [info0] "=rm"(info[0]),
-                  [info1] "=rm"(info[1]),
-                  [info2] "=rm"(info[2]),
-                  [info3] "=rm"(info[3])
-                : [fid] "rm"(fid)
-                : "eax", "ebx", "ecx", "edx"
+            __asm__ __volatile__ (
+                "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
             );
         }
 
@@ -2431,15 +2557,8 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSignWave, mal_uint64 count, float*
             unsigned int hi;
             unsigned int lo;
 
-            __asm__ (
-                "movl %[reg], %%ecx\n\t"
-                "xgetbv\n\t"
-                "movl %%eax, %[lo]\n\t"
-                "movl %%edx, %[hi]\n\t"
-                : [lo] "=rm"(lo),
-                  [hi] "=rm"(hi)
-                : [reg] "rm"(reg)
-                : "eax", "ecx", "edx"
+            __asm__ __volatile__ (
+                "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg)
             );
 
             return ((unsigned long long)hi << 32ULL) | (unsigned long long)lo;
@@ -2455,94 +2574,110 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSignWave, mal_uint64 count, float*
 
 static MAL_INLINE mal_bool32 mal_has_sse2()
 {
-#if (defined(MAL_X64) || defined(MAL_X86)) && !defined(MAL_NO_SSE2)
-    #if defined(MAL_X64)
-        return MAL_TRUE;    // 64-bit targets always support SSE2.
-    #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
-        return MAL_TRUE;    // If the compiler is allowed to freely generate SSE2 code we can assume support.
-    #else
-        #if defined(MAL_NO_CPUID)
-            return MAL_FALSE;
+#if defined(MAL_SUPPORT_SSE2)
+    #if (defined(MAL_X64) || defined(MAL_X86)) && !defined(MAL_NO_SSE2)
+        #if defined(MAL_X64)
+            return MAL_TRUE;    // 64-bit targets always support SSE2.
+        #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
+            return MAL_TRUE;    // If the compiler is allowed to freely generate SSE2 code we can assume support.
         #else
-            int info[4];
-            mal_cpuid(info, 1);
-            return (info[3] & (1 << 26)) != 0;
+            #if defined(MAL_NO_CPUID)
+                return MAL_FALSE;
+            #else
+                int info[4];
+                mal_cpuid(info, 1);
+                return (info[3] & (1 << 26)) != 0;
+            #endif
         #endif
+    #else
+        return MAL_FALSE;       // SSE2 is only supported on x86 and x64 architectures.
     #endif
 #else
-    return MAL_FALSE;       // SSE2 is only supported on x86 and x64 architectures.
+    return MAL_FALSE;           // No compiler support.
 #endif
 }
 
 static MAL_INLINE mal_bool32 mal_has_avx()
 {
-#if (defined(MAL_X64) || defined(MAL_X86)) && !defined(MAL_NO_AVX)
-    #if defined(_AVX_) || defined(__AVX__)
-        return MAL_TRUE;    // If the compiler is allowed to freely generate AVX code we can assume support.
-    #else
-        // AVX requires both CPU and OS support.
-        #if defined(MAL_NO_CPUID) || defined(MAL_NO_XGETBV)
-            return MAL_FALSE;
+#if defined(MAL_SUPPORT_AVX)
+    #if (defined(MAL_X64) || defined(MAL_X86)) && !defined(MAL_NO_AVX)
+        #if defined(_AVX_) || defined(__AVX__)
+            return MAL_TRUE;    // If the compiler is allowed to freely generate AVX code we can assume support.
         #else
-            int info[4];
-            mal_cpuid(info, 1);
-            if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) {
-                mal_uint64 xrc = mal_xgetbv(0);
-                if ((xrc & 0x06) == 0x06) {
-                    return MAL_TRUE;
+            // AVX requires both CPU and OS support.
+            #if defined(MAL_NO_CPUID) || defined(MAL_NO_XGETBV)
+                return MAL_FALSE;
+            #else
+                int info[4];
+                mal_cpuid(info, 1);
+                if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) {
+                    mal_uint64 xrc = mal_xgetbv(0);
+                    if ((xrc & 0x06) == 0x06) {
+                        return MAL_TRUE;
+                    } else {
+                        return MAL_FALSE;
+                    }
                 } else {
                     return MAL_FALSE;
                 }
-            } else {
-                return MAL_FALSE;
-            }
+            #endif
         #endif
+    #else
+        return MAL_FALSE;       // AVX is only supported on x86 and x64 architectures.
     #endif
 #else
-    return MAL_FALSE;       // AVX is only supported on x86 and x64 architectures.
+    return MAL_FALSE;           // No compiler support.
 #endif
 }
 
 static MAL_INLINE mal_bool32 mal_has_avx512f()
 {
-#if (defined(MAL_X64) || defined(MAL_X86)) && !defined(MAL_NO_AVX512)
-    #if defined(__AVX512F__)
-        return MAL_TRUE;    // If the compiler is allowed to freely generate AVX-512F code we can assume support.
-    #else
-        // AVX-512 requires both CPU and OS support.
-        #if defined(MAL_NO_CPUID) || defined(MAL_NO_XGETBV)
-            return MAL_FALSE;
+#if defined(MAL_SUPPORT_AVX512)
+    #if (defined(MAL_X64) || defined(MAL_X86)) && !defined(MAL_NO_AVX512)
+        #if defined(__AVX512F__)
+            return MAL_TRUE;    // If the compiler is allowed to freely generate AVX-512F code we can assume support.
         #else
-            int info[4];
-            mal_cpuid(info, 1);
-            if (((info[2] & (1 << 27)) != 0) && ((info[1] & (1 << 16)) != 0)) {
-                mal_uint64 xrc = mal_xgetbv(0);
-                if ((xrc & 0xE6) == 0xE6) {
-                    return MAL_TRUE;
+            // AVX-512 requires both CPU and OS support.
+            #if defined(MAL_NO_CPUID) || defined(MAL_NO_XGETBV)
+                return MAL_FALSE;
+            #else
+                int info[4];
+                mal_cpuid(info, 1);
+                if (((info[2] & (1 << 27)) != 0) && ((info[1] & (1 << 16)) != 0)) {
+                    mal_uint64 xrc = mal_xgetbv(0);
+                    if ((xrc & 0xE6) == 0xE6) {
+                        return MAL_TRUE;
+                    } else {
+                        return MAL_FALSE;
+                    }
                 } else {
                     return MAL_FALSE;
                 }
-            } else {
-                return MAL_FALSE;
-            }
+            #endif
         #endif
+    #else
+        return MAL_FALSE;       // AVX-512F is only supported on x86 and x64 architectures.
     #endif
 #else
-    return MAL_FALSE;       // AVX-512F is only supported on x86 and x64 architectures.
+    return MAL_FALSE;           // No compiler support.
 #endif
 }
 
 static MAL_INLINE mal_bool32 mal_has_neon()
 {
-#if defined(MAL_ARM) && !defined(MAL_NO_NEON)
-    #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
-        return MAL_TRUE;    // If the compiler is allowed to freely generate NEON code we can assume support.
+#if defined(MAL_SUPPORT_NEON)
+    #if defined(MAL_ARM) && !defined(MAL_NO_NEON)
+        #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
+            return MAL_TRUE;    // If the compiler is allowed to freely generate NEON code we can assume support.
+        #else
+            // TODO: Runtime check.
+            return MAL_FALSE;
+        #endif
     #else
-        // TODO: Runtime check.
-        return MAL_FALSE;
+        return MAL_FALSE;       // NEON is only supported on ARM architectures.
     #endif
 #else
-    return MAL_FALSE;       // NEON is only supported on ARM architectures.
+    return MAL_FALSE;           // No compiler support.
 #endif
 }
 
@@ -2670,6 +2805,11 @@ typedef int     (WINAPI * MAL_PFN_StringFromGUID2)(const GUID* const rguid, LPOL
 
 typedef HWND (WINAPI * MAL_PFN_GetForegroundWindow)();
 typedef HWND (WINAPI * MAL_PFN_GetDesktopWindow)();
+
+// Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG.
+typedef LONG (WINAPI * MAL_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult);
+typedef LONG (WINAPI * MAL_PFN_RegCloseKey)(HKEY hKey);
+typedef LONG (WINAPI * MAL_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData);
 #endif
 
 
@@ -2682,32 +2822,35 @@ typedef HWND (WINAPI * MAL_PFN_GetDesktopWindow)();
 
 // The default format when mal_format_unknown (0) is requested when initializing a device.
 #ifndef MAL_DEFAULT_FORMAT
-#define MAL_DEFAULT_FORMAT                          mal_format_f32
+#define MAL_DEFAULT_FORMAT                                  mal_format_f32
 #endif
 
 // The default channel count to use when 0 is used when initializing a device.
 #ifndef MAL_DEFAULT_CHANNELS
-#define MAL_DEFAULT_CHANNELS                        2
+#define MAL_DEFAULT_CHANNELS                                2
 #endif
 
 // The default sample rate to use when 0 is used when initializing a device.
 #ifndef MAL_DEFAULT_SAMPLE_RATE
-#define MAL_DEFAULT_SAMPLE_RATE                     48000
-#endif
-
-// The default size of the device's buffer in milliseconds.
-//
-// If this is too small you may get underruns and overruns in which case you'll need to either increase
-// this value or use an explicit buffer size.
-#ifndef MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS
-#define MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS     25
+#define MAL_DEFAULT_SAMPLE_RATE                             48000
 #endif
 
 // Default periods when none is specified in mal_device_init(). More periods means more work on the CPU.
 #ifndef MAL_DEFAULT_PERIODS
-#define MAL_DEFAULT_PERIODS                         2
+#define MAL_DEFAULT_PERIODS                                 2
+#endif
+
+// The base buffer size in milliseconds for low latency mode.
+#ifndef MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY
+#define MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY    10
+#endif
+
+// The base buffer size in milliseconds for conservative mode.
+#ifndef MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE
+#define MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE   50
 #endif
 
+
 // Standard sample rates, in order of priority.
 mal_uint32 g_malStandardSampleRatePriorities[] = {
     MAL_SAMPLE_RATE_48000,  // Most common
@@ -2738,60 +2881,64 @@ mal_uint32 g_malStandardSampleRatePriorities[] = {
 // Standard Library Stuff
 //
 ///////////////////////////////////////////////////////////////////////////////
-#ifndef mal_zero_memory
+#ifndef MAL_MALLOC
 #ifdef MAL_WIN32
-#define mal_zero_memory(p, sz) ZeroMemory((p), (sz))
+#define MAL_MALLOC(sz) HeapAlloc(GetProcessHeap(), 0, (sz))
 #else
-#define mal_zero_memory(p, sz) memset((p), 0, (sz))
+#define MAL_MALLOC(sz) malloc((sz))
 #endif
 #endif
 
-#define mal_zero_object(p) mal_zero_memory((p), sizeof(*(p)))
-
-#ifndef mal_copy_memory
+#ifndef MAL_REALLOC
 #ifdef MAL_WIN32
-#define mal_copy_memory(dst, src, sz) CopyMemory((dst), (src), (sz))
+#define MAL_REALLOC(p, sz) (((sz) > 0) ? ((p) ? HeapReAlloc(GetProcessHeap(), 0, (p), (sz)) : HeapAlloc(GetProcessHeap(), 0, (sz))) : ((VOID*)(size_t)(HeapFree(GetProcessHeap(), 0, (p)) & 0)))
 #else
-#define mal_copy_memory(dst, src, sz) memcpy((dst), (src), (sz))
+#define MAL_REALLOC(p, sz) realloc((p), (sz))
 #endif
 #endif
 
-#ifndef mal_malloc
+#ifndef MAL_FREE
 #ifdef MAL_WIN32
-#define mal_malloc(sz) HeapAlloc(GetProcessHeap(), 0, (sz))
+#define MAL_FREE(p) HeapFree(GetProcessHeap(), 0, (p))
 #else
-#define mal_malloc(sz) malloc((sz))
+#define MAL_FREE(p) free((p))
 #endif
 #endif
 
-#ifndef mal_realloc
+#ifndef MAL_ZERO_MEMORY
 #ifdef MAL_WIN32
-#define mal_realloc(p, sz) (((sz) > 0) ? ((p) ? HeapReAlloc(GetProcessHeap(), 0, (p), (sz)) : HeapAlloc(GetProcessHeap(), 0, (sz))) : ((VOID*)(SIZE_T)(HeapFree(GetProcessHeap(), 0, (p)) & 0)))
+#define MAL_ZERO_MEMORY(p, sz) ZeroMemory((p), (sz))
 #else
-#define mal_realloc(p, sz) realloc((p), (sz))
+#define MAL_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
 #endif
 #endif
 
-#ifndef mal_free
+#ifndef MAL_COPY_MEMORY
 #ifdef MAL_WIN32
-#define mal_free(p) HeapFree(GetProcessHeap(), 0, (p))
+#define MAL_COPY_MEMORY(dst, src, sz) CopyMemory((dst), (src), (sz))
 #else
-#define mal_free(p) free((p))
+#define MAL_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
 #endif
 #endif
 
-#ifndef mal_assert
+#ifndef MAL_ASSERT
 #ifdef MAL_WIN32
-#define mal_assert(condition) assert(condition)
+#define MAL_ASSERT(condition) assert(condition)
 #else
-#define mal_assert(condition) assert(condition)
+#define MAL_ASSERT(condition) assert(condition)
 #endif
 #endif
 
-#define mal_countof(x)  (sizeof(x) / sizeof(x[0]))
-#define mal_max(x, y)   (((x) > (y)) ? (x) : (y))
-#define mal_min(x, y)   (((x) < (y)) ? (x) : (y))
-#define mal_offset_ptr(p, offset) (((mal_uint8*)(p)) + (offset))
+#define mal_zero_memory MAL_ZERO_MEMORY
+#define mal_copy_memory MAL_COPY_MEMORY
+#define mal_assert      MAL_ASSERT
+
+#define mal_zero_object(p)          mal_zero_memory((p), sizeof(*(p)))
+#define mal_countof(x)              (sizeof(x) / sizeof(x[0]))
+#define mal_max(x, y)               (((x) > (y)) ? (x) : (y))
+#define mal_min(x, y)               (((x) < (y)) ? (x) : (y))
+#define mal_clamp(x, lo, hi)        (mal_max(lo, mal_min(x, hi)))
+#define mal_offset_ptr(p, offset)   (((mal_uint8*)(p)) + (offset))
 
 #define mal_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / mal_get_bytes_per_sample(format) / (channels))
 
@@ -3036,6 +3183,103 @@ static MAL_INLINE float mal_mix_f32(float x, float y, float a)
 {
     return x*(1-a) + y*a;
 }
+static MAL_INLINE float mal_mix_f32_fast(float x, float y, float a)
+{
+    return x + (y - x)*a;
+}
+
+static MAL_INLINE double mal_mix_f64(double x, double y, double a)
+{
+    return x*(1-a) + y*a;
+}
+static MAL_INLINE double mal_mix_f64_fast(double x, double y, double a)
+{
+    return x + (y - x)*a;
+}
+
+static MAL_INLINE float mal_scale_to_range_f32(float x, float lo, float hi)
+{
+    return lo + x*(hi-lo);
+}
+
+
+
+// Random Number Generation
+//
+// mini_al uses the LCG random number generation algorithm. This is good enough for audio.
+//
+// Note that mini_al's LCG implementation uses global state which is _not_ thread-local. When this is called across
+// multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough
+// for mini_al's purposes.
+#define MAL_LCG_M   4294967296
+#define MAL_LCG_A   1103515245
+#define MAL_LCG_C   12345
+static mal_int32 g_malLCG;
+
+void mal_seed(mal_int32 seed)
+{
+    g_malLCG = seed;
+}
+
+mal_int32 mal_rand_s32()
+{
+    mal_int32 lcg = g_malLCG;
+    mal_int32 r = (MAL_LCG_A * lcg + MAL_LCG_C) % MAL_LCG_M;
+    g_malLCG = r;
+    return r;
+}
+
+double mal_rand_f64()
+{
+    return (mal_rand_s32() + 0x80000000) / (double)0x7FFFFFFF;
+}
+
+float mal_rand_f32()
+{
+    return (float)mal_rand_f64();
+}
+
+static MAL_INLINE float mal_rand_range_f32(float lo, float hi)
+{
+    return mal_scale_to_range_f32(mal_rand_f32(), lo, hi);
+}
+
+static MAL_INLINE mal_int32 mal_rand_range_s32(mal_int32 lo, mal_int32 hi)
+{
+    double x = mal_rand_f64();
+    return lo + (mal_int32)(x*(hi-lo));
+}
+
+
+static MAL_INLINE float mal_dither_f32(mal_dither_mode ditherMode, float ditherMin, float ditherMax)
+{
+    if (ditherMode == mal_dither_mode_rectangle) {
+        float a = mal_rand_range_f32(ditherMin, ditherMax);
+        return a;
+    }
+    if (ditherMode == mal_dither_mode_triangle) {
+        float a = mal_rand_range_f32(ditherMin, 0);
+        float b = mal_rand_range_f32(0, ditherMax);
+        return a + b;
+    }
+
+    return 0;
+}
+
+static MAL_INLINE mal_int32 mal_dither_s32(mal_dither_mode ditherMode, mal_int32 ditherMin, mal_int32 ditherMax)
+{
+    if (ditherMode == mal_dither_mode_rectangle) {
+        mal_int32 a = mal_rand_range_s32(ditherMin, ditherMax);
+        return a;
+    }
+    if (ditherMode == mal_dither_mode_triangle) {
+        mal_int32 a = mal_rand_range_s32(ditherMin, 0);
+        mal_int32 b = mal_rand_range_s32(0, ditherMax);
+        return a + b;
+    }
+
+    return 0;
+}
 
 
 // Splits a buffer into parts of equal length and of the given alignment. The returned size of the split buffers will be a
@@ -3332,6 +3576,7 @@ mal_bool32 mal_thread_create__posix(mal_context* pContext, mal_thread* pThread,
 {
     pthread_attr_t* pAttr = NULL;
 
+#if !defined(__EMSCRIPTEN__)
     // Try setting the thread priority. It's not critical if anything fails here.
     pthread_attr_t attr;
     if (((mal_pthread_attr_init_proc)pContext->posix.pthread_attr_init)(&attr) == 0) {
@@ -3381,6 +3626,7 @@ mal_bool32 mal_thread_create__posix(mal_context* pContext, mal_thread* pThread,
 
         ((mal_pthread_attr_destroy_proc)pContext->posix.pthread_attr_destroy)(&attr);
     }
+#endif
 
     int result = ((mal_pthread_create_proc)pContext->posix.pthread_create)(&pThread->posix.thread, pAttr, entryProc, pData);
     if (result != 0) {
@@ -3645,6 +3891,34 @@ mal_uint32 mal_get_best_sample_rate_within_range(mal_uint32 sampleRateMin, mal_u
     return 0;
 }
 
+mal_uint32 mal_get_closest_standard_sample_rate(mal_uint32 sampleRateIn)
+{
+    mal_uint32 closestRate = 0;
+    mal_uint32 closestDiff = 0xFFFFFFFF;
+
+    for (size_t iStandardRate = 0; iStandardRate < mal_countof(g_malStandardSampleRatePriorities); ++iStandardRate) {
+        mal_uint32 standardRate = g_malStandardSampleRatePriorities[iStandardRate];
+
+        mal_uint32 diff;
+        if (sampleRateIn > standardRate) {
+            diff = sampleRateIn - standardRate;
+        } else {
+            diff = standardRate - sampleRateIn;
+        }
+
+        if (diff == 0) {
+            return standardRate;    // The input sample rate is a standard rate.
+        }
+
+        if (closestDiff > diff) {
+            closestDiff = diff;
+            closestRate = standardRate;
+        }
+    }
+
+    return closestRate;
+}
+
 
 // Posts a log message.
 void mal_log(mal_context* pContext, mal_device* pDevice, const char* message)
@@ -3922,6 +4196,17 @@ mal_result mal_context_get_device_info__null(mal_context* pContext, mal_device_t
         mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1);
     }
 
+    // Support everything on the null backend.
+    pDeviceInfo->formatCount = mal_format_count - 1;    // Minus one because we don't want to include mal_format_unknown.
+    for (mal_uint32 iFormat = 0; iFormat < pDeviceInfo->formatCount; ++iFormat) {
+        pDeviceInfo->formats[iFormat] = (mal_format)(iFormat + 1);  // +1 to skip over mal_format_unknown.
+    }
+
+    pDeviceInfo->minChannels   = 1;
+    pDeviceInfo->maxChannels   = MAL_MAX_CHANNELS;
+    pDeviceInfo->minSampleRate = MAL_SAMPLE_RATE_8000;
+    pDeviceInfo->maxSampleRate = MAL_SAMPLE_RATE_384000;
+
     return MAL_SUCCESS;
 }
 
@@ -4188,7 +4473,7 @@ typedef size_t DWORD_PTR;
 
 // The SDK that comes with old versions of MSVC (VC6, for example) does not appear to define WAVEFORMATEXTENSIBLE. We
 // define our own implementation in this case.
-#if defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_)
+#if (defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_)) || defined(__DMC__)
 typedef struct
 {
     WAVEFORMATEX Format;
@@ -4204,7 +4489,11 @@ typedef struct
 #endif
 
 #ifndef WAVE_FORMAT_EXTENSIBLE
-#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
+#define WAVE_FORMAT_EXTENSIBLE  0xFFFE
+#endif
+
+#ifndef WAVE_FORMAT_IEEE_FLOAT
+#define WAVE_FORMAT_IEEE_FLOAT  0x0003
 #endif
 
 GUID MAL_GUID_NULL = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
@@ -4307,12 +4596,12 @@ void mal_channel_mask_to_channel_map__win32(DWORD dwChannelMask, mal_uint32 chan
 #define mal_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b)
 #endif
 
-mal_format mal_format_from_WAVEFORMATEX(WAVEFORMATEX* pWF)
+mal_format mal_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF)
 {
     mal_assert(pWF != NULL);
 
     if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
-        WAVEFORMATEXTENSIBLE* pWFEX = (WAVEFORMATEXTENSIBLE*)pWF;
+        const WAVEFORMATEXTENSIBLE* pWFEX = (const WAVEFORMATEXTENSIBLE*)pWF;
         if (mal_is_guid_equal(&pWFEX->SubFormat, &MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM)) {
             if (pWFEX->Samples.wValidBitsPerSample == 32) {
                 return mal_format_s32;
@@ -4386,6 +4675,36 @@ mal_format mal_format_from_WAVEFORMATEX(WAVEFORMATEX* pWF)
 //    #pragma warning(pop)
 //#endif
 
+
+// Some compilers don't define VerifyVersionInfoW. Need to write this ourselves.
+#if defined(__DMC__)
+#define _WIN32_WINNT_VISTA      0x0600
+#define VER_MINORVERSION        0x01
+#define VER_MAJORVERSION        0x02
+#define VER_SERVICEPACKMAJOR    0x20
+#define VER_GREATER_EQUAL       0x03
+
+typedef struct  {
+    DWORD dwOSVersionInfoSize;
+    DWORD dwMajorVersion;
+    DWORD dwMinorVersion;
+    DWORD dwBuildNumber;
+    DWORD dwPlatformId;
+    WCHAR szCSDVersion[128];
+    WORD  wServicePackMajor;
+    WORD  wServicePackMinor;
+    WORD  wSuiteMask;
+    BYTE  wProductType;
+    BYTE  wReserved;
+} mal_OSVERSIONINFOEXW;
+
+BOOL WINAPI VerifyVersionInfoW(mal_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
+ULONGLONG WINAPI VerSetConditionMask(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask);
+#else
+typedef OSVERSIONINFOEXW mal_OSVERSIONINFOEXW;
+#endif
+
+
 #ifndef PROPERTYKEY_DEFINED
 #define PROPERTYKEY_DEFINED
 typedef struct
@@ -4454,6 +4773,7 @@ typedef mal_int64                                           MAL_REFERENCE_TIME;
 #define MAL_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED    0x40000000
 
 // We only care about a few error codes.
+#define MAL_AUDCLNT_E_INVALID_DEVICE_PERIOD                 (-2004287456)
 #define MAL_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED               (-2004287463)
 #define MAL_AUDCLNT_S_BUFFER_EMPTY                          (143196161)
 
@@ -4617,9 +4937,9 @@ typedef struct
 
     // IAudioClient
     HRESULT (STDMETHODCALLTYPE * Initialize)       (mal_IAudioClient* pThis, MAL_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MAL_REFERENCE_TIME bufferDuration, MAL_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
-    HRESULT (STDMETHODCALLTYPE * GetBufferSize)    (mal_IAudioClient* pThis, UINT32* pNumBufferFrames);
+    HRESULT (STDMETHODCALLTYPE * GetBufferSize)    (mal_IAudioClient* pThis, mal_uint32* pNumBufferFrames);
     HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (mal_IAudioClient* pThis, MAL_REFERENCE_TIME* pLatency);
-    HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(mal_IAudioClient* pThis, UINT32* pNumPaddingFrames);
+    HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(mal_IAudioClient* pThis, mal_uint32* pNumPaddingFrames);
     HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(mal_IAudioClient* pThis, MAL_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
     HRESULT (STDMETHODCALLTYPE * GetMixFormat)     (mal_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat);
     HRESULT (STDMETHODCALLTYPE * GetDevicePeriod)  (mal_IAudioClient* pThis, MAL_REFERENCE_TIME* pDefaultDevicePeriod, MAL_REFERENCE_TIME* pMinimumDevicePeriod);
@@ -4637,9 +4957,9 @@ HRESULT mal_IAudioClient_QueryInterface(mal_IAudioClient* pThis, const IID* cons
 ULONG   mal_IAudioClient_AddRef(mal_IAudioClient* pThis)                                                 { return pThis->lpVtbl->AddRef(pThis); }
 ULONG   mal_IAudioClient_Release(mal_IAudioClient* pThis)                                                { return pThis->lpVtbl->Release(pThis); }
 HRESULT mal_IAudioClient_Initialize(mal_IAudioClient* pThis, MAL_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MAL_REFERENCE_TIME bufferDuration, MAL_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }
-HRESULT mal_IAudioClient_GetBufferSize(mal_IAudioClient* pThis, UINT32* pNumBufferFrames)                { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
+HRESULT mal_IAudioClient_GetBufferSize(mal_IAudioClient* pThis, mal_uint32* pNumBufferFrames)                { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
 HRESULT mal_IAudioClient_GetStreamLatency(mal_IAudioClient* pThis, MAL_REFERENCE_TIME* pLatency)             { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
-HRESULT mal_IAudioClient_GetCurrentPadding(mal_IAudioClient* pThis, UINT32* pNumPaddingFrames)           { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
+HRESULT mal_IAudioClient_GetCurrentPadding(mal_IAudioClient* pThis, mal_uint32* pNumPaddingFrames)           { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
 HRESULT mal_IAudioClient_IsFormatSupported(mal_IAudioClient* pThis, MAL_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }
 HRESULT mal_IAudioClient_GetMixFormat(mal_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat)            { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
 HRESULT mal_IAudioClient_GetDevicePeriod(mal_IAudioClient* pThis, MAL_REFERENCE_TIME* pDefaultDevicePeriod, MAL_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
@@ -4659,8 +4979,8 @@ typedef struct
     ULONG   (STDMETHODCALLTYPE * Release)       (mal_IAudioRenderClient* pThis);
 
     // IAudioRenderClient
-    HRESULT (STDMETHODCALLTYPE * GetBuffer)    (mal_IAudioRenderClient* pThis, UINT32 numFramesRequested, BYTE** ppData);
-    HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(mal_IAudioRenderClient* pThis, UINT32 numFramesWritten, DWORD dwFlags);
+    HRESULT (STDMETHODCALLTYPE * GetBuffer)    (mal_IAudioRenderClient* pThis, mal_uint32 numFramesRequested, BYTE** ppData);
+    HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(mal_IAudioRenderClient* pThis, mal_uint32 numFramesWritten, DWORD dwFlags);
 } mal_IAudioRenderClientVtbl;
 struct mal_IAudioRenderClient
 {
@@ -4669,8 +4989,8 @@ struct mal_IAudioRenderClient
 HRESULT mal_IAudioRenderClient_QueryInterface(mal_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
 ULONG   mal_IAudioRenderClient_AddRef(mal_IAudioRenderClient* pThis)                                                 { return pThis->lpVtbl->AddRef(pThis); }
 ULONG   mal_IAudioRenderClient_Release(mal_IAudioRenderClient* pThis)                                                { return pThis->lpVtbl->Release(pThis); }
-HRESULT mal_IAudioRenderClient_GetBuffer(mal_IAudioRenderClient* pThis, UINT32 numFramesRequested, BYTE** ppData)    { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); }
-HRESULT mal_IAudioRenderClient_ReleaseBuffer(mal_IAudioRenderClient* pThis, UINT32 numFramesWritten, DWORD dwFlags)  { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); }
+HRESULT mal_IAudioRenderClient_GetBuffer(mal_IAudioRenderClient* pThis, mal_uint32 numFramesRequested, BYTE** ppData)    { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); }
+HRESULT mal_IAudioRenderClient_ReleaseBuffer(mal_IAudioRenderClient* pThis, mal_uint32 numFramesWritten, DWORD dwFlags)  { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); }
 
 
 // IAudioCaptureClient
@@ -4682,9 +5002,9 @@ typedef struct
     ULONG   (STDMETHODCALLTYPE * Release)       (mal_IAudioCaptureClient* pThis);
 
     // IAudioRenderClient
-    HRESULT (STDMETHODCALLTYPE * GetBuffer)        (mal_IAudioCaptureClient* pThis, BYTE** ppData, UINT32* pNumFramesToRead, DWORD* pFlags, UINT64* pDevicePosition, UINT64* pQPCPosition);
-    HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)    (mal_IAudioCaptureClient* pThis, UINT32 numFramesRead);
-    HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(mal_IAudioCaptureClient* pThis, UINT32* pNumFramesInNextPacket);
+    HRESULT (STDMETHODCALLTYPE * GetBuffer)        (mal_IAudioCaptureClient* pThis, BYTE** ppData, mal_uint32* pNumFramesToRead, DWORD* pFlags, mal_uint64* pDevicePosition, mal_uint64* pQPCPosition);
+    HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)    (mal_IAudioCaptureClient* pThis, mal_uint32 numFramesRead);
+    HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(mal_IAudioCaptureClient* pThis, mal_uint32* pNumFramesInNextPacket);
 } mal_IAudioCaptureClientVtbl;
 struct mal_IAudioCaptureClient
 {
@@ -4693,9 +5013,64 @@ struct mal_IAudioCaptureClient
 HRESULT mal_IAudioCaptureClient_QueryInterface(mal_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
 ULONG   mal_IAudioCaptureClient_AddRef(mal_IAudioCaptureClient* pThis)                                                 { return pThis->lpVtbl->AddRef(pThis); }
 ULONG   mal_IAudioCaptureClient_Release(mal_IAudioCaptureClient* pThis)                                                { return pThis->lpVtbl->Release(pThis); }
-HRESULT mal_IAudioCaptureClient_GetBuffer(mal_IAudioCaptureClient* pThis, BYTE** ppData, UINT32* pNumFramesToRead, DWORD* pFlags, UINT64* pDevicePosition, UINT64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); }
-HRESULT mal_IAudioCaptureClient_ReleaseBuffer(mal_IAudioCaptureClient* pThis, UINT32 numFramesRead)                    { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); }
-HRESULT mal_IAudioCaptureClient_GetNextPacketSize(mal_IAudioCaptureClient* pThis, UINT32* pNumFramesInNextPacket)      { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); }
+HRESULT mal_IAudioCaptureClient_GetBuffer(mal_IAudioCaptureClient* pThis, BYTE** ppData, mal_uint32* pNumFramesToRead, DWORD* pFlags, mal_uint64* pDevicePosition, mal_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); }
+HRESULT mal_IAudioCaptureClient_ReleaseBuffer(mal_IAudioCaptureClient* pThis, mal_uint32 numFramesRead)                    { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); }
+HRESULT mal_IAudioCaptureClient_GetNextPacketSize(mal_IAudioCaptureClient* pThis, mal_uint32* pNumFramesInNextPacket)      { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); }
+
+// This is the part that's preventing mini_al from being compiled as C with UWP. We need to implement IActivateAudioInterfaceCompletionHandler
+// in C which is quite annoying.
+#ifndef MAL_WIN32_DESKTOP
+    #ifdef __cplusplus
+    #include <mmdeviceapi.h>
+    #include <wrl\implements.h>
+
+    class malCompletionHandler : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::ClassicCom >, Microsoft::WRL::FtmBase, IActivateAudioInterfaceCompletionHandler >
+    {
+    public:
+
+        malCompletionHandler()
+            : m_hEvent(NULL)
+        {
+        }
+
+        mal_result Init()
+        {
+            m_hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
+            if (m_hEvent == NULL) {
+                return MAL_ERROR;
+            }
+
+            return MAL_SUCCESS;
+        }
+
+        void Uninit()
+        {
+            if (m_hEvent != NULL) {
+                CloseHandle(m_hEvent);
+            }
+        }
+
+        void Wait()
+        {
+            WaitForSingleObject(m_hEvent, INFINITE);
+        }
+
+        HRESULT STDMETHODCALLTYPE ActivateCompleted(IActivateAudioInterfaceAsyncOperation *activateOperation)
+        {
+            (void)activateOperation;
+            SetEvent(m_hEvent);
+            return S_OK;
+        }
+
+    private:
+        HANDLE m_hEvent;  // This is created in Init(), deleted in Uninit(), waited on in Wait() and signaled in ActivateCompleted().
+    };
+    #else
+    #error "The UWP build is currently only supported in C++."
+    #endif
+#endif  // !MAL_WIN32_DESKTOP
+
+
 
 mal_bool32 mal_context_is_device_id_equal__wasapi(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1)
 {
@@ -4707,10 +5082,231 @@ mal_bool32 mal_context_is_device_id_equal__wasapi(mal_context* pContext, const m
     return memcmp(pID0->wasapi, pID1->wasapi, sizeof(pID0->wasapi)) == 0;
 }
 
-mal_result mal_context_get_device_info_from_MMDevice__wasapi(mal_context* pContext, mal_IMMDevice* pMMDevice, mal_share_mode shareMode, mal_bool32 onlySimpleInfo, mal_device_info* pInfo)
+void mal_set_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, mal_device_info* pInfo)
 {
-    mal_assert(pContext != NULL);
-    mal_assert(pMMDevice != NULL);
+    mal_assert(pWF != NULL);
+    mal_assert(pInfo != NULL);
+
+    pInfo->formatCount   = 1;
+    pInfo->formats[0]    = mal_format_from_WAVEFORMATEX(pWF);
+    pInfo->minChannels   = pWF->nChannels;
+    pInfo->maxChannels   = pWF->nChannels;
+    pInfo->minSampleRate = pWF->nSamplesPerSec;
+    pInfo->maxSampleRate = pWF->nSamplesPerSec;
+}
+
+#ifndef MAL_WIN32_DESKTOP
+mal_result mal_context_get_IAudioClient_UWP__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_IAudioClient** ppAudioClient, IUnknown** ppActivatedInterface)
+{
+    mal_assert(pContext != NULL);
+    mal_assert(ppAudioClient != NULL);
+
+    mal_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
+    malCompletionHandler completionHandler;
+
+    IID iid;
+    if (pDeviceID != NULL) {
+        mal_copy_memory(&iid, pDeviceID->wasapi, sizeof(iid));
+    } else {
+        if (deviceType == mal_device_type_playback) {
+            iid = MAL_IID_DEVINTERFACE_AUDIO_RENDER;
+        } else {
+            iid = MAL_IID_DEVINTERFACE_AUDIO_CAPTURE;
+        }
+    }
+
+    LPOLESTR iidStr;
+    HRESULT hr = StringFromIID(iid, &iidStr);
+    if (FAILED(hr)) {
+        return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.", MAL_OUT_OF_MEMORY);
+    }
+
+    mal_result result = completionHandler.Init();
+    if (result != MAL_SUCCESS) {
+        mal_CoTaskMemFree(pContext, iidStr);
+        return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+    }
+
+    hr = ActivateAudioInterfaceAsync(iidStr, MAL_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
+    if (FAILED(hr)) {
+        completionHandler.Uninit();
+        mal_CoTaskMemFree(pContext, iidStr);
+        return mal_context_post_error(pContext, NULL, "[WASAPI] ActivateAudioInterfaceAsync() failed.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+    }
+
+    mal_CoTaskMemFree(pContext, iidStr);
+
+    // Wait for the async operation for finish.
+    completionHandler.Wait();
+    completionHandler.Uninit();
+
+    HRESULT activateResult;
+    IUnknown* pActivatedInterface;
+    hr = mal_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);
+    mal_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
+
+    if (FAILED(hr) || FAILED(activateResult)) {
+        return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to activate device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+    }
+
+    // Here is where we grab the IAudioClient interface.
+    hr = pActivatedInterface->QueryInterface(MAL_IID_IAudioClient, (void**)ppAudioClient);
+    if (FAILED(hr)) {
+        return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to query IAudioClient interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+    }
+
+    if (ppActivatedInterface) {
+        *ppActivatedInterface = pActivatedInterface;
+    } else {
+        pActivatedInterface->Release();
+    }
+
+    return MAL_SUCCESS;
+}
+#endif
+
+mal_result mal_context_get_device_info_from_IAudioClient__wasapi(mal_context* pContext, /*mal_IMMDevice**/void* pMMDevice, mal_IAudioClient* pAudioClient, mal_share_mode shareMode, mal_device_info* pInfo)
+{
+    mal_assert(pAudioClient != NULL);
+    mal_assert(pInfo != NULL);
+
+    // We use a different technique to retrieve the device information depending on whether or not we are using shared or exclusive mode.
+    if (shareMode == mal_share_mode_shared) {
+        // Shared Mode. We use GetMixFormat() here.
+        WAVEFORMATEX* pWF = NULL;
+        HRESULT hr = mal_IAudioClient_GetMixFormat((mal_IAudioClient*)pAudioClient, (WAVEFORMATEX**)&pWF);
+        if (SUCCEEDED(hr)) {
+            mal_set_device_info_from_WAVEFORMATEX(pWF, pInfo);
+            return MAL_SUCCESS;
+        } else {
+            return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to retrieve mix format for device info retrieval.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+        }
+    } else {
+        // Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently support on UWP.
+#ifdef MAL_WIN32_DESKTOP
+        // The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is
+        // correct which will simplify our searching.
+        mal_IPropertyStore *pProperties;
+        HRESULT hr = mal_IMMDevice_OpenPropertyStore((mal_IMMDevice*)pMMDevice, STGM_READ, &pProperties);
+        if (SUCCEEDED(hr)) {
+            PROPVARIANT var;
+            mal_PropVariantInit(&var);
+
+            hr = mal_IPropertyStore_GetValue(pProperties, &MAL_PKEY_AudioEngine_DeviceFormat, &var);
+            if (SUCCEEDED(hr)) {
+                WAVEFORMATEX* pWF = (WAVEFORMATEX*)var.blob.pBlobData;
+                mal_set_device_info_from_WAVEFORMATEX(pWF, pInfo);
+
+                // In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format
+                // first. If this fails, fall back to a search.
+                hr = mal_IAudioClient_IsFormatSupported((mal_IAudioClient*)pAudioClient, MAL_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL);
+                mal_PropVariantClear(pContext, &var);
+
+                if (FAILED(hr)) {
+                    // The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel
+                    // count returned by MAL_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format.
+                    mal_uint32 channels = pInfo->minChannels;
+
+                    mal_format formatsToSearch[] = {
+                        mal_format_s16,
+                        mal_format_s24,
+                        //mal_format_s24_32,
+                        mal_format_f32,
+                        mal_format_s32,
+                        mal_format_u8
+                    };
+
+                    mal_channel defaultChannelMap[MAL_MAX_CHANNELS];
+                    mal_get_standard_channel_map(mal_standard_channel_map_microsoft, channels, defaultChannelMap);
+
+                    WAVEFORMATEXTENSIBLE wf;
+                    mal_zero_object(&wf);
+                    wf.Format.cbSize     = sizeof(wf);
+                    wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+                    wf.Format.nChannels  = (WORD)channels;
+                    wf.dwChannelMask     = mal_channel_map_to_channel_mask__win32(defaultChannelMap, channels);
+
+                    mal_bool32 found = MAL_FALSE;
+                    for (mal_uint32 iFormat = 0; iFormat < mal_countof(formatsToSearch); ++iFormat) {
+                        mal_format format = formatsToSearch[iFormat];
+
+                        wf.Format.wBitsPerSample       = (WORD)mal_get_bytes_per_sample(format)*8;
+                        wf.Format.nBlockAlign          = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
+                        wf.Format.nAvgBytesPerSec      = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
+                        wf.Samples.wValidBitsPerSample = /*(format == mal_format_s24_32) ? 24 :*/ wf.Format.wBitsPerSample;
+                        if (format == mal_format_f32) {
+                            wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+                        } else {
+                            wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM;
+                        }
+
+                        for (mal_uint32 iSampleRate = 0; iSampleRate < mal_countof(g_malStandardSampleRatePriorities); ++iSampleRate) {
+                            wf.Format.nSamplesPerSec = g_malStandardSampleRatePriorities[iSampleRate];
+
+                            hr = mal_IAudioClient_IsFormatSupported((mal_IAudioClient*)pAudioClient, MAL_AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL);
+                            if (SUCCEEDED(hr)) {
+                                mal_set_device_info_from_WAVEFORMATEX((WAVEFORMATEX*)&wf, pInfo);
+                                found = MAL_TRUE;
+                                break;
+                            }
+                        }
+
+                        if (found) {
+                            break;
+                        }
+                    }
+
+                    if (!found) {
+                        mal_IPropertyStore_Release(pProperties);
+                        return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to find suitable device format for device info retrieval.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+                    }
+                }
+            } else {
+                mal_IPropertyStore_Release(pProperties);
+                return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to retrieve device format for device info retrieval.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+            }
+        } else {
+            return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to open property store for device info retrieval.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+        }
+
+        return MAL_SUCCESS;
+#else
+        // Exclusive mode not fully supported in UWP right now.
+        return MAL_ERROR;
+#endif
+    }
+}
+
+#ifdef MAL_WIN32_DESKTOP
+mal_result mal_context_get_MMDevice__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_IMMDevice** ppMMDevice)
+{
+    mal_assert(pContext != NULL);
+    mal_assert(ppMMDevice != NULL);
+
+    mal_IMMDeviceEnumerator* pDeviceEnumerator;
+    HRESULT hr = mal_CoCreateInstance(pContext, MAL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MAL_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
+    if (FAILED(hr)) {
+        return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to create IMMDeviceEnumerator.", MAL_FAILED_TO_INIT_BACKEND);
+    }
+
+    if (pDeviceID == NULL) {
+        hr = mal_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == mal_device_type_playback) ? mal_eRender : mal_eCapture, mal_eConsole, ppMMDevice);
+    } else {
+        hr = mal_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice);
+    }
+
+    mal_IMMDeviceEnumerator_Release(pDeviceEnumerator);
+    if (FAILED(hr)) {
+        return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to retrieve IMMDevice.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+    }
+
+    return MAL_SUCCESS;
+}
+
+mal_result mal_context_get_device_info_from_MMDevice__wasapi(mal_context* pContext, mal_IMMDevice* pMMDevice, mal_share_mode shareMode, mal_bool32 onlySimpleInfo, mal_device_info* pInfo)
+{
+    mal_assert(pContext != NULL);
+    mal_assert(pMMDevice != NULL);
     mal_assert(pInfo != NULL);
 
     // ID.
@@ -4730,31 +5326,36 @@ mal_result mal_context_get_device_info_from_MMDevice__wasapi(mal_context* pConte
         mal_CoTaskMemFree(pContext, id);
     }
 
-    mal_IPropertyStore *pProperties;
-    hr = mal_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties);
-    if (SUCCEEDED(hr)) {
-        PROPVARIANT var;
-
-        // Description / Friendly Name
-        mal_PropVariantInit(&var);
-        hr = mal_IPropertyStore_GetValue(pProperties, &MAL_PKEY_Device_FriendlyName, &var);
+    {
+        mal_IPropertyStore *pProperties;
+        hr = mal_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties);
         if (SUCCEEDED(hr)) {
-            WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);
-            mal_PropVariantClear(pContext, &var);
-        }
+            PROPVARIANT var;
 
-        // Format
-        if (!onlySimpleInfo) {
-            // TODO:
-            // - Get MAL_PKEY_AudioEngine_DeviceFormat for the channel count
-            // - Open the device
-            // - If shared mode, call GetMixFormat()
-            // - If exclusive mode, loop over most common formats (s16, s24, f32), then each of the standard sample rates.
-            //   - If anything fails, don't allow exclusive mode for this device.
-            (void)shareMode;
+            // Description / Friendly Name
+            mal_PropVariantInit(&var);
+            hr = mal_IPropertyStore_GetValue(pProperties, &MAL_PKEY_Device_FriendlyName, &var);
+            if (SUCCEEDED(hr)) {
+                WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);
+                mal_PropVariantClear(pContext, &var);
+            }
+
+            mal_IPropertyStore_Release(pProperties);
         }
+    }
 
-        mal_IPropertyStore_Release(pProperties);
+    // Format
+    if (!onlySimpleInfo) {
+        mal_IAudioClient* pAudioClient;
+        hr = mal_IMMDevice_Activate(pMMDevice, &MAL_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
+        if (SUCCEEDED(hr)) {
+            mal_result result = mal_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, shareMode, pInfo);
+            
+            mal_IAudioClient_Release(pAudioClient);
+            return result;
+        } else {
+            return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to activate audio client for device info retrieval.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+        }
     }
 
     return MAL_SUCCESS;
@@ -4792,6 +5393,7 @@ mal_result mal_context_enumerate_device_collection__wasapi(mal_context* pContext
 
     return MAL_SUCCESS;
 }
+#endif
 
 mal_result mal_context_enumerate_devices__wasapi(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData)
 {
@@ -4857,28 +5459,41 @@ mal_result mal_context_enumerate_devices__wasapi(mal_context* pContext, mal_enum
 
 mal_result mal_context_get_device_info__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo)
 {
-    mal_IMMDeviceEnumerator* pDeviceEnumerator;
-    HRESULT hr = mal_CoCreateInstance(pContext, MAL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MAL_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
-    if (FAILED(hr)) {
-        return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to create IMMDeviceEnumerator.", MAL_FAILED_TO_INIT_BACKEND);
+#ifdef MAL_WIN32_DESKTOP
+    mal_IMMDevice* pMMDevice = NULL;
+    mal_result result = mal_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice);
+    if (result != MAL_SUCCESS) {
+        return result;
     }
 
-    mal_IMMDevice* pMMDevice = NULL;
-    if (pDeviceID == NULL) {
-        hr = mal_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == mal_device_type_playback) ? mal_eRender : mal_eCapture, mal_eConsole, &pMMDevice);
+    result = mal_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, shareMode, MAL_FALSE, pDeviceInfo);   // MAL_FALSE = !onlySimpleInfo.
+
+    mal_IMMDevice_Release(pMMDevice);
+    return result;
+#else
+    // UWP currently only uses default devices.
+    if (deviceType == mal_device_type_playback) {
+        mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
     } else {
-        hr = mal_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, &pMMDevice);
+        mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
     }
 
-    mal_IMMDeviceEnumerator_Release(pDeviceEnumerator);
-    if (FAILED(hr)) {
-        return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to retrieve IMMDevice.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+    // Not currently supporting exclusive mode on UWP.
+    if (shareMode == mal_share_mode_exclusive) {
+        return MAL_ERROR;
     }
 
-    mal_result result = mal_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, shareMode, MAL_FALSE, pDeviceInfo);   // MAL_FALSE = !onlySimpleInfo.
+    mal_IAudioClient* pAudioClient;
+    mal_result result = mal_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, &pAudioClient, NULL);
+    if (result != MAL_SUCCESS) {
+        return result;
+    }
 
-    mal_IMMDevice_Release(pMMDevice);
+    result = mal_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, shareMode, pDeviceInfo);
+
+    mal_IAudioClient_Release(pAudioClient);
     return result;
+#endif
 }
 
 
@@ -4892,7 +5507,7 @@ mal_result mal_context_init__wasapi(mal_context* pContext)
 #ifdef MAL_WIN32_DESKTOP
     // WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven
     // exclusive mode does not work until SP1.
-    OSVERSIONINFOEXW osvi;
+    mal_OSVERSIONINFOEXW osvi;
     mal_zero_object(&osvi);
     osvi.dwOSVersionInfoSize = sizeof(osvi);
     osvi.dwMajorVersion = HIBYTE(_WIN32_WINNT_VISTA);
@@ -4947,59 +5562,6 @@ void mal_device_uninit__wasapi(mal_device* pDevice)
     }
 }
 
-// This is the part that's preventing mini_al from being compiled as C with UWP. We need to implement IActivateAudioInterfaceCompletionHandler
-// in C which is quite annoying.
-#ifndef MAL_WIN32_DESKTOP
-    #ifdef __cplusplus
-    #include <mmdeviceapi.h>
-    #include <wrl\implements.h>
-
-    class malCompletionHandler : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::ClassicCom >, Microsoft::WRL::FtmBase, IActivateAudioInterfaceCompletionHandler >
-    {
-    public:
-
-        malCompletionHandler()
-            : m_hEvent(NULL)
-        {
-        }
-
-        mal_result Init()
-        {
-            m_hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
-            if (m_hEvent == NULL) {
-                return MAL_ERROR;
-            }
-
-            return MAL_SUCCESS;
-        }
-
-        void Uninit()
-        {
-            if (m_hEvent != NULL) {
-                CloseHandle(m_hEvent);
-            }
-        }
-
-        void Wait()
-        {
-            WaitForSingleObject(m_hEvent, INFINITE);
-        }
-
-        HRESULT STDMETHODCALLTYPE ActivateCompleted(IActivateAudioInterfaceAsyncOperation *activateOperation)
-        {
-            (void)activateOperation;
-            SetEvent(m_hEvent);
-            return S_OK;
-        }
-
-    private:
-        HANDLE m_hEvent;  // This is created in Init(), deleted in Uninit(), waited on in Wait() and signaled in ActivateCompleted().
-    };
-    #else
-    #error "The UWP build is currently only supported in C++."
-    #endif
-#endif  // !MAL_WIN32_DESKTOP
-
 mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice)
 {
     (void)pContext;
@@ -5011,95 +5573,26 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type,
     mal_result result = MAL_SUCCESS;
     const char* errorMsg = "";
     MAL_AUDCLNT_SHAREMODE shareMode = MAL_AUDCLNT_SHAREMODE_SHARED;
-    MAL_REFERENCE_TIME bufferDurationInMicroseconds = ((mal_uint64)pDevice->bufferSizeInFrames * 1000 * 1000) / pConfig->sampleRate;
     WAVEFORMATEXTENSIBLE* pBestFormatTemp = NULL;
+    MAL_REFERENCE_TIME bufferDurationInMicroseconds;
+    
 
 #ifdef MAL_WIN32_DESKTOP
     mal_IMMDevice* pMMDevice = NULL;
-
-    mal_IMMDeviceEnumerator* pDeviceEnumerator;
-    hr = mal_CoCreateInstance(pContext, MAL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MAL_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
-    if (FAILED(hr)) {
-        errorMsg = "[WASAPI] Failed to create IMMDeviceEnumerator.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
-        goto done;
-    }
-
-    if (pDeviceID == NULL) {
-        hr = mal_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (type == mal_device_type_playback) ? mal_eRender : mal_eCapture, mal_eConsole, &pMMDevice);
-    } else {
-        hr = mal_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, &pMMDevice);
-    }
-
-    if (FAILED(hr)) {
-        mal_IMMDeviceEnumerator_Release(pDeviceEnumerator);
-        errorMsg = "[WASAPI] Failed to create backend device.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
+    result = mal_context_get_MMDevice__wasapi(pContext, type, pDeviceID, &pMMDevice);
+    if (result != MAL_SUCCESS) {
         goto done;
     }
 
-    mal_IMMDeviceEnumerator_Release(pDeviceEnumerator);
-
     hr = mal_IMMDevice_Activate(pMMDevice, &MAL_IID_IAudioClient, CLSCTX_ALL, NULL, &pDevice->wasapi.pAudioClient);
     if (FAILED(hr)) {
         errorMsg = "[WASAPI] Failed to activate device.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
         goto done;
     }
 #else
-    mal_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
-    malCompletionHandler completionHandler;
-
-    IID iid;
-    if (pDeviceID != NULL) {
-        mal_copy_memory(&iid, pDeviceID->wasapi, sizeof(iid));
-    } else {
-        if (type == mal_device_type_playback) {
-            iid = MAL_IID_DEVINTERFACE_AUDIO_RENDER;
-        } else {
-            iid = MAL_IID_DEVINTERFACE_AUDIO_CAPTURE;
-        }
-    }
-
-    LPOLESTR iidStr;
-    hr = StringFromIID(iid, &iidStr);
-    if (FAILED(hr)) {
-        errorMsg = "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.", result = MAL_OUT_OF_MEMORY;
-        goto done;
-    }
-
-    result = completionHandler.Init();
+    IUnknown* pActivatedInterface = NULL;
+    result = mal_context_get_IAudioClient_UWP__wasapi(pContext, type, pDeviceID, (mal_IAudioClient**)&pDevice->wasapi.pAudioClient, &pActivatedInterface);
     if (result != MAL_SUCCESS) {
-        mal_CoTaskMemFree(pContext, iidStr);
-
-        errorMsg = "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
-        goto done;
-    }
-
-    hr = ActivateAudioInterfaceAsync(iidStr, MAL_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
-    if (FAILED(hr)) {
-        completionHandler.Uninit();
-        mal_CoTaskMemFree(pContext, iidStr);
-
-        errorMsg = "[WASAPI] ActivateAudioInterfaceAsync() failed.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
-        goto done;
-    }
-
-    mal_CoTaskMemFree(pContext, iidStr);
-
-    // Wait for the async operation for finish.
-    completionHandler.Wait();
-    completionHandler.Uninit();
-
-    HRESULT activateResult;
-    IUnknown* pActivatedInterface;
-    hr = mal_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);
-    if (FAILED(hr) || FAILED(activateResult)) {
-        errorMsg = "[WASAPI] Failed to activate device.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
-        goto done;
-    }
-
-    // Here is where we grab the IAudioClient interface.
-    hr = pActivatedInterface->QueryInterface(MAL_IID_IAudioClient, &pDevice->wasapi.pAudioClient);
-    if (FAILED(hr)) {
-        errorMsg = "[WASAPI] Failed to query IAudioClient interface.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
         goto done;
     }
 #endif
@@ -5216,24 +5709,61 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type,
     // Get the internal channel map based on the channel mask.
     mal_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDevice->internalChannels, pDevice->internalChannelMap);
 
-    // Slightly different initialization for shared and exclusive modes.
-    if (shareMode == MAL_AUDCLNT_SHAREMODE_SHARED) {
-        // Shared.
+    // If we're using a default buffer size we need to calculate it based on the efficiency of the system.
+    if (pDevice->usingDefaultBufferSize) {
+        // CPU speed is a factor to consider when determine how large of a buffer we need.
+        float fCPUSpeed = mal_calculate_cpu_speed_factor();
+
+        // We need a slightly bigger buffer if we're using shared mode to cover the inherent tax association with shared mode.
+        float fShareMode;
+        if (pConfig->shareMode == mal_share_mode_shared) {
+            fShareMode = 1.0f;
+        } else {
+            fShareMode = 0.8f;
+        }
+
+        // In my testing, capture seems to have worse latency than playback for some reason.
+        float fType;
+        if (type == mal_device_type_playback) {
+            fType = 1.0f;
+        } else {
+            fType = 2.0f;
+        }
+
+        // Backend tax. Need to fiddle with this.
+        float fBackend = 1.0;
+
+        pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fShareMode*fType*fBackend);
+    }
+
+    bufferDurationInMicroseconds = ((mal_uint64)pDevice->bufferSizeInFrames * 1000 * 1000) / pConfig->sampleRate;
+
+    // Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode.
+    if (shareMode == MAL_AUDCLNT_SHAREMODE_EXCLUSIVE) {
+        // Exclusive.
         MAL_REFERENCE_TIME bufferDuration = bufferDurationInMicroseconds*10;
-        hr = mal_IAudioClient_Initialize((mal_IAudioClient*)pDevice->wasapi.pAudioClient, shareMode, MAL_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL);
-        if (FAILED(hr)) {
-            if (hr == E_ACCESSDENIED) {
-                errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MAL_ACCESS_DENIED;
+
+        // If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing
+        // it and trying it again.
+        hr = E_FAIL;
+        for (;;) {
+            hr = mal_IAudioClient_Initialize((mal_IAudioClient*)pDevice->wasapi.pAudioClient, shareMode, MAL_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
+            if (hr == MAL_AUDCLNT_E_INVALID_DEVICE_PERIOD) {
+                if (bufferDuration > 500*10000) {
+                    break;
+                } else {
+                    if (bufferDuration == 0) {  // <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better.
+                        break;
+                    }
+
+                    bufferDuration = bufferDuration * 2;
+                    continue;
+                }
             } else {
-                errorMsg = "[WASAPI] Failed to initialize device.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
+                break;
             }
-
-            goto done;
         }
-    } else {
-        // Exclusive.
-        MAL_REFERENCE_TIME bufferDuration = bufferDurationInMicroseconds*10;
-        hr = mal_IAudioClient_Initialize((mal_IAudioClient*)pDevice->wasapi.pAudioClient, shareMode, MAL_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
+        
         if (hr == MAL_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
             UINT bufferSizeInFrames;
             hr = mal_IAudioClient_GetBufferSize((mal_IAudioClient*)pDevice->wasapi.pAudioClient, &bufferSizeInFrames);
@@ -5256,7 +5786,25 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type,
         }
 
         if (FAILED(hr)) {
-            errorMsg = "[WASAPI] Failed to initialize device.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
+            // Failed to initialize in exclusive mode. We don't return an error here, but instead fall back to shared mode.
+            shareMode = MAL_AUDCLNT_SHAREMODE_SHARED;
+
+            //errorMsg = "[WASAPI] Failed to initialize device.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
+            //goto done;
+        }
+    }
+
+    if (shareMode == MAL_AUDCLNT_SHAREMODE_SHARED) {
+        // Shared.
+        MAL_REFERENCE_TIME bufferDuration = bufferDurationInMicroseconds*10;
+        hr = mal_IAudioClient_Initialize((mal_IAudioClient*)pDevice->wasapi.pAudioClient, shareMode, MAL_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL);
+        if (FAILED(hr)) {
+            if (hr == E_ACCESSDENIED) {
+                errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MAL_ACCESS_DENIED;
+            } else {
+                errorMsg = "[WASAPI] Failed to initialize device.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
+            }
+
             goto done;
         }
     }
@@ -5332,8 +5880,8 @@ done:
         mal_IMMDevice_Release(pMMDevice);
     }
 #else
-    if (pAsyncOp != NULL) {
-        mal_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
+    if (pActivatedInterface != NULL) {
+        pActivatedInterface->Release();
     }
 #endif
 
@@ -5382,6 +5930,12 @@ mal_result mal_device__stop_backend__wasapi(mal_device* pDevice)
         return mal_post_error(pDevice, "[WASAPI] Failed to stop internal device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE);
     }
 
+    // The client needs to be reset or else we won't be able to resume it again.
+    hr = mal_IAudioClient_Reset((mal_IAudioClient*)pDevice->wasapi.pAudioClient);
+    if (FAILED(hr)) {
+        return mal_post_error(pDevice, "[WASAPI] Failed to reset internal device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE);
+    }
+
     return MAL_SUCCESS;
 }
 
@@ -5402,7 +5956,7 @@ mal_uint32 mal_device__get_available_frames__wasapi(mal_device* pDevice)
 
 #if 1
     if (pDevice->type == mal_device_type_playback) {
-        UINT32 paddingFramesCount;
+        mal_uint32 paddingFramesCount;
         HRESULT hr = mal_IAudioClient_GetCurrentPadding((mal_IAudioClient*)pDevice->wasapi.pAudioClient, &paddingFramesCount);
         if (FAILED(hr)) {
             return 0;
@@ -5414,7 +5968,7 @@ mal_uint32 mal_device__get_available_frames__wasapi(mal_device* pDevice)
             return pDevice->bufferSizeInFrames - paddingFramesCount;
         }
     } else {
-        UINT32 framesAvailable;
+        mal_uint32 framesAvailable;
         HRESULT hr = mal_IAudioCaptureClient_GetNextPacketSize((mal_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, &framesAvailable);
         if (FAILED(hr)) {
             return 0;
@@ -5423,7 +5977,7 @@ mal_uint32 mal_device__get_available_frames__wasapi(mal_device* pDevice)
         return framesAvailable;
     }
 #else
-    UINT32 paddingFramesCount;
+    mal_uint32 paddingFramesCount;
     HRESULT hr = mal_IAudioClient_GetCurrentPadding(pDevice->wasapi.pAudioClient, &paddingFramesCount);
     if (FAILED(hr)) {
         return 0;
@@ -5499,10 +6053,10 @@ mal_result mal_device__main_loop__wasapi(mal_device* pDevice)
                 return mal_post_error(pDevice, "[WASAPI] Failed to release internal buffer from playback device in preparation for sending new data to the device.", MAL_FAILED_TO_UNMAP_DEVICE_BUFFER);
             }
         } else {
-            UINT32 framesRemaining = framesAvailable;
+            mal_uint32 framesRemaining = framesAvailable;
             while (framesRemaining > 0) {
                 BYTE* pData;
-                UINT32 framesToSend;
+                mal_uint32 framesToSend;
                 DWORD flags;
                 HRESULT hr = mal_IAudioCaptureClient_GetBuffer((mal_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, &pData, &framesToSend, &flags, NULL, NULL);
                 if (FAILED(hr)) {
@@ -5898,6 +6452,147 @@ void mal_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pCh
 }
 
 
+mal_result mal_context_create_IDirectSound__dsound(mal_context* pContext, mal_share_mode shareMode, const mal_device_id* pDeviceID, mal_IDirectSound** ppDirectSound)
+{
+    mal_assert(pContext != NULL);
+    mal_assert(ppDirectSound != NULL);
+
+    *ppDirectSound = NULL;
+    mal_IDirectSound* pDirectSound = NULL;
+
+    if (FAILED(((mal_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) {
+        return mal_context_post_error(pContext, NULL, "[DirectSound] DirectSoundCreate() failed for playback device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+    }
+
+    // The cooperative level must be set before doing anything else.
+    HWND hWnd = ((MAL_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
+    if (hWnd == NULL) {
+        hWnd = ((MAL_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
+    }
+    if (FAILED(mal_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == mal_share_mode_exclusive) ? MAL_DSSCL_EXCLUSIVE : MAL_DSSCL_PRIORITY))) {
+        return mal_context_post_error(pContext, NULL, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+    }
+
+    *ppDirectSound = pDirectSound;
+    return MAL_SUCCESS;
+}
+
+mal_result mal_context_create_IDirectSoundCapture__dsound(mal_context* pContext, mal_share_mode shareMode, const mal_device_id* pDeviceID, mal_IDirectSoundCapture** ppDirectSoundCapture)
+{
+    mal_assert(pContext != NULL);
+    mal_assert(ppDirectSoundCapture != NULL);
+
+    // Everything is shared in capture mode by the looks of it.
+    (void)shareMode;
+
+    *ppDirectSoundCapture = NULL;
+    mal_IDirectSoundCapture* pDirectSoundCapture = NULL;
+
+    if (FAILED(((mal_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL))) {
+        return mal_context_post_error(pContext, NULL, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+    }
+
+    *ppDirectSoundCapture = pDirectSoundCapture;
+    return MAL_SUCCESS;
+}
+
+mal_result mal_context_get_format_info_for_IDirectSoundCapture__dsound(mal_context* pContext, mal_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate)
+{
+    mal_assert(pContext != NULL);
+    mal_assert(pDirectSoundCapture != NULL);
+
+    if (pChannels) {
+        *pChannels = 0;
+    }
+    if (pBitsPerSample) {
+        *pBitsPerSample = 0;
+    }
+    if (pSampleRate) {
+        *pSampleRate = 0;
+    }
+
+    MAL_DSCCAPS caps;
+    mal_zero_object(&caps);
+    caps.dwSize = sizeof(caps);
+    if (FAILED(mal_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps))) {
+        return mal_context_post_error(pContext, NULL, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+    }
+
+    if (pChannels) {
+        *pChannels = (WORD)caps.dwChannels;
+    }
+
+    // The device can support multiple formats. We just go through the different formats in order of priority and
+    // pick the first one. This the same type of system as the WinMM backend.
+    WORD bitsPerSample = 16;
+    DWORD sampleRate = 48000;
+
+    if (caps.dwChannels == 1) {
+        if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) {
+            sampleRate = 48000;
+        } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) {
+            sampleRate = 44100;
+        } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) {
+            sampleRate = 22050;
+        } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) {
+            sampleRate = 11025;
+        } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) {
+            sampleRate = 96000;
+        } else {
+            bitsPerSample = 8;
+            if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) {
+                sampleRate = 48000;
+            } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) {
+                sampleRate = 44100;
+            } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) {
+                sampleRate = 22050;
+            } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) {
+                sampleRate = 11025;
+            } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) {
+                sampleRate = 96000;
+            } else {
+                bitsPerSample = 16;  // Didn't find it. Just fall back to 16-bit.
+            }
+        }
+    } else if (caps.dwChannels == 2) {
+        if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) {
+            sampleRate = 48000;
+        } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) {
+            sampleRate = 44100;
+        } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) {
+            sampleRate = 22050;
+        } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) {
+            sampleRate = 11025;
+        } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) {
+            sampleRate = 96000;
+        } else {
+            bitsPerSample = 8;
+            if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) {
+                sampleRate = 48000;
+            } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) {
+                sampleRate = 44100;
+            } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) {
+                sampleRate = 22050;
+            } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) {
+                sampleRate = 11025;
+            } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) {
+                sampleRate = 96000;
+            } else {
+                bitsPerSample = 16;  // Didn't find it. Just fall back to 16-bit.
+            }
+        }
+    }
+
+    if (pBitsPerSample) {
+        *pBitsPerSample = bitsPerSample;
+    }
+    if (pSampleRate) {
+        *pSampleRate = sampleRate;
+    }
+
+    return MAL_SUCCESS;
+}
+
 mal_bool32 mal_context_is_device_id_equal__dsound(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1)
 {
     mal_assert(pContext != NULL);
@@ -6027,9 +6722,7 @@ mal_result mal_context_get_device_info__dsound(mal_context* pContext, mal_device
             ((mal_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(mal_context_get_device_info_callback__dsound, &data);
         }
 
-        if (data.found) {
-            return MAL_SUCCESS;
-        } else {
+        if (!data.found) {
             return MAL_NO_DEVICE;
         }
     } else {
@@ -6046,6 +6739,108 @@ mal_result mal_context_get_device_info__dsound(mal_context* pContext, mal_device
         }
     }
 
+    // Retrieving detailed information is slightly different depending on the device type.
+    if (deviceType == mal_device_type_playback) {
+        // Playback.
+        mal_IDirectSound* pDirectSound;
+        mal_result result = mal_context_create_IDirectSound__dsound(pContext, shareMode, pDeviceID, &pDirectSound);
+        if (result != MAL_SUCCESS) {
+            return result;
+        }
+
+        MAL_DSCAPS caps;
+        mal_zero_object(&caps);
+        caps.dwSize = sizeof(caps);
+        if (FAILED(mal_IDirectSound_GetCaps(pDirectSound, &caps))) {
+            return mal_context_post_error(pContext, NULL, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+        }
+
+        if ((caps.dwFlags & MAL_DSCAPS_PRIMARYSTEREO) != 0) {
+            // It supports at least stereo, but could support more.
+            pDeviceInfo->minChannels = 2;
+            pDeviceInfo->maxChannels = 2;
+
+            // Look at the speaker configuration to get a better idea on the channel count.
+            DWORD speakerConfig;
+            if (SUCCEEDED(mal_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig))) {
+                WORD actualChannels;
+                mal_get_channels_from_speaker_config__dsound(speakerConfig, &actualChannels, NULL);
+
+                pDeviceInfo->minChannels = actualChannels;
+                pDeviceInfo->maxChannels = actualChannels;
+            }
+        } else {
+            // It does not support stereo, which means we are stuck with mono.
+            pDeviceInfo->minChannels = 1;
+            pDeviceInfo->maxChannels = 1;
+        }
+
+        // Sample rate.
+        if ((caps.dwFlags & MAL_DSCAPS_CONTINUOUSRATE) != 0) {
+            pDeviceInfo->minSampleRate = caps.dwMinSecondarySampleRate;
+            pDeviceInfo->maxSampleRate = caps.dwMaxSecondarySampleRate;
+
+            // On my machine the min and max sample rates can return 100 and 200000 respectively. I'd rather these be within
+            // the range of our standard sample rates so I'm clamping.
+            if (caps.dwMinSecondarySampleRate < MAL_MIN_SAMPLE_RATE && caps.dwMaxSecondarySampleRate >= MAL_MIN_SAMPLE_RATE) {
+                pDeviceInfo->minSampleRate = MAL_MIN_SAMPLE_RATE;
+            }
+            if (caps.dwMaxSecondarySampleRate > MAL_MAX_SAMPLE_RATE && caps.dwMinSecondarySampleRate <= MAL_MAX_SAMPLE_RATE) {
+                pDeviceInfo->maxSampleRate = MAL_MAX_SAMPLE_RATE;
+            }
+        } else {
+            // Only supports a single sample rate. Set both min an max to the same thing. Do not clamp within the standard rates.
+            pDeviceInfo->minSampleRate = caps.dwMaxSecondarySampleRate;
+            pDeviceInfo->maxSampleRate = caps.dwMaxSecondarySampleRate;
+        }
+
+        // DirectSound can support all formats.
+        pDeviceInfo->formatCount = mal_format_count - 1;    // Minus one because we don't want to include mal_format_unknown.
+        for (mal_uint32 iFormat = 0; iFormat < pDeviceInfo->formatCount; ++iFormat) {
+            pDeviceInfo->formats[iFormat] = (mal_format)(iFormat + 1);  // +1 to skip over mal_format_unknown.
+        }
+
+        mal_IDirectSound_Release(pDirectSound);
+    } else {
+        // Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture
+        // devices can support a number of different formats, but for simplicity and consistency with mal_device_init() I'm just
+        // reporting the best format.
+        mal_IDirectSoundCapture* pDirectSoundCapture;
+        mal_result result = mal_context_create_IDirectSoundCapture__dsound(pContext, shareMode, pDeviceID, &pDirectSoundCapture);
+        if (result != MAL_SUCCESS) {
+            return result;
+        }
+
+        WORD channels;
+        WORD bitsPerSample;
+        DWORD sampleRate;
+        result = mal_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate);
+        if (result != MAL_SUCCESS) {
+            mal_IDirectSoundCapture_Release(pDirectSoundCapture);
+            return result;
+        }
+
+        pDeviceInfo->minChannels = channels;
+        pDeviceInfo->maxChannels = channels;
+        pDeviceInfo->minSampleRate = sampleRate;
+        pDeviceInfo->maxSampleRate = sampleRate;
+        pDeviceInfo->formatCount = 1;
+        if (bitsPerSample == 8) {
+            pDeviceInfo->formats[0] = mal_format_u8;
+        } else if (bitsPerSample == 16) {
+            pDeviceInfo->formats[0] = mal_format_s16;
+        } else if (bitsPerSample == 24) {
+            pDeviceInfo->formats[0] = mal_format_s24;
+        } else if (bitsPerSample == 32) {
+            pDeviceInfo->formats[0] = mal_format_s32;
+        } else {
+            mal_IDirectSoundCapture_Release(pDirectSoundCapture);
+            return MAL_FORMAT_NOT_SUPPORTED;
+        }
+
+        mal_IDirectSoundCapture_Release(pDirectSoundCapture);
+    }
+
     return MAL_SUCCESS;
 }
 
@@ -6181,6 +6976,24 @@ mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type type,
         return MAL_FORMAT_NOT_SUPPORTED;
     }
 
+    if (pDevice->usingDefaultBufferSize) {
+        // CPU speed is a factor to consider when determine how large of a buffer we need.
+        float fCPUSpeed = mal_calculate_cpu_speed_factor();
+
+        // In my testing, capture seems to have worse latency than playback for some reason.
+        float fType;
+        if (type == mal_device_type_playback) {
+            fType = 1.0f;
+        } else {
+            fType = 2.0f;
+        }
+
+        // Backend tax. Need to fiddle with this.
+        float fBackend = 3.0;
+
+        pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fType*fBackend);
+    }
+
 
     WAVEFORMATEXTENSIBLE wf;
     mal_zero_object(&wf);
@@ -6199,19 +7012,10 @@ mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type type,
 
     // Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices :(
     if (type == mal_device_type_playback) {
-        if (FAILED(((mal_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, (mal_IDirectSound**)&pDevice->dsound.pPlayback, NULL))) {
-            mal_device_uninit__dsound(pDevice);
-            return mal_post_error(pDevice, "[DirectSound] DirectSoundCreate() failed for playback device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
-        }
-
-        // The cooperative level must be set before doing anything else.
-        HWND hWnd = ((MAL_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
-        if (hWnd == NULL) {
-            hWnd = ((MAL_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
-        }
-        if (FAILED(mal_IDirectSound_SetCooperativeLevel((mal_IDirectSound*)pDevice->dsound.pPlayback, hWnd, (pConfig->shareMode == mal_share_mode_exclusive) ? MAL_DSSCL_EXCLUSIVE : MAL_DSSCL_PRIORITY))) {
+        mal_result result = mal_context_create_IDirectSound__dsound(pContext, pConfig->shareMode, pDeviceID, (mal_IDirectSound**)&pDevice->dsound.pPlayback);
+        if (result != MAL_SUCCESS) {
             mal_device_uninit__dsound(pDevice);
-            return mal_post_error(pDevice, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+            return result;
         }
 
         MAL_DSBUFFERDESC descDSPrimary;
@@ -6330,81 +7134,16 @@ mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type type,
             pDevice->bufferSizeInFrames *= 2; // <-- Might need to fiddle with this to find a more ideal value. May even be able to just add a fixed amount rather than scaling.
         }
 
-        if (FAILED(((mal_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, (mal_IDirectSoundCapture**)&pDevice->dsound.pCapture, NULL))) {
+        mal_result result = mal_context_create_IDirectSoundCapture__dsound(pContext, pConfig->shareMode, pDeviceID, (mal_IDirectSoundCapture**)&pDevice->dsound.pCapture);
+        if (result != MAL_SUCCESS) {
             mal_device_uninit__dsound(pDevice);
-            return mal_post_error(pDevice, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+            return result;
         }
 
-
-        MAL_DSCCAPS caps;
-        mal_zero_object(&caps);
-        caps.dwSize = sizeof(caps);
-        if (FAILED(mal_IDirectSoundCapture_GetCaps((mal_IDirectSoundCapture*)pDevice->dsound.pCapture, &caps))) {
+        result = mal_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, (mal_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.Format.nChannels, &wf.Format.wBitsPerSample, &wf.Format.nSamplesPerSec);
+        if (result != MAL_SUCCESS) {
             mal_device_uninit__dsound(pDevice);
-            return mal_post_error(pDevice, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
-        }
-
-        wf.Format.nChannels = (WORD)caps.dwChannels;
-
-        // The device can support multiple formats. We just go through the different formats in order of priority and
-        // pick the first one. This the same type of system as the WinMM backend.
-        wf.Format.wBitsPerSample = 16;
-        wf.Format.nSamplesPerSec = 48000;
-
-        if (caps.dwChannels == 1) {
-            if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) {
-                wf.Format.nSamplesPerSec = 48000;
-            } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) {
-                wf.Format.nSamplesPerSec = 44100;
-            } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) {
-                wf.Format.nSamplesPerSec = 22050;
-            } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) {
-                wf.Format.nSamplesPerSec = 11025;
-            } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) {
-                wf.Format.nSamplesPerSec = 96000;
-            } else {
-                wf.Format.wBitsPerSample = 8;
-                if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) {
-                    wf.Format.nSamplesPerSec = 48000;
-                } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) {
-                    wf.Format.nSamplesPerSec = 44100;
-                } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) {
-                    wf.Format.nSamplesPerSec = 22050;
-                } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) {
-                    wf.Format.nSamplesPerSec = 11025;
-                } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) {
-                    wf.Format.nSamplesPerSec = 96000;
-                } else {
-                    wf.Format.wBitsPerSample = 16;  // Didn't find it. Just fall back to 16-bit.
-                }
-            }
-        } else if (caps.dwChannels == 2) {
-            if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) {
-                wf.Format.nSamplesPerSec = 48000;
-            } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) {
-                wf.Format.nSamplesPerSec = 44100;
-            } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) {
-                wf.Format.nSamplesPerSec = 22050;
-            } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) {
-                wf.Format.nSamplesPerSec = 11025;
-            } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) {
-                wf.Format.nSamplesPerSec = 96000;
-            } else {
-                wf.Format.wBitsPerSample = 8;
-                if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) {
-                    wf.Format.nSamplesPerSec = 48000;
-                } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) {
-                    wf.Format.nSamplesPerSec = 44100;
-                } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) {
-                    wf.Format.nSamplesPerSec = 22050;
-                } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) {
-                    wf.Format.nSamplesPerSec = 11025;
-                } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) {
-                    wf.Format.nSamplesPerSec = 96000;
-                } else {
-                    wf.Format.wBitsPerSample = 16;  // Didn't find it. Just fall back to 16-bit.
-                }
-            }
+            return result;
         }
 
         wf.Format.nBlockAlign          = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
@@ -6740,7 +7479,7 @@ typedef struct
 } MAL_WAVEINCAPS2A;
 
 typedef UINT     (WINAPI * MAL_PFN_waveOutGetNumDevs)(void);
-typedef MMRESULT (WINAPI * MAL_PFN_waveOutGetDevCapsA)(UINT_PTR uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc);
+typedef MMRESULT (WINAPI * MAL_PFN_waveOutGetDevCapsA)(mal_uintptr uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc);
 typedef MMRESULT (WINAPI * MAL_PFN_waveOutOpen)(LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
 typedef MMRESULT (WINAPI * MAL_PFN_waveOutClose)(HWAVEOUT hwo);
 typedef MMRESULT (WINAPI * MAL_PFN_waveOutPrepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
@@ -6748,7 +7487,7 @@ typedef MMRESULT (WINAPI * MAL_PFN_waveOutUnprepareHeader)(HWAVEOUT hwo, LPWAVEH
 typedef MMRESULT (WINAPI * MAL_PFN_waveOutWrite)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
 typedef MMRESULT (WINAPI * MAL_PFN_waveOutReset)(HWAVEOUT hwo);
 typedef UINT     (WINAPI * MAL_PFN_waveInGetNumDevs)(void);
-typedef MMRESULT (WINAPI * MAL_PFN_waveInGetDevCapsA)(UINT_PTR uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic);
+typedef MMRESULT (WINAPI * MAL_PFN_waveInGetDevCapsA)(mal_uintptr uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic);
 typedef MMRESULT (WINAPI * MAL_PFN_waveInOpen)(LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
 typedef MMRESULT (WINAPI * MAL_PFN_waveInClose)(HWAVEIN hwi);
 typedef MMRESULT (WINAPI * MAL_PFN_waveInPrepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
@@ -6801,6 +7540,86 @@ typedef struct
     GUID NameGuid;
 } MAL_WAVECAPSA;
 
+mal_result mal_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate)
+{
+    if (pBitsPerSample) {
+        *pBitsPerSample = 0;
+    }
+    if (pSampleRate) {
+        *pSampleRate = 0;
+    }
+
+    WORD bitsPerSample = 0;
+    DWORD sampleRate = 0;
+
+    if (channels == 1) {
+        bitsPerSample = 16;
+        if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
+            sampleRate = 48000;
+        } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
+            sampleRate = 44100;
+        } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
+            sampleRate = 22050;
+        } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
+            sampleRate = 11025;
+        } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
+            sampleRate = 96000;
+        } else {
+            bitsPerSample = 8;
+            if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
+                sampleRate = 48000;
+            } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
+                sampleRate = 44100;
+            } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
+                sampleRate = 22050;
+            } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
+                sampleRate = 11025;
+            } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
+                sampleRate = 96000;
+            } else {
+                return MAL_FORMAT_NOT_SUPPORTED;
+            }
+        }
+    } else {
+        bitsPerSample = 16;
+        if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
+            sampleRate = 48000;
+        } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
+            sampleRate = 44100;
+        } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
+            sampleRate = 22050;
+        } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
+            sampleRate = 11025;
+        } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
+            sampleRate = 96000;
+        } else {
+            bitsPerSample = 8;
+            if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
+                sampleRate = 48000;
+            } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
+                sampleRate = 44100;
+            } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
+                sampleRate = 22050;
+            } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
+                sampleRate = 11025;
+            } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
+                sampleRate = 96000;
+            } else {
+                return MAL_FORMAT_NOT_SUPPORTED;
+            }
+        }
+    }
+
+    if (pBitsPerSample) {
+        *pBitsPerSample = bitsPerSample;
+    }
+    if (pSampleRate) {
+        *pSampleRate = sampleRate;
+    }
+
+    return MAL_SUCCESS;
+}
+
 mal_result mal_context_get_device_info_from_WAVECAPS(mal_context* pContext, MAL_WAVECAPSA* pCaps, mal_device_info* pDeviceInfo)
 {
     mal_assert(pContext != NULL);
@@ -6835,12 +7654,12 @@ mal_result mal_context_get_device_info_from_WAVECAPS(mal_context* pContext, MAL_
             mal_strcat_s(keyStr, sizeof(keyStr), guidStr);
 
             HKEY hKey;
-            LONG result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey);
+            LONG result = ((MAL_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey);
             if (result == ERROR_SUCCESS) {
                 BYTE nameFromReg[512];
                 DWORD nameFromRegSize = sizeof(nameFromReg);
-                result = RegQueryValueExA(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize);
-                RegCloseKey(hKey);
+                result = ((MAL_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize);
+                ((MAL_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey);
 
                 if (result == ERROR_SUCCESS) {
                     // We have the value from the registry, so now we need to construct the name string.
@@ -6864,6 +7683,31 @@ mal_result mal_context_get_device_info_from_WAVECAPS(mal_context* pContext, MAL_
         }
     }
 
+    
+    WORD bitsPerSample;
+    DWORD sampleRate;
+    mal_result result = mal_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate);
+    if (result != MAL_SUCCESS) {
+        return result;
+    }
+
+    pDeviceInfo->minChannels = pCaps->wChannels;
+    pDeviceInfo->maxChannels = pCaps->wChannels;
+    pDeviceInfo->minSampleRate = sampleRate;
+    pDeviceInfo->maxSampleRate = sampleRate;
+    pDeviceInfo->formatCount = 1;
+    if (bitsPerSample == 8) {
+        pDeviceInfo->formats[0] = mal_format_u8;
+    } else if (bitsPerSample == 16) {
+        pDeviceInfo->formats[0] = mal_format_s16;
+    } else if (bitsPerSample == 24) {
+        pDeviceInfo->formats[0] = mal_format_s24;
+    } else if (bitsPerSample == 32) {
+        pDeviceInfo->formats[0] = mal_format_s32;
+    } else {
+        return MAL_FORMAT_NOT_SUPPORTED;
+    }
+
     return MAL_SUCCESS;
 }
 
@@ -7059,7 +7903,7 @@ mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type type, m
 
     const char* errorMsg = "";
     mal_result errorCode = MAL_ERROR;
-
+    mal_result result = MAL_SUCCESS;
 
     // WinMM doesn't seem to have a good way to query the format of the device. Therefore, we'll restrict the formats to the
     // standard formats documented here https://msdn.microsoft.com/en-us/library/windows/desktop/dd743855(v=vs.85).aspx. If
@@ -7125,66 +7969,12 @@ mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type type, m
         goto on_error;
     }
 
-    if (wChannels == 1) {
-        wf.nChannels = 1;
-        wf.wBitsPerSample = 16;
-        if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
-            wf.nSamplesPerSec = 48000;
-        } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
-            wf.nSamplesPerSec = 44100;
-        } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
-            wf.nSamplesPerSec = 22050;
-        } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
-            wf.nSamplesPerSec = 11025;
-        } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
-            wf.nSamplesPerSec = 96000;
-        } else {
-            wf.wBitsPerSample = 8;
-            if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
-                wf.nSamplesPerSec = 48000;
-            } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
-                wf.nSamplesPerSec = 44100;
-            } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
-                wf.nSamplesPerSec = 22050;
-            } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
-                wf.nSamplesPerSec = 11025;
-            } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
-                wf.nSamplesPerSec = 96000;
-            } else {
-                errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = MAL_FORMAT_NOT_SUPPORTED;
-                goto on_error;
-            }
-        }
-    } else {
-        wf.nChannels = 2;
-        wf.wBitsPerSample = 16;
-        if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
-            wf.nSamplesPerSec = 48000;
-        } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
-            wf.nSamplesPerSec = 44100;
-        } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
-            wf.nSamplesPerSec = 22050;
-        } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
-            wf.nSamplesPerSec = 11025;
-        } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
-            wf.nSamplesPerSec = 96000;
-        } else {
-            wf.wBitsPerSample = 8;
-            if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
-                wf.nSamplesPerSec = 48000;
-            } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
-                wf.nSamplesPerSec = 44100;
-            } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
-                wf.nSamplesPerSec = 22050;
-            } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
-                wf.nSamplesPerSec = 11025;
-            } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
-                wf.nSamplesPerSec = 96000;
-            } else {
-                errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = MAL_FORMAT_NOT_SUPPORTED;
-                goto on_error;
-            }
-        }
+    wf.nChannels = wChannels;
+
+    result = mal_get_best_info_from_formats_flags__winmm(dwFormats, wChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec);
+    if (result != MAL_SUCCESS) {
+        errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
+        goto on_error;
     }
 
     wf.nBlockAlign     = (wf.nChannels * wf.wBitsPerSample) / 8;
@@ -7200,14 +7990,14 @@ mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type type, m
 
 
     if (type == mal_device_type_playback) {
-        MMRESULT result = ((MAL_PFN_waveOutOpen)pContext->winmm.waveOutOpen)((LPHWAVEOUT)&pDevice->winmm.hDevice, winMMDeviceID, &wf, (DWORD_PTR)pDevice->winmm.hEvent, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC);
-        if (result != MMSYSERR_NOERROR) {
+        MMRESULT resultMM = ((MAL_PFN_waveOutOpen)pContext->winmm.waveOutOpen)((LPHWAVEOUT)&pDevice->winmm.hDevice, winMMDeviceID, &wf, (DWORD_PTR)pDevice->winmm.hEvent, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC);
+        if (resultMM != MMSYSERR_NOERROR) {
             errorMsg = "[WinMM] Failed to open playback device.", errorCode = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
             goto on_error;
         }
     } else {
-        MMRESULT result = ((MAL_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((LPHWAVEIN)&pDevice->winmm.hDevice, winMMDeviceID, &wf, (DWORD_PTR)pDevice->winmm.hEvent, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC);
-        if (result != MMSYSERR_NOERROR) {
+        MMRESULT resultMM = ((MAL_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((LPHWAVEIN)&pDevice->winmm.hDevice, winMMDeviceID, &wf, (DWORD_PTR)pDevice->winmm.hEvent, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC);
+        if (resultMM != MMSYSERR_NOERROR) {
             errorMsg = "[WinMM] Failed to open capture device.", errorCode = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
             goto on_error;
         }
@@ -7238,11 +8028,21 @@ mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type type, m
 
     // Latency with WinMM seems pretty bad from my testing... Need to increase the default buffer size.
     if (pDevice->usingDefaultBufferSize) {
-        if (pDevice->type == mal_device_type_playback) {
-            pDevice->bufferSizeInFrames *= 4; // <-- Might need to fiddle with this to find a more ideal value. May even be able to just add a fixed amount rather than scaling.
+        // CPU speed is a factor to consider when determine how large of a buffer we need.
+        float fCPUSpeed = mal_calculate_cpu_speed_factor();
+
+        // In my testing, capture seems to have worse latency than playback for some reason.
+        float fType;
+        if (type == mal_device_type_playback) {
+            fType = 1.0f;
         } else {
-            pDevice->bufferSizeInFrames *= 2;
+            fType = 2.0f;
         }
+
+        // Backend tax. Need to fiddle with this.
+        float fBackend = 12.0;
+
+        pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fType*fBackend);
     }
 
     // The size of the intermediary buffer needs to be able to fit every fragment.
@@ -7705,7 +8505,11 @@ typedef int                   (* mal_snd_pcm_hw_params_set_periods_near_proc)
 typedef int                   (* mal_snd_pcm_hw_params_set_access_proc)          (mal_snd_pcm_t *pcm, mal_snd_pcm_hw_params_t *params, mal_snd_pcm_access_t _access);
 typedef int                   (* mal_snd_pcm_hw_params_get_format_proc)          (const mal_snd_pcm_hw_params_t *params, mal_snd_pcm_format_t *format);
 typedef int                   (* mal_snd_pcm_hw_params_get_channels_proc)        (const mal_snd_pcm_hw_params_t *params, unsigned int *val);
+typedef int                   (* mal_snd_pcm_hw_params_get_channels_min_proc)    (const mal_snd_pcm_hw_params_t *params, unsigned int *val);
+typedef int                   (* mal_snd_pcm_hw_params_get_channels_max_proc)    (const mal_snd_pcm_hw_params_t *params, unsigned int *val);
 typedef int                   (* mal_snd_pcm_hw_params_get_rate_proc)            (const mal_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
+typedef int                   (* mal_snd_pcm_hw_params_get_rate_min_proc)        (const mal_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
+typedef int                   (* mal_snd_pcm_hw_params_get_rate_max_proc)        (const mal_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
 typedef int                   (* mal_snd_pcm_hw_params_get_buffer_size_proc)     (const mal_snd_pcm_hw_params_t *params, mal_snd_pcm_uframes_t *val);
 typedef int                   (* mal_snd_pcm_hw_params_get_periods_proc)         (const mal_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
 typedef int                   (* mal_snd_pcm_hw_params_get_access_proc)          (const mal_snd_pcm_hw_params_t *params, mal_snd_pcm_access_t *_access);
@@ -7774,8 +8578,8 @@ static struct
     const char* name;
     float scale;
 } g_malDefaultBufferSizeScalesALSA[] = {
-    {"bcm2835 IEC958/HDMI", 20},
-    {"bcm2835 ALSA",        20}
+    {"bcm2835 IEC958/HDMI", 8.0f},
+    {"bcm2835 ALSA",        8.0f}
 };
 
 float mal_find_default_buffer_size_scale__alsa(const char* deviceName)
@@ -7805,7 +8609,7 @@ mal_format mal_convert_alsa_format_to_mal_format(mal_snd_pcm_format_t formatALSA
         case MAL_SND_PCM_FORMAT_U8:       return mal_format_u8;
         case MAL_SND_PCM_FORMAT_S16_LE:   return mal_format_s16;
         case MAL_SND_PCM_FORMAT_S24_3LE:  return mal_format_s24;
-        //MAL_SND_PCM_FORMAT_S24_LE,        return mal_format_s24_32
+        //case MAL_SND_PCM_FORMAT_S24_LE:   return mal_format_s24_32
         case MAL_SND_PCM_FORMAT_S32_LE:   return mal_format_s32;
         case MAL_SND_PCM_FORMAT_FLOAT_LE: return mal_format_f32;
         default:                          return mal_format_unknown;
@@ -8027,6 +8831,123 @@ mal_bool32 mal_does_id_exist_in_list__alsa(mal_device_id* pUniqueIDs, mal_uint32
 }
 
 
+mal_result mal_context_open_pcm__alsa(mal_context* pContext, mal_share_mode shareMode, mal_device_type type, const mal_device_id* pDeviceID, mal_snd_pcm_t** ppPCM)
+{
+    mal_assert(pContext != NULL);
+    mal_assert(ppPCM != NULL);
+
+    *ppPCM = NULL;
+
+    mal_snd_pcm_t* pPCM = NULL;
+
+    
+
+    mal_snd_pcm_stream_t stream = (type == mal_device_type_playback) ? MAL_SND_PCM_STREAM_PLAYBACK : MAL_SND_PCM_STREAM_CAPTURE;
+    int openMode = MAL_SND_PCM_NO_AUTO_RESAMPLE | MAL_SND_PCM_NO_AUTO_CHANNELS | MAL_SND_PCM_NO_AUTO_FORMAT;
+
+    if (pDeviceID == NULL) {
+        // We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes
+        // me feel better to try as hard as we can get to get _something_ working.
+        const char* defaultDeviceNames[] = {
+            "default",
+            NULL,
+            NULL,
+            NULL,
+            NULL,
+            NULL,
+            NULL
+        };
+
+        if (shareMode == mal_share_mode_exclusive) {
+            defaultDeviceNames[1] = "hw";
+            defaultDeviceNames[2] = "hw:0";
+            defaultDeviceNames[3] = "hw:0,0";
+        } else {
+            if (type == mal_device_type_playback) {
+                defaultDeviceNames[1] = "dmix";
+                defaultDeviceNames[2] = "dmix:0";
+                defaultDeviceNames[3] = "dmix:0,0";
+            } else {
+                defaultDeviceNames[1] = "dsnoop";
+                defaultDeviceNames[2] = "dsnoop:0";
+                defaultDeviceNames[3] = "dsnoop:0,0";
+            }
+            defaultDeviceNames[4] = "hw";
+            defaultDeviceNames[5] = "hw:0";
+            defaultDeviceNames[6] = "hw:0,0";
+        }
+
+        mal_bool32 isDeviceOpen = MAL_FALSE;
+        for (size_t i = 0; i < mal_countof(defaultDeviceNames); ++i) {
+            if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') {
+                if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) {
+                    isDeviceOpen = MAL_TRUE;
+                    break;
+                }
+            }
+        }
+
+        if (!isDeviceOpen) {
+            return mal_context_post_error(pContext, NULL, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+        }
+    } else {
+        // We're trying to open a specific device. There's a few things to consider here:
+        //
+        // mini_al recongnizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When
+        // an ID of this format is specified, it indicates to mini_al that it can try different combinations of plugins ("hw", "dmix", etc.) until it
+        // finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw").
+
+        // May end up needing to make small adjustments to the ID, so make a copy.
+        mal_device_id deviceID = *pDeviceID;
+
+        mal_bool32 isDeviceOpen = MAL_FALSE;
+        if (deviceID.alsa[0] != ':') {
+            // The ID is not in ":0,0" format. Use the ID exactly as-is.
+            if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode) == 0) {
+                isDeviceOpen = MAL_TRUE;
+            }
+        } else {
+            // The ID is in ":0,0" format. Try different plugins depending on the shared mode.
+            if (deviceID.alsa[1] == '\0') {
+                deviceID.alsa[0] = '\0';  // An ID of ":" should be converted to "".
+            }
+
+            char hwid[256];
+            if (shareMode == mal_share_mode_shared) {
+                if (type == mal_device_type_playback) {
+                    mal_strcpy_s(hwid, sizeof(hwid), "dmix");
+                } else {
+                    mal_strcpy_s(hwid, sizeof(hwid), "dsnoop");
+                }
+
+                if (mal_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
+                    if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode) == 0) {
+                        isDeviceOpen = MAL_TRUE;
+                    }
+                }
+            }
+
+            // If at this point we still don't have an open device it means we're either preferencing exclusive mode or opening with "dmix"/"dsnoop" failed.
+            if (!isDeviceOpen) {
+                mal_strcpy_s(hwid, sizeof(hwid), "hw");
+                if (mal_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
+                    if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode) == 0) {
+                        isDeviceOpen = MAL_TRUE;
+                    }
+                }
+            }
+        }
+
+        if (!isDeviceOpen) {
+            return mal_context_post_error(pContext, NULL, "[ALSA] snd_pcm_open() failed.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+        }
+    }
+
+    *ppPCM = pPCM;
+    return MAL_SUCCESS;
+}
+
+
 mal_bool32 mal_context_is_device_id_equal__alsa(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1)
 {
     mal_assert(pContext != NULL);
@@ -8242,11 +9163,57 @@ mal_result mal_context_get_device_info__alsa(mal_context* pContext, mal_device_t
         return result;
     }
 
-    if (data.foundDevice) {
-        return MAL_SUCCESS;
-    } else {
+    if (!data.foundDevice) {
         return MAL_NO_DEVICE;
     }
+
+
+    // For detailed info we need to open the device.
+    mal_snd_pcm_t* pPCM;
+    result = mal_context_open_pcm__alsa(pContext, shareMode, deviceType, pDeviceID, &pPCM);
+    if (result != MAL_SUCCESS) {
+        return result;
+    }
+
+    // We need to initialize a HW parameters object in order to know what formats are supported.
+    mal_snd_pcm_hw_params_t* pHWParams = (mal_snd_pcm_hw_params_t*)alloca(((mal_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)());
+    mal_zero_memory(pHWParams, ((mal_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)());
+
+    if (((mal_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams) < 0) {
+        return mal_context_post_error(pContext, NULL, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", MAL_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
+    }
+
+    int sampleRateDir = 0;
+
+    ((mal_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &pDeviceInfo->minChannels);
+    ((mal_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &pDeviceInfo->maxChannels);
+    ((mal_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &pDeviceInfo->minSampleRate, &sampleRateDir);
+    ((mal_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &pDeviceInfo->maxSampleRate, &sampleRateDir);
+
+    // Formats.
+    mal_snd_pcm_format_mask_t* pFormatMask = (mal_snd_pcm_format_mask_t*)alloca(((mal_snd_pcm_format_mask_sizeof_proc)pContext->alsa.snd_pcm_format_mask_sizeof)());
+    mal_zero_memory(pFormatMask, ((mal_snd_pcm_format_mask_sizeof_proc)pContext->alsa.snd_pcm_format_mask_sizeof)());
+    ((mal_snd_pcm_hw_params_get_format_mask_proc)pContext->alsa.snd_pcm_hw_params_get_format_mask)(pHWParams, pFormatMask);
+
+    pDeviceInfo->formatCount = 0;
+    if (((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MAL_SND_PCM_FORMAT_U8)) {
+        pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_u8;
+    }
+    if (((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MAL_SND_PCM_FORMAT_S16_LE)) {
+        pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_s16;
+    }
+    if (((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MAL_SND_PCM_FORMAT_S24_3LE)) {
+        pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_s24;
+    }
+    if (((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MAL_SND_PCM_FORMAT_S32_LE)) {
+        pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_s32;
+    }
+    if (((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MAL_SND_PCM_FORMAT_FLOAT_LE)) {
+        pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_f32;
+    }
+
+    ((mal_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
+    return MAL_SUCCESS;
 }
 
 mal_result mal_context_init__alsa(mal_context* pContext)
@@ -8274,7 +9241,11 @@ mal_result mal_context_init__alsa(mal_context* pContext)
     pContext->alsa.snd_pcm_hw_params_set_access           = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access");
     pContext->alsa.snd_pcm_hw_params_get_format           = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format");
     pContext->alsa.snd_pcm_hw_params_get_channels         = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels");
+    pContext->alsa.snd_pcm_hw_params_get_channels_min     = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min");
+    pContext->alsa.snd_pcm_hw_params_get_channels_max     = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max");
     pContext->alsa.snd_pcm_hw_params_get_rate             = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate");
+    pContext->alsa.snd_pcm_hw_params_get_rate_min         = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min");
+    pContext->alsa.snd_pcm_hw_params_get_rate_max         = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max");
     pContext->alsa.snd_pcm_hw_params_get_buffer_size      = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size");
     pContext->alsa.snd_pcm_hw_params_get_periods          = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods");
     pContext->alsa.snd_pcm_hw_params_get_access           = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access");
@@ -8323,7 +9294,11 @@ mal_result mal_context_init__alsa(mal_context* pContext)
     mal_snd_pcm_hw_params_set_access_proc           _snd_pcm_hw_params_set_access           = snd_pcm_hw_params_set_access;
     mal_snd_pcm_hw_params_get_format_proc           _snd_pcm_hw_params_get_format           = snd_pcm_hw_params_get_format;
     mal_snd_pcm_hw_params_get_channels_proc         _snd_pcm_hw_params_get_channels         = snd_pcm_hw_params_get_channels;
+    mal_snd_pcm_hw_params_get_channels_min_proc     _snd_pcm_hw_params_get_channels_min     = snd_pcm_hw_params_get_channels_min;
+    mal_snd_pcm_hw_params_get_channels_max_proc     _snd_pcm_hw_params_get_channels_max     = snd_pcm_hw_params_get_channels_max;
     mal_snd_pcm_hw_params_get_rate_proc             _snd_pcm_hw_params_get_rate             = snd_pcm_hw_params_get_rate;
+    mal_snd_pcm_hw_params_get_rate_min_proc         _snd_pcm_hw_params_get_rate_min         = snd_pcm_hw_params_get_rate_min;
+    mal_snd_pcm_hw_params_get_rate_max_proc         _snd_pcm_hw_params_get_rate_max         = snd_pcm_hw_params_get_rate_max;
     mal_snd_pcm_hw_params_get_buffer_size_proc      _snd_pcm_hw_params_get_buffer_size      = snd_pcm_hw_params_get_buffer_size;
     mal_snd_pcm_hw_params_get_periods_proc          _snd_pcm_hw_params_get_periods          = snd_pcm_hw_params_get_periods;
     mal_snd_pcm_hw_params_get_access_proc           _snd_pcm_hw_params_get_access           = snd_pcm_hw_params_get_access;
@@ -8371,6 +9346,8 @@ mal_result mal_context_init__alsa(mal_context* pContext)
     pContext->alsa.snd_pcm_hw_params_set_access           = (mal_proc)_snd_pcm_hw_params_set_access;
     pContext->alsa.snd_pcm_hw_params_get_format           = (mal_proc)_snd_pcm_hw_params_get_format;
     pContext->alsa.snd_pcm_hw_params_get_channels         = (mal_proc)_snd_pcm_hw_params_get_channels;
+    pContext->alsa.snd_pcm_hw_params_get_channels_min     = (mal_proc)_snd_pcm_hw_params_get_channels_min;
+    pContext->alsa.snd_pcm_hw_params_get_channels_max     = (mal_proc)_snd_pcm_hw_params_get_channels_max;
     pContext->alsa.snd_pcm_hw_params_get_rate             = (mal_proc)_snd_pcm_hw_params_get_rate;
     pContext->alsa.snd_pcm_hw_params_get_buffer_size      = (mal_proc)_snd_pcm_hw_params_get_buffer_size;
     pContext->alsa.snd_pcm_hw_params_get_periods          = (mal_proc)_snd_pcm_hw_params_get_periods;
@@ -8642,188 +9619,113 @@ mal_bool32 mal_device_read__alsa(mal_device* pDevice)
             }
 
             if (requiresRestart) {
-                if (((mal_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((mal_snd_pcm_t*)pDevice->alsa.pPCM) < 0) {
-                    return MAL_FALSE;
-                }
-            }
-
-            framesAvailable -= mappedFrames;
-        }
-    } else {
-        // readi/writei.
-        mal_snd_pcm_sframes_t framesRead = 0;
-        while (!pDevice->alsa.breakFromMainLoop) {
-            mal_uint32 framesAvailable = mal_device__wait_for_frames__alsa(pDevice, NULL);
-            if (framesAvailable == 0) {
-                continue;
-            }
-
-            framesRead = ((mal_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
-            if (framesRead < 0) {
-                if (framesRead == -EAGAIN) {
-                    continue;   // Just keep trying...
-                } else if (framesRead == -EPIPE) {
-                    // Overrun. Just recover and try reading again.
-                    if (((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((mal_snd_pcm_t*)pDevice->alsa.pPCM, framesRead, MAL_TRUE) < 0) {
-                        mal_post_error(pDevice, "[ALSA] Failed to recover device after overrun.", MAL_FAILED_TO_START_BACKEND_DEVICE);
-                        return MAL_FALSE;
-                    }
-
-                    framesRead = ((mal_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
-                    if (framesRead < 0) {
-                        mal_post_error(pDevice, "[ALSA] Failed to read data from the internal device.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE);
-                        return MAL_FALSE;
-                    }
-
-                    break;  // Success.
-                } else {
-                    return MAL_FALSE;
-                }
-            } else {
-                break;  // Success.
-            }
-        }
-
-        framesToSend = framesRead;
-        pBuffer = pDevice->alsa.pIntermediaryBuffer;
-    }
-
-    if (framesToSend > 0) {
-        mal_device__send_frames_to_client(pDevice, framesToSend, pBuffer);
-    }
-
-    return MAL_TRUE;
-}
-
-void mal_device_uninit__alsa(mal_device* pDevice)
-{
-    mal_assert(pDevice != NULL);
-
-    if ((mal_snd_pcm_t*)pDevice->alsa.pPCM) {
-        ((mal_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((mal_snd_pcm_t*)pDevice->alsa.pPCM);
-
-        if (pDevice->alsa.pIntermediaryBuffer != NULL) {
-            mal_free(pDevice->alsa.pIntermediaryBuffer);
-        }
-    }
-}
-
-mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice)
-{
-    (void)pContext;
-
-    mal_assert(pDevice != NULL);
-    mal_zero_object(&pDevice->alsa);
-
-    mal_snd_pcm_format_t formatALSA = mal_convert_mal_format_to_alsa_format(pConfig->format);
-    mal_snd_pcm_stream_t stream = (type == mal_device_type_playback) ? MAL_SND_PCM_STREAM_PLAYBACK : MAL_SND_PCM_STREAM_CAPTURE;
-
-    int openMode = MAL_SND_PCM_NO_AUTO_RESAMPLE | MAL_SND_PCM_NO_AUTO_CHANNELS | MAL_SND_PCM_NO_AUTO_FORMAT;
-
-    if (pDeviceID == NULL) {
-        // We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes
-        // me feel better to try as hard as we can get to get _something_ working.
-        const char* defaultDeviceNames[] = {
-            "default",
-            NULL,
-            NULL,
-            NULL,
-            NULL,
-            NULL,
-            NULL
-        };
-
-        if (pConfig->shareMode == mal_share_mode_exclusive) {
-            defaultDeviceNames[1] = "hw";
-            defaultDeviceNames[2] = "hw:0";
-            defaultDeviceNames[3] = "hw:0,0";
-        } else {
-            if (type == mal_device_type_playback) {
-                defaultDeviceNames[1] = "dmix";
-                defaultDeviceNames[2] = "dmix:0";
-                defaultDeviceNames[3] = "dmix:0,0";
-            } else {
-                defaultDeviceNames[1] = "dsnoop";
-                defaultDeviceNames[2] = "dsnoop:0";
-                defaultDeviceNames[3] = "dsnoop:0,0";
-            }
-            defaultDeviceNames[4] = "hw";
-            defaultDeviceNames[5] = "hw:0";
-            defaultDeviceNames[6] = "hw:0,0";
-        }
-
-        mal_bool32 isDeviceOpen = MAL_FALSE;
-        for (size_t i = 0; i < mal_countof(defaultDeviceNames); ++i) {
-            if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') {
-                if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)((mal_snd_pcm_t**)&pDevice->alsa.pPCM, defaultDeviceNames[i], stream, openMode) == 0) {
-                    isDeviceOpen = MAL_TRUE;
-                    break;
-                }
-            }
-        }
-
-        if (!isDeviceOpen) {
-            mal_device_uninit__alsa(pDevice);
-            return mal_post_error(pDevice, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
-        }
-    } else {
-        // We're trying to open a specific device. There's a few things to consider here:
-        //
-        // mini_al recongnizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When
-        // an ID of this format is specified, it indicates to mini_al that it can try different combinations of plugins ("hw", "dmix", etc.) until it
-        // finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw").
-        mal_bool32 isDeviceOpen = MAL_FALSE;
-        if (pDeviceID->alsa[0] != ':') {
-            // The ID is not in ":0,0" format. Use the ID exactly as-is.
-            if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)((mal_snd_pcm_t**)&pDevice->alsa.pPCM, pDeviceID->alsa, stream, openMode) == 0) {
-                isDeviceOpen = MAL_TRUE;
-            }
-        } else {
-            // The ID is in ":0,0" format. Try different plugins depending on the shared mode.
-            if (pDeviceID->alsa[1] == '\0') {
-                pDeviceID->alsa[0] = '\0';  // An ID of ":" should be converted to "".
+                if (((mal_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((mal_snd_pcm_t*)pDevice->alsa.pPCM) < 0) {
+                    return MAL_FALSE;
+                }
             }
 
-            char hwid[256];
-            if (pConfig->shareMode == mal_share_mode_shared) {
-                if (type == mal_device_type_playback) {
-                    mal_strcpy_s(hwid, sizeof(hwid), "dmix");
-                } else {
-                    mal_strcpy_s(hwid, sizeof(hwid), "dsnoop");
-                }
+            framesAvailable -= mappedFrames;
+        }
+    } else {
+        // readi/writei.
+        mal_snd_pcm_sframes_t framesRead = 0;
+        while (!pDevice->alsa.breakFromMainLoop) {
+            mal_uint32 framesAvailable = mal_device__wait_for_frames__alsa(pDevice, NULL);
+            if (framesAvailable == 0) {
+                continue;
+            }
 
-                if (mal_strcat_s(hwid, sizeof(hwid), pDeviceID->alsa) == 0) {
-                    if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)((mal_snd_pcm_t**)&pDevice->alsa.pPCM, hwid, stream, openMode) == 0) {
-                        isDeviceOpen = MAL_TRUE;
+            framesRead = ((mal_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
+            if (framesRead < 0) {
+                if (framesRead == -EAGAIN) {
+                    continue;   // Just keep trying...
+                } else if (framesRead == -EPIPE) {
+                    // Overrun. Just recover and try reading again.
+                    if (((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((mal_snd_pcm_t*)pDevice->alsa.pPCM, framesRead, MAL_TRUE) < 0) {
+                        mal_post_error(pDevice, "[ALSA] Failed to recover device after overrun.", MAL_FAILED_TO_START_BACKEND_DEVICE);
+                        return MAL_FALSE;
                     }
-                }
-            }
 
-            // If at this point we still don't have an open device it means we're either preferencing exclusive mode or opening with "dmix"/"dsnoop" failed.
-            if (!isDeviceOpen) {
-                mal_strcpy_s(hwid, sizeof(hwid), "hw");
-                if (mal_strcat_s(hwid, sizeof(hwid), pDeviceID->alsa) == 0) {
-                    if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)((mal_snd_pcm_t**)&pDevice->alsa.pPCM, hwid, stream, openMode) == 0) {
-                        isDeviceOpen = MAL_TRUE;
+                    framesRead = ((mal_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
+                    if (framesRead < 0) {
+                        mal_post_error(pDevice, "[ALSA] Failed to read data from the internal device.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE);
+                        return MAL_FALSE;
                     }
+
+                    break;  // Success.
+                } else {
+                    return MAL_FALSE;
                 }
+            } else {
+                break;  // Success.
             }
         }
 
-        if (!isDeviceOpen) {
-            mal_device_uninit__alsa(pDevice);
-            return mal_post_error(pDevice, "[ALSA] snd_pcm_open() failed.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+        framesToSend = framesRead;
+        pBuffer = pDevice->alsa.pIntermediaryBuffer;
+    }
+
+    if (framesToSend > 0) {
+        mal_device__send_frames_to_client(pDevice, framesToSend, pBuffer);
+    }
+
+    return MAL_TRUE;
+}
+
+void mal_device_uninit__alsa(mal_device* pDevice)
+{
+    mal_assert(pDevice != NULL);
+
+    if ((mal_snd_pcm_t*)pDevice->alsa.pPCM) {
+        ((mal_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((mal_snd_pcm_t*)pDevice->alsa.pPCM);
+
+        if (pDevice->alsa.pIntermediaryBuffer != NULL) {
+            mal_free(pDevice->alsa.pIntermediaryBuffer);
         }
     }
+}
+
+mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice)
+{
+    (void)pContext;
+
+    mal_assert(pDevice != NULL);
+    mal_zero_object(&pDevice->alsa);
+
+    mal_snd_pcm_format_t formatALSA = mal_convert_mal_format_to_alsa_format(pConfig->format);
+
+    mal_result result = mal_context_open_pcm__alsa(pContext, pConfig->shareMode, type, pDeviceID, (mal_snd_pcm_t**)&pDevice->alsa.pPCM);
+    if (result != MAL_SUCCESS) {
+        return result;
+    }
 
-    // We may need to scale the size of the buffer depending on the device.
+    // If using the default buffer size we do some calculations based on a simple profiling test.
     if (pDevice->usingDefaultBufferSize) {
         mal_snd_pcm_info_t* pInfo = (mal_snd_pcm_info_t*)alloca(((mal_snd_pcm_info_sizeof_proc)pContext->alsa.snd_pcm_info_sizeof)());
         mal_zero_memory(pInfo, ((mal_snd_pcm_info_sizeof_proc)pContext->alsa.snd_pcm_info_sizeof)());
 
-        if (((mal_snd_pcm_info_proc)pContext->alsa.snd_pcm_info)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pInfo) == 0) {
-            float bufferSizeScale = 1;
+        // CPU speed is a factor to consider when determine how large of a buffer we need.
+        float fCPUSpeed = mal_calculate_cpu_speed_factor();
+
+        // We need a slightly bigger buffer if we're using shared mode to cover the inherent tax association with shared mode.
+        float fShareMode;
+        if (pConfig->shareMode == mal_share_mode_shared) {
+            fShareMode = 1.0f;
+        } else {
+            fShareMode = 0.8f;
+        }
+
+        // In my testing, capture seems to have worse latency than playback for some reason.
+        float fType;
+        if (type == mal_device_type_playback) {
+            fType = 1.0f;
+        } else {
+            fType = 2.0f;
+        }
 
+        // We may need to scale the size of the buffer depending on the device.
+        float fDevice = 1;
+        if (((mal_snd_pcm_info_proc)pContext->alsa.snd_pcm_info)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pInfo) == 0) {
             const char* deviceName = ((mal_snd_pcm_info_get_name_proc)pContext->alsa.snd_pcm_info_get_name)(pInfo);
             if (deviceName != NULL) {
                 if (mal_strcmp(deviceName, "default") == 0) {
@@ -8843,7 +9745,7 @@ mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, ma
                         if ((type == mal_device_type_playback && (IOID == NULL || mal_strcmp(IOID, "Output") == 0)) ||
                             (type == mal_device_type_capture  && (IOID != NULL && mal_strcmp(IOID, "Input" ) == 0))) {
                             if (mal_strcmp(NAME, deviceName) == 0) {
-                                bufferSizeScale = mal_find_default_buffer_size_scale__alsa(DESC);
+                                fDevice = mal_find_default_buffer_size_scale__alsa(DESC);
                                 foundDevice = MAL_TRUE;
                             }
                         }
@@ -8860,11 +9762,11 @@ mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, ma
 
                     ((mal_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);
                 } else {
-                    bufferSizeScale = mal_find_default_buffer_size_scale__alsa(deviceName);
+                    fDevice = mal_find_default_buffer_size_scale__alsa(deviceName);
                 }
             }
 
-            pDevice->bufferSizeInFrames = (mal_uint32)(pDevice->bufferSizeInFrames * bufferSizeScale);
+            pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fShareMode*fType*fDevice);
         }
     }
 
@@ -10070,6 +10972,13 @@ void mal_context_get_device_info_sink_callback__pulse(mal_pa_context* pPulseCont
     if (pInfo->description != NULL) {
         mal_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
     }
+
+    pData->pDeviceInfo->minChannels   = pInfo->sample_spec.channels;
+    pData->pDeviceInfo->maxChannels   = pInfo->sample_spec.channels;
+    pData->pDeviceInfo->minSampleRate = pInfo->sample_spec.rate;
+    pData->pDeviceInfo->maxSampleRate = pInfo->sample_spec.rate;
+    pData->pDeviceInfo->formatCount = 1;
+    pData->pDeviceInfo->formats[0] = mal_format_from_pulse(pInfo->sample_spec.format);
 }
 
 void mal_context_get_device_info_source_callback__pulse(mal_pa_context* pPulseContext, const mal_pa_source_info* pInfo, int endOfList, void* pUserData)
@@ -10089,6 +10998,13 @@ void mal_context_get_device_info_source_callback__pulse(mal_pa_context* pPulseCo
     if (pInfo->description != NULL) {
         mal_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
     }
+
+    pData->pDeviceInfo->minChannels = pInfo->sample_spec.channels;
+    pData->pDeviceInfo->maxChannels = pInfo->sample_spec.channels;
+    pData->pDeviceInfo->minSampleRate = pInfo->sample_spec.rate;
+    pData->pDeviceInfo->maxSampleRate = pInfo->sample_spec.rate;
+    pData->pDeviceInfo->formatCount = 1;
+    pData->pDeviceInfo->formats[0] = mal_format_from_pulse(pInfo->sample_spec.format);
 }
 
 mal_result mal_context_get_device_info__pulse(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo)
@@ -10496,6 +11412,8 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, m
         dev = pDeviceID->pulse;
     }
 
+    mal_uint32 bufferSizeInFrames = pConfig->bufferSizeInFrames;
+
     mal_pa_sink_info sinkInfo;
     mal_pa_source_info sourceInfo;
     mal_pa_operation* pOP = NULL;
@@ -10632,7 +11550,26 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, m
     }
 #endif
 
-    attr.maxlength = pConfig->bufferSizeInFrames * mal_get_bytes_per_sample(mal_format_from_pulse(ss.format))*ss.channels;
+    // If using the default buffer size try to find an appropriate default.
+    if (pDevice->usingDefaultBufferSize) {
+        // CPU speed is a factor to consider when determine how large of a buffer we need.
+        float fCPUSpeed = mal_calculate_cpu_speed_factor();
+
+        // In my testing, capture seems to have worse latency than playback for some reason.
+        float fType;
+        if (type == mal_device_type_playback) {
+            fType = 1.0f;
+        } else {
+            fType = 2.0f;
+        }
+
+        // Backend tax. Need to fiddle with this.
+        float fBackend = 1.2;
+
+        bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fType*fBackend);
+    }
+
+    attr.maxlength = bufferSizeInFrames * mal_get_bytes_per_sample(mal_format_from_pulse(ss.format))*ss.channels;
     attr.tlength   = attr.maxlength / pConfig->periods;
     attr.prebuf    = (mal_uint32)-1;
     attr.minreq    = attr.tlength;
@@ -10952,6 +11889,36 @@ typedef const char*        (* mal_jack_port_name_proc)               (const mal_
 typedef void*              (* mal_jack_port_get_buffer_proc)         (mal_jack_port_t* port, mal_jack_nframes_t nframes);
 typedef void               (* mal_jack_free_proc)                    (void* ptr);
 
+mal_result mal_context_open_client__jack(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, mal_jack_client_t** ppClient)
+{
+    mal_assert(pContext != NULL);
+    mal_assert(ppClient != NULL);
+
+    (void)type;
+    (void)pDeviceID;
+
+    if (ppClient) {
+        *ppClient = NULL;
+    }
+
+    size_t maxClientNameSize = ((mal_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); // Includes null terminator.
+
+    char clientName[256];
+    mal_strncpy_s(clientName, mal_min(sizeof(clientName), maxClientNameSize), (pContext->config.jack.pClientName != NULL) ? pContext->config.jack.pClientName : "mini_al", (size_t)-1);
+
+    mal_jack_status_t status;
+    mal_jack_client_t* pClient = ((mal_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->config.jack.tryStartServer) ? 0 : mal_JackNoStartServer, &status, NULL);
+    if (pClient == NULL) {
+        return mal_context_post_error(pContext, NULL, "[JACK] Failed to open client.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+    }
+
+    if (ppClient) {
+        *ppClient = pClient;
+    }
+
+    return MAL_SUCCESS;
+}
+
 mal_bool32 mal_context_is_device_id_equal__jack(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1)
 {
     mal_assert(pContext != NULL);
@@ -11006,6 +11973,37 @@ mal_result mal_context_get_device_info__jack(mal_context* pContext, mal_device_t
         mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
     }
 
+    // Jack only supports f32 and has a specific channel count and sample rate.
+    pDeviceInfo->formatCount = 1;
+    pDeviceInfo->formats[0] = mal_format_f32;
+
+    // The channel count and sample rate can only be determined by opening the device.
+    mal_jack_client_t* pClient;
+    mal_result result = mal_context_open_client__jack(pContext, deviceType, pDeviceID, &pClient);
+    if (result != MAL_SUCCESS) {
+        return result;
+    }
+
+    pDeviceInfo->minSampleRate = ((mal_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((mal_jack_client_t*)pClient);
+    pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
+
+    pDeviceInfo->minChannels = 0;
+    pDeviceInfo->maxChannels = 0;
+
+    const char** ppPorts = ((mal_jack_get_ports_proc)pContext->jack.jack_get_ports)((mal_jack_client_t*)pClient, NULL, NULL, mal_JackPortIsPhysical | ((deviceType == mal_device_type_playback) ? mal_JackPortIsInput : mal_JackPortIsOutput));
+    if (ppPorts == NULL) {
+        ((mal_jack_client_close_proc)pContext->jack.jack_client_close)((mal_jack_client_t*)pClient);
+        return mal_context_post_error(pContext, NULL, "[JACK] Failed to query physical ports.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+    }
+
+    while (ppPorts[pDeviceInfo->minChannels] != NULL) {
+        pDeviceInfo->minChannels += 1;
+        pDeviceInfo->maxChannels += 1;
+    }
+
+    ((mal_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
+    ((mal_jack_client_close_proc)pContext->jack.jack_client_close)((mal_jack_client_t*)pClient);
+
     return MAL_SUCCESS;
 }
 
@@ -11206,15 +12204,9 @@ mal_result mal_device_init__jack(mal_context* pContext, mal_device_type type, ma
 
 
     // Open the client.
-    size_t maxClientNameSize = ((mal_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); // Includes null terminator.
-
-    char clientName[256];
-    mal_strncpy_s(clientName, mal_min(sizeof(clientName), maxClientNameSize), (pContext->config.jack.pClientName != NULL) ? pContext->config.jack.pClientName : "mini_al", (size_t)-1);
-
-    mal_jack_status_t status;
-    pDevice->jack.pClient = ((mal_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->config.jack.tryStartServer) ? 0 : mal_JackNoStartServer, &status, NULL);
-    if (pDevice->jack.pClient == NULL) {
-        return mal_post_error(pDevice, "[JACK] Failed to open client.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+    mal_result result = mal_context_open_client__jack(pContext, type, pDeviceID, (mal_jack_client_t**)&pDevice->jack.pClient);
+    if (result != MAL_SUCCESS) {
+        return result;
     }
 
     // Callbacks.
@@ -11384,6 +12376,29 @@ int mal_open_temp_device__oss()
     return -1;
 }
 
+mal_result mal_context_open_device__oss(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, int* pfd)
+{
+    mal_assert(pContext != NULL);
+    mal_assert(pfd != NULL);
+    (void)pContext;
+
+    *pfd = -1;
+
+    char deviceName[64];
+    if (pDeviceID != NULL) {
+        mal_strncpy_s(deviceName, sizeof(deviceName), pDeviceID->oss, (size_t)-1);
+    } else {
+        mal_strncpy_s(deviceName, sizeof(deviceName), "/dev/dsp", (size_t)-1);
+    }
+
+    *pfd = open(deviceName, (type == mal_device_type_playback) ? O_WRONLY : O_RDONLY, 0);
+    if (*pfd == -1) {
+        return MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
+    }
+
+    return MAL_SUCCESS;
+}
+
 mal_bool32 mal_context_is_device_id_equal__oss(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1)
 {
     mal_assert(pContext != NULL);
@@ -11472,18 +12487,18 @@ mal_result mal_context_get_device_info__oss(mal_context* pContext, mal_device_ty
     // If we get here it means we are _not_ using the default device.
     mal_bool32 foundDevice = MAL_FALSE;
 
-    int fd = mal_open_temp_device__oss();
-    if (fd == -1) {
+    int fdTemp = mal_open_temp_device__oss();
+    if (fdTemp == -1) {
         return mal_context_post_error(pContext, NULL, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.", MAL_NO_BACKEND);
     }
 
     oss_sysinfo si;
-    int result = ioctl(fd, SNDCTL_SYSINFO, &si);
+    int result = ioctl(fdTemp, SNDCTL_SYSINFO, &si);
     if (result != -1) {
         for (int iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
             oss_audioinfo ai;
             ai.dev = iAudioDevice;
-            result = ioctl(fd, SNDCTL_AUDIOINFO, &ai);
+            result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai);
             if (result != -1) {
                 if (mal_strcmp(ai.devnode, pDeviceID->oss) == 0) {
                     // It has the same name, so now just confirm the type.
@@ -11501,6 +12516,29 @@ mal_result mal_context_get_device_info__oss(mal_context* pContext, mal_device_ty
                             mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1);
                         }
 
+                        pDeviceInfo->minChannels = ai.min_channels;
+                        pDeviceInfo->maxChannels = ai.max_channels;
+                        pDeviceInfo->minSampleRate = ai.min_rate;
+                        pDeviceInfo->maxSampleRate = ai.max_rate;
+                        pDeviceInfo->formatCount = 0;
+
+                        unsigned int formatMask;
+                        if (deviceType == mal_device_type_playback) {
+                            formatMask = ai.oformats;
+                        } else {
+                            formatMask = ai.iformats;
+                        }
+
+                        if ((formatMask & AFMT_U8) != 0) {
+                            pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_u8;
+                        }
+                        if ((formatMask & AFMT_S16_LE) != 0) {
+                            pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_s16;
+                        }
+                        if ((formatMask & AFMT_S32_LE) != 0) {
+                            pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_s32;
+                        }
+
                         foundDevice = MAL_TRUE;
                         break;
                     }
@@ -11508,18 +12546,19 @@ mal_result mal_context_get_device_info__oss(mal_context* pContext, mal_device_ty
             }
         }
     } else {
-        close(fd);
+        close(fdTemp);
         return mal_context_post_error(pContext, NULL, "[OSS] Failed to retrieve system information for device enumeration.", MAL_NO_BACKEND);
     }
 
 
-    close(fd);
+    close(fdTemp);
 
-    if (foundDevice) {
-        return MAL_SUCCESS;
-    } else {
+    if (!foundDevice) {
         return MAL_NO_DEVICE;
     }
+
+
+    return MAL_SUCCESS;
 }
 
 mal_result mal_context_init__oss(mal_context* pContext)
@@ -11575,15 +12614,8 @@ mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, mal
     mal_assert(pDevice != NULL);
     mal_zero_object(&pDevice->oss);
 
-    char deviceName[64];
-    if (pDeviceID != NULL) {
-        mal_strncpy_s(deviceName, sizeof(deviceName), pDeviceID->oss, (size_t)-1);
-    } else {
-        mal_strncpy_s(deviceName, sizeof(deviceName), "/dev/dsp", (size_t)-1);
-    }
-
-    pDevice->oss.fd = open(deviceName, (type == mal_device_type_playback) ? O_WRONLY : O_RDONLY, 0);
-    if (pDevice->oss.fd == -1) {
+    mal_result result = mal_context_open_device__oss(pContext, type, pDeviceID, &pDevice->oss.fd);
+    if (result != MAL_SUCCESS) {
         return mal_post_error(pDevice, "[OSS] Failed to open device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
     }
 
@@ -11602,8 +12634,8 @@ mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, mal
         case mal_format_u8:
         default: ossFormat = AFMT_U8; break;
     }
-    int result = ioctl(pDevice->oss.fd, SNDCTL_DSP_SETFMT, &ossFormat);
-    if (result == -1) {
+    int ossResult = ioctl(pDevice->oss.fd, SNDCTL_DSP_SETFMT, &ossFormat);
+    if (ossResult == -1) {
         close(pDevice->oss.fd);
         return mal_post_error(pDevice, "[OSS] Failed to set format.", MAL_FORMAT_NOT_SUPPORTED);
     }
@@ -11618,8 +12650,8 @@ mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, mal
 
     // Channels.
     int ossChannels = (int)pConfig->channels;
-    result = ioctl(pDevice->oss.fd, SNDCTL_DSP_CHANNELS, &ossChannels);
-    if (result == -1) {
+    ossResult = ioctl(pDevice->oss.fd, SNDCTL_DSP_CHANNELS, &ossChannels);
+    if (ossResult == -1) {
         close(pDevice->oss.fd);
         return mal_post_error(pDevice, "[OSS] Failed to set channel count.", MAL_FORMAT_NOT_SUPPORTED);
     }
@@ -11629,8 +12661,8 @@ mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, mal
 
     // Sample rate.
     int ossSampleRate = (int)pConfig->sampleRate;
-    result = ioctl(pDevice->oss.fd, SNDCTL_DSP_SPEED, &ossSampleRate);
-    if (result == -1) {
+    ossResult = ioctl(pDevice->oss.fd, SNDCTL_DSP_SPEED, &ossSampleRate);
+    if (ossResult == -1) {
         close(pDevice->oss.fd);
         return mal_post_error(pDevice, "[OSS] Failed to set sample rate.", MAL_FORMAT_NOT_SUPPORTED);
     }
@@ -11638,6 +12670,24 @@ mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, mal
     pDevice->internalSampleRate = ossSampleRate;
 
 
+    // Try calculating an appropriate default buffer size.
+    if (pDevice->usingDefaultBufferSize) {
+        // CPU speed is a factor to consider when determine how large of a buffer we need.
+        float fCPUSpeed = mal_calculate_cpu_speed_factor();
+
+        // In my testing, capture seems to have worse latency than playback for some reason.
+        float fType;
+        if (type == mal_device_type_playback) {
+            fType = 1.0f;
+        } else {
+            fType = 2.0f;
+        }
+
+        // Backend tax. Need to fiddle with this.
+        float fBackend = 1.0;
+
+        pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fType*fBackend);
+    }
 
     // The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if
     // it should be done before or after format/channels/rate.
@@ -11656,8 +12706,8 @@ mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, mal
     }
 
     int ossFragment = (int)((pDevice->periods << 16) | ossFragmentSizePower);
-    result = ioctl(pDevice->oss.fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment);
-    if (result == -1) {
+    ossResult = ioctl(pDevice->oss.fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment);
+    if (ossResult == -1) {
         close(pDevice->oss.fd);
         return mal_post_error(pDevice, "[OSS] Failed to set fragment size and period count.", MAL_FORMAT_NOT_SUPPORTED);
     }
@@ -12087,6 +13137,8 @@ mal_result mal_context_get_device_info__opensl(mal_context* pContext, mal_device
 
         mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1);
     }
+
+    goto return_detailed_info;
 #else
     goto return_default_device;
 #endif
@@ -12106,6 +13158,26 @@ return_default_device:
         mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
     }
 
+    goto return_detailed_info;
+
+
+return_detailed_info:
+
+    // For now we're just outputting a set of values that are supported by the API but not necessarily supported
+    // by the device natively. Later on we should work on this so that it more closely reflects the device's
+    // actual native format.
+    pDeviceInfo->minChannels = 1;
+    pDeviceInfo->maxChannels = 2;
+    pDeviceInfo->minSampleRate = 8000;
+    pDeviceInfo->maxSampleRate = 48000;
+    pDeviceInfo->formatCount = 2;
+    pDeviceInfo->formats[0] = mal_format_u8;
+    pDeviceInfo->formats[1] = mal_format_s16;
+#if defined(MAL_ANDROID) && __ANDROID_API__ >= 21
+    pDeviceInfo->formats[pDeviceInfo->formatCount] = mal_format_f32;
+    pDeviceInfo->formatCount += 1;
+#endif
+
     return MAL_SUCCESS;
 }
 
@@ -12241,6 +13313,25 @@ mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type type,
     mal_assert(pDevice != NULL);
     mal_zero_object(&pDevice->opensl);
 
+    // Try calculating an appropriate default buffer size.
+    if (pDevice->usingDefaultBufferSize) {
+        // CPU speed is a factor to consider when determine how large of a buffer we need.
+        float fCPUSpeed = mal_calculate_cpu_speed_factor();
+
+        // In my testing, capture seems to have worse latency than playback for some reason.
+        float fType;
+        if (type == mal_device_type_playback) {
+            fType = 1.0f;
+        } else {
+            fType = 2.0f;
+        }
+
+        // Backend tax. Need to fiddle with this.
+        float fBackend = 1.5f;
+
+        pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fType*fBackend);
+    }
+
     pDevice->opensl.currentBufferIndex = 0;
     pDevice->opensl.periodSizeInFrames = pDevice->bufferSizeInFrames / pConfig->periods;
     pDevice->bufferSizeInFrames = pDevice->opensl.periodSizeInFrames * pConfig->periods;
@@ -12865,12 +13956,28 @@ mal_result mal_context_get_device_info__openal(mal_context* pContext, mal_device
             return result;
         }
 
-        if (data.foundDevice) {
-            return MAL_SUCCESS;
-        } else {
+        if (!data.foundDevice) {
             return MAL_NO_DEVICE;
         }
     }
+
+    // mini_al's OpenAL backend only supports:
+    //   - mono and stereo
+    //   - u8, s16 and f32
+    //   - All standard sample rates
+    pDeviceInfo->minChannels = 1;
+    pDeviceInfo->maxChannels = 2;
+    pDeviceInfo->minSampleRate = MAL_MIN_SAMPLE_RATE;
+    pDeviceInfo->maxSampleRate = MAL_MAX_SAMPLE_RATE;
+    pDeviceInfo->formatCount = 2;
+    pDeviceInfo->formats[0] = mal_format_u8;
+    pDeviceInfo->formats[1] = mal_format_s16;
+    if (pContext->openal.isFloat32Supported) {
+        pDeviceInfo->formats[pDeviceInfo->formatCount] = mal_format_f32;
+        pDeviceInfo->formatCount += 1;
+    }
+
+    return MAL_SUCCESS;
 }
 
 mal_result mal_context_init__openal(mal_context* pContext)
@@ -13120,9 +14227,23 @@ mal_result mal_device_init__openal(mal_context* pContext, mal_device_type type,
         pDevice->periods = MAL_MAX_PERIODS_OPENAL;
     }
 
-    // OpenAL has bad latency in my testing :(
+    // Try calculating an appropriate default buffer size.
     if (pDevice->usingDefaultBufferSize) {
-        pDevice->bufferSizeInFrames *= 4;
+        // CPU speed is a factor to consider when determine how large of a buffer we need.
+        float fCPUSpeed = mal_calculate_cpu_speed_factor();
+
+        // In my testing, capture seems to have worse latency than playback for some reason.
+        float fType;
+        if (type == mal_device_type_playback) {
+            fType = 1.0f;
+        } else {
+            fType = 2.0f;
+        }
+
+        // Backend tax. Need to fiddle with this. OpenAL has bad latency in my testing :(
+        float fBackend = 8.0;
+
+        pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fType*fBackend);
     }
 
     mal_ALCsizei bufferSizeInSamplesAL = pDevice->bufferSizeInFrames;
@@ -13647,7 +14768,7 @@ mal_result mal_context_enumerate_devices__sdl(mal_context* pContext, mal_enum_de
     } else
 #endif
     {
-        // SDL1 only uses default devices.
+        // SDL1 only uses default devices, and does not support capture.
         mal_bool32 cbResult = MAL_TRUE;
 
         // Playback.
@@ -13658,6 +14779,7 @@ mal_result mal_context_enumerate_devices__sdl(mal_context* pContext, mal_enum_de
             cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData);
         }
 
+#if 0   // No capture with SDL1.
         // Capture.
         if (cbResult) {
             mal_device_info deviceInfo;
@@ -13665,6 +14787,7 @@ mal_result mal_context_enumerate_devices__sdl(mal_context* pContext, mal_enum_de
             mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
             cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData);
         }
+#endif
     }
 
     return MAL_SUCCESS;
@@ -13702,6 +14825,79 @@ mal_result mal_context_get_device_info__sdl(mal_context* pContext, mal_device_ty
         }
     }
 
+    // To get an accurate idea on the backend's native format we need to open the device. Not ideal, but it's the only way. An
+    // alternative to this is to report all channel counts, sample rates and formats, but that doesn't offer a good representation
+    // of the device's _actual_ ideal format.
+    //
+    // Note: With Emscripten, it looks like non-zero values need to be specified for desiredSpec. Whatever is specified in
+    // desiredSpec will be used by SDL since it uses it just does it's own format conversion internally. Therefore, from what
+    // I can tell, there's no real way to know the device's actual format which means I'm just going to fall back to the full
+    // range of channels and sample rates on Emscripten builds.
+#if defined(__EMSCRIPTEN__)
+    pDeviceInfo->minChannels = MAL_MIN_CHANNELS;
+    pDeviceInfo->maxChannels = MAL_MAX_CHANNELS;
+    pDeviceInfo->minSampleRate = MAL_MIN_SAMPLE_RATE;
+    pDeviceInfo->maxSampleRate = MAL_MAX_SAMPLE_RATE;
+    pDeviceInfo->formatCount = 3;
+    pDeviceInfo->formats[0] = mal_format_u8;
+    pDeviceInfo->formats[1] = mal_format_s16;
+    pDeviceInfo->formats[2] = mal_format_s32;
+#else
+    MAL_SDL_AudioSpec desiredSpec, obtainedSpec;
+    mal_zero_memory(&desiredSpec, sizeof(desiredSpec));
+
+#ifndef MAL_USE_SDL_1
+    if (!pContext->sdl.usingSDL1) {
+        int isCapture = (deviceType == mal_device_type_playback) ? 0 : 1;
+
+        const char* pDeviceName = NULL;
+        if (pDeviceID != NULL) {
+            pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture);
+        }
+
+        MAL_SDL_AudioDeviceID tempDeviceID = ((MAL_PFN_SDL_OpenAudioDevice)pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE);
+        if (tempDeviceID == 0) {
+            return mal_context_post_error(pContext, NULL, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+        }
+
+        ((MAL_PFN_SDL_CloseAudioDevice)pContext->sdl.SDL_CloseAudioDevice)(tempDeviceID);
+    } else
+#endif
+    {
+        // SDL1 uses default devices.
+        (void)pDeviceID;
+
+        // SDL1 only supports playback as far as I can tell.
+        if (deviceType != mal_device_type_playback) {
+            return MAL_NO_DEVICE;
+        }
+
+        MAL_SDL_AudioDeviceID tempDeviceID = ((MAL_PFN_SDL_OpenAudio)pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec);
+        if (tempDeviceID != 0) {
+            return mal_context_post_error(pContext, NULL, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+        }
+
+        ((MAL_PFN_SDL_CloseAudio)pContext->sdl.SDL_CloseAudio)();
+    }
+
+    pDeviceInfo->minChannels = obtainedSpec.channels;
+    pDeviceInfo->maxChannels = obtainedSpec.channels;
+    pDeviceInfo->minSampleRate = obtainedSpec.freq;
+    pDeviceInfo->maxSampleRate = obtainedSpec.freq;
+    pDeviceInfo->formatCount = 1;
+    if (obtainedSpec.format == MAL_AUDIO_U8) {
+        pDeviceInfo->formats[0] = mal_format_u8;
+    } else if (obtainedSpec.format == MAL_AUDIO_S16) {
+        pDeviceInfo->formats[0] = mal_format_s16;
+    } else if (obtainedSpec.format == MAL_AUDIO_S32) {
+        pDeviceInfo->formats[0] = mal_format_s32;
+    } else if (obtainedSpec.format == MAL_AUDIO_F32) {
+        pDeviceInfo->formats[0] = mal_format_f32;
+    } else {
+        return MAL_FORMAT_NOT_SUPPORTED;
+    }
+#endif
+
     return MAL_SUCCESS;
 }
 
@@ -13834,6 +15030,28 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, mal
     // SDL wants the buffer size to be a power of 2. The SDL_AudioSpec property for this is only a Uint16, so we need
     // to explicitly clamp this because it will be easy to overflow.
     mal_uint32 bufferSize = pConfig->bufferSizeInFrames;
+    if (pDevice->usingDefaultBufferSize) {
+        // CPU speed is a factor to consider when determine how large of a buffer we need.
+        float fCPUSpeed = mal_calculate_cpu_speed_factor();
+
+        // In my testing, capture seems to have worse latency than playback for some reason.
+        float fType;
+        if (type == mal_device_type_playback) {
+            fType = 1.0f;
+        } else {
+            fType = 2.0f;
+        }
+
+        // Backend tax. Need to fiddle with this. Special case for Emscripten.
+    #if defined(__EMSCRIPTEN__)
+        float fBackend = 4.0f;
+    #else
+        float fBackend = 2.0f;
+    #endif
+
+        bufferSize = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fType*fBackend);
+    }
+
     if (bufferSize > 32768) {
         bufferSize = 32768;
     } else {
@@ -14250,6 +15468,7 @@ mal_result mal_context_uninit_backend_apis__win32(mal_context* pContext)
     mal_CoUninitialize(pContext);
     mal_dlclose(pContext->win32.hUser32DLL);
     mal_dlclose(pContext->win32.hOle32DLL);
+    mal_dlclose(pContext->win32.hAdvapi32DLL);
 
     return MAL_SUCCESS;
 }
@@ -14279,6 +15498,17 @@ mal_result mal_context_init_backend_apis__win32(mal_context* pContext)
 
     pContext->win32.GetForegroundWindow = (mal_proc)mal_dlsym(pContext->win32.hUser32DLL, "GetForegroundWindow");
     pContext->win32.GetDesktopWindow    = (mal_proc)mal_dlsym(pContext->win32.hUser32DLL, "GetDesktopWindow");
+
+
+    // Advapi32.dll
+    pContext->win32.hAdvapi32DLL = mal_dlopen("advapi32.dll");
+    if (pContext->win32.hAdvapi32DLL == NULL) {
+        return MAL_FAILED_TO_INIT_BACKEND;
+    }
+
+    pContext->win32.RegOpenKeyExA    = (mal_proc)mal_dlsym(pContext->win32.hAdvapi32DLL, "RegOpenKeyExA");
+    pContext->win32.RegCloseKey      = (mal_proc)mal_dlsym(pContext->win32.hAdvapi32DLL, "RegCloseKey");
+    pContext->win32.RegQueryValueExA = (mal_proc)mal_dlsym(pContext->win32.hAdvapi32DLL, "RegQueryValueExA");
 #endif
 
     mal_CoInitializeEx(pContext, NULL, 0);  // 0 = COINIT_MULTITHREADED
@@ -14345,9 +15575,11 @@ mal_result mal_context_init_backend_apis__nix(mal_context* pContext)
     pContext->posix.pthread_cond_signal         = (mal_proc)pthread_cond_signal;
     pContext->posix.pthread_attr_init           = (mal_proc)pthread_attr_init;
     pContext->posix.pthread_attr_destroy        = (mal_proc)pthread_attr_destroy;
+#if !defined(__EMSCRIPTEN__)
     pContext->posix.pthread_attr_setschedpolicy = (mal_proc)pthread_attr_setschedpolicy;
     pContext->posix.pthread_attr_getschedparam  = (mal_proc)pthread_attr_getschedparam;
     pContext->posix.pthread_attr_setschedparam  = (mal_proc)pthread_attr_setschedparam;
+#endif
 #endif
 
     return MAL_SUCCESS;
@@ -14421,15 +15653,17 @@ mal_result mal_context_init(const mal_backend backends[], mal_uint32 backendCoun
         return result;
     }
 
-    if (backends == NULL) {
-        backends = g_malDefaultBackends;
-        backendCount = mal_countof(g_malDefaultBackends);
+    mal_backend* pBackendsToIterate = (mal_backend*)backends;
+    mal_uint32 backendsToIterateCount = backendCount;
+    if (pBackendsToIterate == NULL) {
+        pBackendsToIterate = (mal_backend*)g_malDefaultBackends;
+        backendsToIterateCount = mal_countof(g_malDefaultBackends);
     }
 
-    mal_assert(backends != NULL);
+    mal_assert(pBackendsToIterate != NULL);
 
-    for (mal_uint32 iBackend = 0; iBackend < backendCount; ++iBackend) {
-        mal_backend backend = backends[iBackend];
+    for (mal_uint32 iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
+        mal_backend backend = pBackendsToIterate[iBackend];
 
         result = MAL_NO_BACKEND;
         switch (backend) {
@@ -14740,6 +15974,12 @@ mal_result mal_context_get_device_info(mal_context* pContext, mal_device_type ty
         }
         mal_mutex_unlock(&pContext->deviceInfoLock);
 
+        // Clamp ranges.
+        deviceInfo.minChannels   = mal_max(deviceInfo.minChannels,   MAL_MIN_CHANNELS);
+        deviceInfo.maxChannels   = mal_min(deviceInfo.maxChannels,   MAL_MAX_CHANNELS);
+        deviceInfo.minSampleRate = mal_max(deviceInfo.minSampleRate, MAL_MIN_SAMPLE_RATE);
+        deviceInfo.maxSampleRate = mal_min(deviceInfo.maxSampleRate, MAL_MAX_SAMPLE_RATE);
+
         *pDeviceInfo = deviceInfo;
         return result;
     }
@@ -14814,7 +16054,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
 
     // Default buffer size and periods.
     if (config.bufferSizeInFrames == 0) {
-        config.bufferSizeInFrames = (config.sampleRate/1000) * MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS;
+        config.bufferSizeInFrames = (config.sampleRate/1000) * MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY;
         pDevice->usingDefaultBufferSize = MAL_TRUE;
     }
     if (config.periods == 0) {
@@ -15030,15 +16270,17 @@ mal_result mal_device_init_ex(const mal_backend backends[], mal_uint32 backendCo
         return MAL_OUT_OF_MEMORY;
     }
 
-    if (backends == NULL) {
-        backends = g_malDefaultBackends;
-        backendCount = mal_countof(g_malDefaultBackends);
+    mal_backend* pBackendsToIterate = (mal_backend*)backends;
+    mal_uint32 backendsToIterateCount = backendCount;
+    if (pBackendsToIterate == NULL) {
+        pBackendsToIterate = (mal_backend*)g_malDefaultBackends;
+        backendsToIterateCount = mal_countof(g_malDefaultBackends);
     }
 
     mal_result result = MAL_NO_BACKEND;
 
-    for (mal_uint32 iBackend = 0; iBackend < backendCount; ++iBackend) {
-        result = mal_context_init(&backends[iBackend], 1, pContextConfig, pContext);
+    for (mal_uint32 iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
+        result = mal_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext);
         if (result == MAL_SUCCESS) {
             result = mal_device_init(pContext, type, pDeviceID, pConfig, pUserData, pDevice);
             if (result == MAL_SUCCESS) {
@@ -15367,14 +16609,18 @@ mal_device_config mal_device_config_init_ex(mal_format format, mal_uint32 channe
     config.onRecvCallback = onRecvCallback;
     config.onSendCallback = onSendCallback;
 
-    if (channelMap == NULL) {
-        if (channels > 8) {
-            mal_zero_memory(channelMap, sizeof(mal_channel)*MAL_MAX_CHANNELS);
+    if (channels > 0) {
+        if (channelMap == NULL) {
+            if (channels > 8) {
+                mal_zero_memory(config.channelMap, sizeof(mal_channel)*MAL_MAX_CHANNELS);
+            } else {
+                mal_get_standard_channel_map(mal_standard_channel_map_default, channels, config.channelMap);
+            }
         } else {
-            mal_get_standard_channel_map(mal_standard_channel_map_default, channels, config.channelMap);
+            mal_copy_memory(config.channelMap, channelMap, sizeof(config.channelMap));
         }
     } else {
-        mal_copy_memory(config.channelMap, channelMap, sizeof(config.channelMap));
+        mal_zero_memory(config.channelMap, sizeof(mal_channel)*MAL_MAX_CHANNELS);
     }
 
     return config;
@@ -15812,7 +17058,7 @@ void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, m
     }
 }
 
-void mal_channel_map_copy(mal_channel* pOut, mal_channel* pIn, mal_uint32 channels)
+void mal_channel_map_copy(mal_channel* pOut, const mal_channel* pIn, mal_uint32 channels)
 {
     if (pOut != NULL && pIn != NULL && channels > 0) {
         mal_copy_memory(pOut, pIn, sizeof(*pOut) * channels);
@@ -16195,17 +17441,34 @@ void mal_pcm_deinterleave_u8(void** dst, const void* src, mal_uint64 frameCount,
 // s16
 void mal_pcm_s16_to_u8__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
 {
-    (void)ditherMode;
-
     mal_uint8* dst_u8 = (mal_uint8*)dst;
     const mal_int16* src_s16 = (const mal_int16*)src;
 
-    mal_uint64 i;
-    for (i = 0; i < count; i += 1) {
-        mal_int16 x = src_s16[i];
-        x = x >> 8;
-        x = x + 128;
-        dst_u8[i] = (mal_uint8)x;
+    if (ditherMode == mal_dither_mode_none) {
+        mal_uint64 i;
+        for (i = 0; i < count; i += 1) {
+            mal_int16 x = src_s16[i];
+            x = x >> 8;
+            x = x + 128;
+            dst_u8[i] = (mal_uint8)x;
+        }
+    } else {
+        mal_uint64 i;
+        for (i = 0; i < count; i += 1) {
+            mal_int16 x = src_s16[i];
+
+            // Dither. Don't overflow.
+            mal_int32 dither = mal_dither_s32(ditherMode, -0x80, 0x7F);
+            if ((x + dither) <= 0x7FFF) {
+                x = (mal_int16)(x + dither);
+            } else {
+                x = 0x7FFF;
+            }
+
+            x = x >> 8;
+            x = x + 128;
+            dst_u8[i] = (mal_uint8)x;
+        }
     }
 }
 
@@ -16434,15 +17697,32 @@ void mal_pcm_deinterleave_s16(void** dst, const void* src, mal_uint64 frameCount
 // s24
 void mal_pcm_s24_to_u8__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
 {
-    (void)ditherMode;
-
     mal_uint8* dst_u8 = (mal_uint8*)dst;
     const mal_uint8* src_s24 = (const mal_uint8*)src;
 
-    mal_uint64 i;
-    for (i = 0; i < count; i += 1) {
-        mal_int8 x = (mal_int8)src_s24[i*3 + 2] + 128;
-        dst_u8[i] = (mal_uint8)x;
+    if (ditherMode == mal_dither_mode_none) {
+        mal_uint64 i;
+        for (i = 0; i < count; i += 1) {
+            mal_int8 x = (mal_int8)src_s24[i*3 + 2] + 128;
+            dst_u8[i] = (mal_uint8)x;
+        }
+    } else {
+        mal_uint64 i;
+        for (i = 0; i < count; i += 1) {
+            mal_int32 x = (mal_int32)(((mal_uint32)(src_s24[i*3+0]) << 8) | ((mal_uint32)(src_s24[i*3+1]) << 16) | ((mal_uint32)(src_s24[i*3+2])) << 24);
+
+            // Dither. Don't overflow.
+            mal_int32 dither = mal_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
+            if ((mal_int64)x + dither <= 0x7FFFFFFF) {
+                x = x + dither;
+            } else {
+                x = 0x7FFFFFFF;
+            }
+            
+            x = x >> 24;
+            x = x + 128;
+            dst_u8[i] = (mal_uint8)x;
+        }
     }
 }
 
@@ -16474,16 +17754,32 @@ void mal_pcm_s24_to_u8(void* dst, const void* src, mal_uint64 count, mal_dither_
 
 void mal_pcm_s24_to_s16__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
 {
-    (void)ditherMode;
-
     mal_int16* dst_s16 = (mal_int16*)dst;
     const mal_uint8* src_s24 = (const mal_uint8*)src;
 
-    mal_uint64 i;
-    for (i = 0; i < count; i += 1) {
-        mal_uint16 dst_lo = ((mal_uint16)src_s24[i*3 + 1]);
-        mal_uint16 dst_hi = ((mal_uint16)src_s24[i*3 + 2]) << 8;
-        dst_s16[i] = (mal_int16)dst_lo | dst_hi;
+    if (ditherMode == mal_dither_mode_none) {
+        mal_uint64 i;
+        for (i = 0; i < count; i += 1) {
+            mal_uint16 dst_lo = ((mal_uint16)src_s24[i*3 + 1]);
+            mal_uint16 dst_hi = ((mal_uint16)src_s24[i*3 + 2]) << 8;
+            dst_s16[i] = (mal_int16)dst_lo | dst_hi;
+        }
+    } else {
+        mal_uint64 i;
+        for (i = 0; i < count; i += 1) {
+            mal_int32 x = (mal_int32)(((mal_uint32)(src_s24[i*3+0]) << 8) | ((mal_uint32)(src_s24[i*3+1]) << 16) | ((mal_uint32)(src_s24[i*3+2])) << 24);
+
+            // Dither. Don't overflow.
+            mal_int32 dither = mal_dither_s32(ditherMode, -0x8000, 0x7FFF);
+            if ((mal_int64)x + dither <= 0x7FFFFFFF) {
+                x = x + dither;
+            } else {
+                x = 0x7FFFFFFF;
+            }
+
+            x = x >> 16;
+            dst_s16[i] = (mal_int16)x;
+        }
     }
 }
 
@@ -16677,17 +17973,34 @@ void mal_pcm_deinterleave_s24(void** dst, const void* src, mal_uint64 frameCount
 // s32
 void mal_pcm_s32_to_u8__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
 {
-    (void)ditherMode;
-
     mal_uint8* dst_u8 = (mal_uint8*)dst;
     const mal_int32* src_s32 = (const mal_int32*)src;
 
-    mal_uint64 i;
-    for (i = 0; i < count; i += 1) {
-        mal_int32 x = src_s32[i];
-        x = x >> 24;
-        x = x + 128;
-        dst_u8[i] = (mal_uint8)x;
+    if (ditherMode == mal_dither_mode_none) {
+        mal_uint64 i;
+        for (i = 0; i < count; i += 1) {
+            mal_int32 x = src_s32[i];
+            x = x >> 24;
+            x = x + 128;
+            dst_u8[i] = (mal_uint8)x;
+        }
+    } else {
+        mal_uint64 i;
+        for (i = 0; i < count; i += 1) {
+            mal_int32 x = src_s32[i];
+
+            // Dither. Don't overflow.
+            mal_int32 dither = mal_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
+            if ((mal_int64)x + dither <= 0x7FFFFFFF) {
+                x = x + dither;
+            } else {
+                x = 0x7FFFFFFF;
+            }
+            
+            x = x >> 24;
+            x = x + 128;
+            dst_u8[i] = (mal_uint8)x;
+        }
     }
 }
 
@@ -16719,16 +18032,32 @@ void mal_pcm_s32_to_u8(void* dst, const void* src, mal_uint64 count, mal_dither_
 
 void mal_pcm_s32_to_s16__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
 {
-    (void)ditherMode;
-
     mal_int16* dst_s16 = (mal_int16*)dst;
     const mal_int32* src_s32 = (const mal_int32*)src;
 
-    mal_uint64 i;
-    for (i = 0; i < count; i += 1) {
-        mal_int32 x = src_s32[i];
-        x = x >> 16;
-        dst_s16[i] = (mal_int16)x;
+    if (ditherMode == mal_dither_mode_none) {
+        mal_uint64 i;
+        for (i = 0; i < count; i += 1) {
+            mal_int32 x = src_s32[i];
+            x = x >> 16;
+            dst_s16[i] = (mal_int16)x;
+        }
+    } else {
+        mal_uint64 i;
+        for (i = 0; i < count; i += 1) {
+            mal_int32 x = src_s32[i];
+
+            // Dither. Don't overflow.
+            mal_int32 dither = mal_dither_s32(ditherMode, -0x8000, 0x7FFF);
+            if ((mal_int64)x + dither <= 0x7FFFFFFF) {
+                x = x + dither;
+            } else {
+                x = 0x7FFFFFFF;
+            }
+            
+            x = x >> 16;
+            dst_s16[i] = (mal_int16)x;
+        }
     }
 }
 
@@ -16760,7 +18089,7 @@ void mal_pcm_s32_to_s16(void* dst, const void* src, mal_uint64 count, mal_dither
 
 void mal_pcm_s32_to_s24__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
 {
-    (void)ditherMode;
+    (void)ditherMode;   // No dithering for s32 -> s24.
 
     mal_uint8* dst_s24 = (mal_uint8*)dst;
     const mal_int32* src_s32 = (const mal_int32*)src;
@@ -16810,7 +18139,7 @@ void mal_pcm_s32_to_s32(void* dst, const void* src, mal_uint64 count, mal_dither
 
 void mal_pcm_s32_to_f32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
 {
-    (void)ditherMode;
+    (void)ditherMode;   // No dithering for s32 -> f32.
 
     float* dst_f32 = (float*)dst;
     const mal_int32* src_s32 = (const mal_int32*)src;
@@ -16918,14 +18247,20 @@ void mal_pcm_deinterleave_s32(void** dst, const void* src, mal_uint64 frameCount
 // f32
 void mal_pcm_f32_to_u8__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
 {
-    (void)ditherMode;
-
     mal_uint8* dst_u8 = (mal_uint8*)dst;
     const float* src_f32 = (const float*)src;
 
+    float ditherMin = 0;
+    float ditherMax = 0;
+    if (ditherMode != mal_dither_mode_none) {
+        ditherMin = 1.0f / -128;
+        ditherMax = 1.0f /  127;
+    }
+
     mal_uint64 i;
     for (i = 0; i < count; i += 1) {
         float x = src_f32[i];
+        x = x + mal_dither_f32(ditherMode, ditherMin, ditherMax);
         x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));    // clip
         x = x + 1;                                  // -1..1 to 0..2
         x = x * 127.5f;                             // 0..2 to 0..255
@@ -16962,14 +18297,20 @@ void mal_pcm_f32_to_u8(void* dst, const void* src, mal_uint64 count, mal_dither_
 
 void mal_pcm_f32_to_s16__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
 {
-    (void)ditherMode;
-
     mal_int16* dst_s16 = (mal_int16*)dst;
     const float* src_f32 = (const float*)src;
 
+    float ditherMin = 0;
+    float ditherMax = 0;
+    if (ditherMode != mal_dither_mode_none) {
+        ditherMin = 1.0f / -32768;
+        ditherMax = 1.0f /  32767;
+    }
+
     mal_uint64 i;
     for (i = 0; i < count; i += 1) {
         float x = src_f32[i];
+        x = x + mal_dither_f32(ditherMode, ditherMin, ditherMax);
         x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));    // clip
 
 #if 0
@@ -17014,7 +18355,7 @@ void mal_pcm_f32_to_s16(void* dst, const void* src, mal_uint64 count, mal_dither
 
 void mal_pcm_f32_to_s24__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
 {
-    (void)ditherMode;
+    (void)ditherMode;   // No dithering for f32 -> s24.
 
     mal_uint8* dst_s24 = (mal_uint8*)dst;
     const float* src_f32 = (const float*)src;
@@ -17069,7 +18410,7 @@ void mal_pcm_f32_to_s24(void* dst, const void* src, mal_uint64 count, mal_dither
 
 void mal_pcm_f32_to_s32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
 {
-    (void)ditherMode;
+    (void)ditherMode;   // No dithering for f32 -> s32.
 
     mal_int32* dst_s32 = (mal_int32*)dst;
     const float* src_f32 = (const float*)src;
@@ -17563,6 +18904,36 @@ mal_uint64 mal_format_converter_read_deinterleaved(mal_format_converter* pConver
 }
 
 
+mal_format_converter_config mal_format_converter_config_init_new()
+{
+    mal_format_converter_config config;
+    mal_zero_object(&config);
+
+    return config;
+}
+
+mal_format_converter_config mal_format_converter_config_init(mal_format formatIn, mal_format formatOut, mal_uint32 channels, mal_format_converter_read_proc onRead, void* pUserData)
+{
+    mal_format_converter_config config = mal_format_converter_config_init_new();
+    config.formatIn = formatIn;
+    config.formatOut = formatOut;
+    config.channels = channels;
+    config.onRead = onRead;
+    config.onReadDeinterleaved = NULL;
+    config.pUserData = pUserData;
+
+    return config;
+}
+
+mal_format_converter_config mal_format_converter_config_init_deinterleaved(mal_format formatIn, mal_format formatOut, mal_uint32 channels, mal_format_converter_read_deinterleaved_proc onReadDeinterleaved, void* pUserData)
+{
+    mal_format_converter_config config = mal_format_converter_config_init(formatIn, formatOut, channels, NULL, pUserData);
+    config.onReadDeinterleaved = onReadDeinterleaved;
+
+    return config;
+}
+
+
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 //
@@ -18037,7 +19408,21 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram
         for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
             for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
                 mal_uint64 iFrame = 0;
+#if defined(MAL_SUPPORT_NEON)
+                if (mal_channel_router__can_use_neon(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
+                    float32x4_t weight = vmovq_n_f32(pRouter->weights[iChannelIn][iChannelOut]);
+
+                    mal_uint64 frameCount4 = frameCount/4;
+                    for (mal_uint64 iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) {
+                        float32x4_t* pO = (float32x4_t*)ppSamplesOut[iChannelOut] + iFrame4;
+                        float32x4_t* pI = (float32x4_t*)ppSamplesIn [iChannelIn ] + iFrame4;
+                        *pO = vaddq_f32(*pO, vmulq_f32(*pI, weight));
+                    }
 
+                    iFrame += frameCount4*4;
+                }
+                else
+#endif
 #if defined(MAL_SUPPORT_AVX512)
                 if (mal_channel_router__can_use_avx512(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
                     __m512 weight = _mm512_set1_ps(pRouter->weights[iChannelIn][iChannelOut]);
@@ -18219,6 +19604,21 @@ mal_channel_router_config mal_channel_router_config_init(mal_uint32 channelsIn,
 //
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
+#define mal_floorf(x) ((float)floor((double)(x)))
+#define mal_sinf(x)   ((float)sin((double)(x)))
+#define mal_cosf(x)   ((float)cos((double)(x)))
+
+static MAL_INLINE double mal_sinc(double x)
+{
+    if (x != 0) {
+        return sin(MAL_PI_D*x) / (MAL_PI_D*x);
+    } else {
+        return 1;
+    }
+}
+
+#define mal_sincf(x) ((float)mal_sinc((double)(x)))
+
 mal_uint64 mal_calculate_frame_count_after_src(mal_uint32 sampleRateOut, mal_uint32 sampleRateIn, mal_uint64 frameCountIn)
 {
     double srcRatio = (double)sampleRateOut / sampleRateIn;
@@ -18235,8 +19635,40 @@ mal_uint64 mal_calculate_frame_count_after_src(mal_uint32 sampleRateOut, mal_uin
 }
 
 
-mal_uint64 mal_src_read_deinterleaved__passthrough(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, mal_bool32 flush, void* pUserData);
-mal_uint64 mal_src_read_deinterleaved__linear(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, mal_bool32 flush, void* pUserData);
+mal_uint64 mal_src_read_deinterleaved__passthrough(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData);
+mal_uint64 mal_src_read_deinterleaved__linear(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData);
+mal_uint64 mal_src_read_deinterleaved__sinc(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData);
+
+void mal_src__build_sinc_table__sinc(mal_src* pSRC)
+{
+    mal_assert(pSRC != NULL);
+
+    pSRC->sinc.table[0] = 1.0f;
+    for (int i = 1; i < mal_countof(pSRC->sinc.table); i += 1) {
+        double x = i*MAL_PI_D / MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION;
+        pSRC->sinc.table[i] = (float)(sin(x)/x);
+    }
+}
+
+void mal_src__build_sinc_table__rectangular(mal_src* pSRC)
+{
+    // This is the same as the base sinc table.
+    mal_src__build_sinc_table__sinc(pSRC);
+}
+
+void mal_src__build_sinc_table__hann(mal_src* pSRC)
+{
+    mal_src__build_sinc_table__sinc(pSRC);
+
+    for (int i = 0; i < mal_countof(pSRC->sinc.table); i += 1) {
+        double x = pSRC->sinc.table[i];
+        double N = MAL_SRC_SINC_MAX_WINDOW_WIDTH*2;
+        double n = ((double)(i) / MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION) + MAL_SRC_SINC_MAX_WINDOW_WIDTH;
+        double w = 0.5 * (1 - cos((2*MAL_PI_D*n) / (N)));
+
+        pSRC->sinc.table[i] = (float)(x * w);
+    }
+}
 
 mal_result mal_src_init(const mal_src_config* pConfig, mal_src* pSRC)
 {
@@ -18255,6 +19687,26 @@ mal_result mal_src_init(const mal_src_config* pConfig, mal_src* pSRC)
 
     pSRC->config = *pConfig;
 
+    if (pSRC->config.algorithm == mal_src_algorithm_sinc) {
+        // Make sure the window width within bounds.
+        if (pSRC->config.sinc.windowWidth == 0) {
+            pSRC->config.sinc.windowWidth = MAL_SRC_SINC_DEFAULT_WINDOW_WIDTH;
+        }
+        if (pSRC->config.sinc.windowWidth < MAL_SRC_SINC_MIN_WINDOW_WIDTH) {
+            pSRC->config.sinc.windowWidth = MAL_SRC_SINC_MIN_WINDOW_WIDTH;
+        }
+        if (pSRC->config.sinc.windowWidth > MAL_SRC_SINC_MAX_WINDOW_WIDTH) {
+            pSRC->config.sinc.windowWidth = MAL_SRC_SINC_MAX_WINDOW_WIDTH;
+        }
+
+        // Set up the lookup table.
+        switch (pSRC->config.sinc.windowFunction) {
+            case mal_src_sinc_window_function_hann:        mal_src__build_sinc_table__hann(pSRC);        break;
+            case mal_src_sinc_window_function_rectangular: mal_src__build_sinc_table__rectangular(pSRC); break;
+            default: return MAL_INVALID_ARGS;   // <-- Hitting this means the window function is unknown to mini_al.
+        }
+    }
+
     return MAL_SUCCESS;
 }
 
@@ -18289,11 +19741,6 @@ mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOu
 }
 
 mal_uint64 mal_src_read_deinterleaved(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData)
-{
-    return mal_src_read_deinterleaved_ex(pSRC, frameCount, ppSamplesOut, MAL_FALSE, pUserData);
-}
-
-mal_uint64 mal_src_read_deinterleaved_ex(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, mal_bool32 flush, void* pUserData)
 {
     if (pSRC == NULL || frameCount == 0 || ppSamplesOut == NULL) {
         return 0;
@@ -18301,13 +19748,14 @@ mal_uint64 mal_src_read_deinterleaved_ex(mal_src* pSRC, mal_uint64 frameCount, v
 
     mal_src_algorithm algorithm = pSRC->config.algorithm;
     if (pSRC->config.sampleRateIn == pSRC->config.sampleRateOut) {
-        algorithm = mal_src_algorithm_none;
+        //algorithm = mal_src_algorithm_none;
     }
 
     // Can use a function pointer for this.
     switch (algorithm) {
-        case mal_src_algorithm_none:   return mal_src_read_deinterleaved__passthrough(pSRC, frameCount, ppSamplesOut, flush, pUserData);
-        case mal_src_algorithm_linear: return mal_src_read_deinterleaved__linear(     pSRC, frameCount, ppSamplesOut, flush, pUserData);
+        case mal_src_algorithm_none:   return mal_src_read_deinterleaved__passthrough(pSRC, frameCount, ppSamplesOut, pUserData);
+        case mal_src_algorithm_linear: return mal_src_read_deinterleaved__linear(     pSRC, frameCount, ppSamplesOut, pUserData);
+        case mal_src_algorithm_sinc:   return mal_src_read_deinterleaved__sinc(       pSRC, frameCount, ppSamplesOut, pUserData);
         default: break;
     }
 
@@ -18315,10 +19763,8 @@ mal_uint64 mal_src_read_deinterleaved_ex(mal_src* pSRC, mal_uint64 frameCount, v
     return 0;
 }
 
-mal_uint64 mal_src_read_deinterleaved__passthrough(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, mal_bool32 flush, void* pUserData)
+mal_uint64 mal_src_read_deinterleaved__passthrough(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData)
 {
-    (void)flush;    // Passthrough need not care about flushing.
-
     if (frameCount <= 0xFFFFFFFF) {
         return pSRC->config.onReadDeinterleaved(pSRC, (mal_uint32)frameCount, ppSamplesOut, pUserData);
     } else {
@@ -18354,10 +19800,8 @@ mal_uint64 mal_src_read_deinterleaved__passthrough(mal_src* pSRC, mal_uint64 fra
     }
 }
 
-mal_uint64 mal_src_read_deinterleaved__linear(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, mal_bool32 flush, void* pUserData)
+mal_uint64 mal_src_read_deinterleaved__linear(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData)
 {
-    (void)flush;    // No flushing at the moment.
-
     mal_assert(pSRC != NULL);
     mal_assert(frameCount > 0);
     mal_assert(ppSamplesOut != NULL);
@@ -18368,7 +19812,7 @@ mal_uint64 mal_src_read_deinterleaved__linear(mal_src* pSRC, mal_uint64 frameCou
 
     float factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut;
 
-    mal_uint32 maxFrameCountPerChunkIn = mal_countof(pSRC->samplesFromClient[0]);
+    mal_uint32 maxFrameCountPerChunkIn = mal_countof(pSRC->linear.input[0]);
 
     mal_uint64 totalFramesRead = 0;
     while (totalFramesRead < frameCount) {
@@ -18381,7 +19825,7 @@ mal_uint64 mal_src_read_deinterleaved__linear(mal_src* pSRC, mal_uint64 frameCou
 
         // Read Input Data
         // ===============
-        float tBeg = pSRC->linear.t;
+        float tBeg = pSRC->linear.timeIn;
         float tEnd = tBeg + (framesToRead*factor);
 
         mal_uint32 framesToReadFromClient = (mal_uint32)(tEnd) + 1 + 1;   // +1 to make tEnd 1-based and +1 because we always need to an extra sample for interpolation.
@@ -18391,7 +19835,7 @@ mal_uint64 mal_src_read_deinterleaved__linear(mal_src* pSRC, mal_uint64 frameCou
 
         float* ppSamplesFromClient[MAL_MAX_CHANNELS];
         for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) {
-            ppSamplesFromClient[iChannel] = pSRC->samplesFromClient[iChannel] + pSRC->linear.leftoverFrames;
+            ppSamplesFromClient[iChannel] = pSRC->linear.input[iChannel] + pSRC->linear.leftoverFrames;
         }
         
         mal_uint32 framesReadFromClient = 0;
@@ -18405,7 +19849,7 @@ mal_uint64 mal_src_read_deinterleaved__linear(mal_src* pSRC, mal_uint64 frameCou
         }
 
         for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) {
-            ppSamplesFromClient[iChannel] = pSRC->samplesFromClient[iChannel];
+            ppSamplesFromClient[iChannel] = pSRC->linear.input[iChannel];
         }
 
 
@@ -18427,10 +19871,10 @@ mal_uint64 mal_src_read_deinterleaved__linear(mal_src* pSRC, mal_uint64 frameCou
         // Output frames are always read in groups of 4 because I'm planning on using this as a reference for some SIMD-y stuff later.
         mal_uint32 maxOutputFramesToRead4 = maxOutputFramesToRead/4;
         for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) {
-            float t0 = pSRC->linear.t + factor*0;
-            float t1 = pSRC->linear.t + factor*1;
-            float t2 = pSRC->linear.t + factor*2;
-            float t3 = pSRC->linear.t + factor*3;
+            float t0 = pSRC->linear.timeIn + factor*0;
+            float t1 = pSRC->linear.timeIn + factor*1;
+            float t2 = pSRC->linear.timeIn + factor*2;
+            float t3 = pSRC->linear.timeIn + factor*3;
 
             for (mal_uint32 iFrameOut = 0; iFrameOut < maxOutputFramesToRead4; iFrameOut += 1) {
                 float iPrevSample0 = (float)floor(t0);
@@ -18458,10 +19902,10 @@ mal_uint64 mal_src_read_deinterleaved__linear(mal_src* pSRC, mal_uint64 frameCou
                 float nextSample2 = ppSamplesFromClient[iChannel][(mal_uint32)iNextSample2];
                 float nextSample3 = ppSamplesFromClient[iChannel][(mal_uint32)iNextSample3];
 
-                ppNextSamplesOut[iChannel][iFrameOut*4 + 0] = mal_mix_f32(prevSample0, nextSample0, alpha0);
-                ppNextSamplesOut[iChannel][iFrameOut*4 + 1] = mal_mix_f32(prevSample1, nextSample1, alpha1);
-                ppNextSamplesOut[iChannel][iFrameOut*4 + 2] = mal_mix_f32(prevSample2, nextSample2, alpha2);
-                ppNextSamplesOut[iChannel][iFrameOut*4 + 3] = mal_mix_f32(prevSample3, nextSample3, alpha3);
+                ppNextSamplesOut[iChannel][iFrameOut*4 + 0] = mal_mix_f32_fast(prevSample0, nextSample0, alpha0);
+                ppNextSamplesOut[iChannel][iFrameOut*4 + 1] = mal_mix_f32_fast(prevSample1, nextSample1, alpha1);
+                ppNextSamplesOut[iChannel][iFrameOut*4 + 2] = mal_mix_f32_fast(prevSample2, nextSample2, alpha2);
+                ppNextSamplesOut[iChannel][iFrameOut*4 + 3] = mal_mix_f32_fast(prevSample3, nextSample3, alpha3);
 
                 t0 += factor*4;
                 t1 += factor*4;
@@ -18469,7 +19913,7 @@ mal_uint64 mal_src_read_deinterleaved__linear(mal_src* pSRC, mal_uint64 frameCou
                 t3 += factor*4;
             }
 
-            float t = pSRC->linear.t + (factor*maxOutputFramesToRead4*4);
+            float t = pSRC->linear.timeIn + (factor*maxOutputFramesToRead4*4);
             for (mal_uint32 iFrameOut = (maxOutputFramesToRead4*4); iFrameOut < maxOutputFramesToRead; iFrameOut += 1) {
                 float iPrevSample = (float)floor(t);
                 float iNextSample = iPrevSample + 1;
@@ -18478,7 +19922,7 @@ mal_uint64 mal_src_read_deinterleaved__linear(mal_src* pSRC, mal_uint64 frameCou
                 float prevSample = ppSamplesFromClient[iChannel][(mal_uint32)iPrevSample];
                 float nextSample = ppSamplesFromClient[iChannel][(mal_uint32)iNextSample];
 
-                ppNextSamplesOut[iChannel][iFrameOut] = mal_mix_f32(prevSample, nextSample, alpha);
+                ppNextSamplesOut[iChannel][iFrameOut] = mal_mix_f32_fast(prevSample, nextSample, alpha);
 
                 t += factor;
             }
@@ -18491,30 +19935,235 @@ mal_uint64 mal_src_read_deinterleaved__linear(mal_src* pSRC, mal_uint64 frameCou
 
         // Residual
         // ========
-        float tNext = pSRC->linear.t + (maxOutputFramesToRead*factor);
+        float tNext = pSRC->linear.timeIn + (maxOutputFramesToRead*factor);
 
-        pSRC->linear.t = tNext;
+        pSRC->linear.timeIn = tNext;
         mal_assert(tNext <= framesReadFromClient+1);
 
         mal_uint32 iNextFrame = (mal_uint32)floor(tNext);
         pSRC->linear.leftoverFrames = framesReadFromClient - iNextFrame;
-        pSRC->linear.t = tNext - iNextFrame;
+        pSRC->linear.timeIn = tNext - iNextFrame;
 
         for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) {
             for (mal_uint32 iFrame = 0; iFrame < pSRC->linear.leftoverFrames; ++iFrame) {
                 float sample = ppSamplesFromClient[iChannel][framesReadFromClient-pSRC->linear.leftoverFrames + iFrame];
                 ppSamplesFromClient[iChannel][iFrame] = sample;
             }
-        }
+        }
+
+        
+        // Exit the loop if we've found everything from the client.
+        if (framesReadFromClient < framesToReadFromClient) {
+            break;
+        }
+    }
+
+    return totalFramesRead;
+}
+
+
+mal_src_config mal_src_config_init_new()
+{
+    mal_src_config config;
+    mal_zero_object(&config);
+
+    return config;
+}
+
+mal_src_config mal_src_config_init(mal_uint32 sampleRateIn, mal_uint32 sampleRateOut, mal_uint32 channels, mal_src_read_deinterleaved_proc onReadDeinterleaved, void* pUserData)
+{
+    mal_src_config config = mal_src_config_init_new();
+    config.sampleRateIn = sampleRateIn;
+    config.sampleRateOut = sampleRateOut;
+    config.channels = channels;
+    config.onReadDeinterleaved = onReadDeinterleaved;
+    config.pUserData = pUserData;
+
+    return config;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Sinc Sample Rate Conversion
+// ===========================
+//
+// The sinc SRC algorithm uses a windowed sinc to perform interpolation of samples. Currently, mini_al's implementation supports rectangular and Hann window
+// methods.
+//
+// Whenever an output sample is being computed, it looks at a sub-section of the input samples. I've called this sub-section in the code below the "window",
+// which I realize is a bit ambigous with the mathematical "window", but it works for me when I need to conceptualize things in my head. The window is made up
+// of two halves. The first half contains past input samples (initialized to zero), and the second half contains future input samples. As time moves forward
+// and input samples are consumed, the window moves forward. The larger the window, the better the quality at the expense of slower processing. The window is
+// limited the range [MAL_SRC_SINC_MIN_WINDOW_WIDTH, MAL_SRC_SINC_MAX_WINDOW_WIDTH] and defaults to MAL_SRC_SINC_DEFAULT_WINDOW_WIDTH.
+//
+// Input samples are cached for efficiency (to prevent frequently requesting tiny numbers of samples from the client). When the window gets to the end of the
+// cache, it's moved back to the start, and more samples are read from the client. If the client has no more data to give, the cache is filled with zeros and
+// the last of the input samples will be consumed. Once the last of the input samples have been consumed, no more samples will be output.
+//
+//
+// When reading output samples, we always first read whatever is already in the input cache. Only when the cache has been fully consumed do we read more data
+// from the client.
+//
+// To access samples in the input buffer you do so relative to the window. When the window itself is at position 0, the first item in the buffer is accessed
+// with "windowPos + windowWidth". Generally, to access any sample relative to the window you do "windowPos + windowWidth + sampleIndexRelativeToWindow".
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Retrieves a sample from the input buffer's window. Values >= 0 retrieve future samples. Negative values return past samples.
+static MAL_INLINE float mal_src_sinc__get_input_sample_from_window(const mal_src* pSRC, mal_uint32 channel, mal_uint32 windowPosInSamples, mal_int32 sampleIndex)
+{
+    mal_assert(pSRC != NULL);
+    mal_assert(channel < pSRC->config.channels);
+    mal_assert(sampleIndex >= -(mal_int32)pSRC->config.sinc.windowWidth);
+    mal_assert(sampleIndex <   (mal_int32)pSRC->config.sinc.windowWidth);
+
+    // The window should always be contained within the input cache.
+    mal_assert(windowPosInSamples >= 0);
+    mal_assert(windowPosInSamples <  mal_countof(pSRC->sinc.input[0]) - pSRC->config.sinc.windowWidth);
+    
+    return pSRC->sinc.input[channel][windowPosInSamples + pSRC->config.sinc.windowWidth + sampleIndex];
+}
+
+static MAL_INLINE float mal_src_sinc__interpolation_factor(const mal_src* pSRC, float x)
+{
+    mal_assert(pSRC != NULL);
+
+    float xabs = (float)fabs(x);
+    if (xabs >= MAL_SRC_SINC_MAX_WINDOW_WIDTH /*pSRC->config.sinc.windowWidth*/) {
+        return 0;
+    }
+
+    xabs = xabs * MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION;
+    mal_int32 ixabs = (mal_int32)xabs;
+
+#if 1
+    float a = xabs - ixabs;
+    return mal_mix_f32_fast(pSRC->sinc.table[ixabs], pSRC->sinc.table[ixabs+1], a);
+#else
+    return pSRC->sinc.table[ixabs];
+#endif
+}
+
+mal_uint64 mal_src_read_deinterleaved__sinc(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData)
+{
+    mal_assert(pSRC != NULL);
+    mal_assert(frameCount > 0);
+    mal_assert(ppSamplesOut != NULL);
+
+    float factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut;
+    float inverseFactor = 1/factor;
+
+    mal_int32 windowWidth  = (mal_int32)pSRC->config.sinc.windowWidth;
+    mal_int32 windowWidth2 = windowWidth*2;
+
+    float* ppNextSamplesOut[MAL_MAX_CHANNELS];
+    mal_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(void*) * pSRC->config.channels);
+
+    mal_uint64 totalOutputFramesRead = 0;
+    while (totalOutputFramesRead < frameCount) {
+        // The maximum number of frames we can read this iteration depends on how many input samples we have available to us. This is the number
+        // of input samples between the end of the window and the end of the cache.
+        mal_uint32 maxInputSamplesAvailableInCache = mal_countof(pSRC->sinc.input[0]) - (pSRC->config.sinc.windowWidth*2) - pSRC->sinc.windowPosInSamples;
+        if (maxInputSamplesAvailableInCache > pSRC->sinc.inputFrameCount) {
+            maxInputSamplesAvailableInCache = pSRC->sinc.inputFrameCount;
+        }
+
+        float timeInBeg = pSRC->sinc.timeIn;
+        float timeInEnd = (float)(pSRC->sinc.windowPosInSamples + maxInputSamplesAvailableInCache);
+
+        mal_assert(timeInBeg >= 0);
+        mal_assert(timeInBeg <= timeInEnd);
+
+        mal_uint64 maxOutputFramesToRead = (mal_uint64)(((timeInEnd - timeInBeg) * inverseFactor));
+
+        mal_uint64 outputFramesRemaining = frameCount - totalOutputFramesRead;
+        mal_uint64 outputFramesToRead = outputFramesRemaining;
+        if (outputFramesToRead > maxOutputFramesToRead) {
+            outputFramesToRead = maxOutputFramesToRead;
+        }
+
+        for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) {
+            // Do SRC.
+            float timeIn = timeInBeg;
+            for (mal_uint32 iSample = 0; iSample < outputFramesToRead; iSample += 1) {
+                mal_int32 iTimeIn  = (mal_int32)timeIn;
+
+                float sampleOut = 0;
+                for (mal_int32 iWindow = -windowWidth+1; iWindow < windowWidth; iWindow += 1) {
+                    float t = (timeIn - iTimeIn);
+                    float w = (float)(iWindow);
+
+                    float a = mal_src_sinc__interpolation_factor(pSRC, (t - w));
+                    float s = mal_src_sinc__get_input_sample_from_window(pSRC, iChannel, iTimeIn, iWindow);
+
+                    sampleOut += s * a;
+                }
+
+                ppNextSamplesOut[iChannel][iSample] = (float)sampleOut;
+
+                timeIn += factor;
+            }
+
+            ppNextSamplesOut[iChannel] += outputFramesToRead;
+        }
+
+        mal_uint32 prevWindowPosInSamples = pSRC->sinc.windowPosInSamples;
+
+        pSRC->sinc.timeIn            += (outputFramesToRead * factor);
+        pSRC->sinc.windowPosInSamples = (mal_uint32)pSRC->sinc.timeIn;
+        pSRC->sinc.inputFrameCount   -= pSRC->sinc.windowPosInSamples - prevWindowPosInSamples;
+
+        // If the window has reached a point where we cannot read a whole output sample it needs to be moved back to the start.
+        mal_uint32 availableOutputFrames = (mal_uint32)((timeInEnd - pSRC->sinc.timeIn) * inverseFactor);
+
+        if (availableOutputFrames == 0) {
+            size_t samplesToMove = mal_countof(pSRC->sinc.input[0]) - pSRC->sinc.windowPosInSamples;
+
+            pSRC->sinc.timeIn            -= mal_floorf(pSRC->sinc.timeIn);
+            pSRC->sinc.windowPosInSamples = 0;
+
+            // Move everything from the end of the cache up to the front.
+            for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) {
+                memmove(pSRC->sinc.input[iChannel], pSRC->sinc.input[iChannel] + mal_countof(pSRC->sinc.input[iChannel]) - samplesToMove, samplesToMove * sizeof(*pSRC->sinc.input[iChannel]));
+            }
+        }
+
+        // Read more data from the client if required.
+        if (pSRC->sinc.inputFrameCount < pSRC->config.sinc.windowWidth || pSRC->sinc.windowPosInSamples == 0) {
+            float* ppInputDst[MAL_MAX_CHANNELS] = {0};
+            for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) {
+                ppInputDst[iChannel] = pSRC->sinc.input[iChannel] + pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount;
+            }
+
+            // Now read data from the client.
+            mal_uint32 framesToReadFromClient = mal_countof(pSRC->sinc.input[0]) - (pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount);
+            if (framesToReadFromClient > 0) {
+                mal_uint32 framesReadFromClient = pSRC->config.onReadDeinterleaved(pSRC, framesToReadFromClient, (void**)ppInputDst, pUserData);
+                if (framesReadFromClient != 0) {
+                    pSRC->sinc.inputFrameCount += framesReadFromClient;
+                } else {
+                    // We couldn't get anything more from the client. If not more output samples can be computed from the available input samples
+                    // we need to return.
+                    if (((pSRC->sinc.timeIn - pSRC->sinc.inputFrameCount) * inverseFactor) < 1) {
+                        break;
+                    }
+                }
+            }
 
-        
-        // Exit the loop if we've found everything from the client.
-        if (framesReadFromClient < framesToReadFromClient) {
-            break;
+            // Anything left over in the cache must be set to zero.
+            mal_uint32 leftoverFrames = mal_countof(pSRC->sinc.input[0]) - (pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount);
+            if (leftoverFrames > 0) {
+                for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) {
+                    mal_zero_memory(pSRC->sinc.input[iChannel] + pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount, leftoverFrames * sizeof(float));
+                }
+            }
         }
+
+        totalOutputFramesRead += outputFramesToRead;
     }
 
-    return totalFramesRead;
+    return totalOutputFramesRead;
 }
 
 
@@ -18604,7 +20253,6 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form
 typedef struct
 {
     mal_dsp* pDSP;
-    mal_bool32 flush;
     void* pUserDataForClient;
 } mal_dsp_callback_data;
 
@@ -18653,9 +20301,9 @@ mal_uint32 mal_dsp__post_format_converter_on_read_deinterleaved(mal_format_conve
         return (mal_uint32)mal_channel_router_read_deinterleaved(&pDSP->channelRouter, frameCount, ppSamplesOut, pUserData);
     } else {
         if (pDSP->isSRCRequired) {
-            return (mal_uint32)mal_src_read_deinterleaved_ex(&pDSP->src, frameCount, ppSamplesOut, pData->flush, pUserData);
+            return (mal_uint32)mal_src_read_deinterleaved(&pDSP->src, frameCount, ppSamplesOut, pUserData);
         } else {
-            return (mal_uint32)mal_format_converter_read_deinterleaved(&pDSP->formatConverterIn, frameCount, ppSamplesOut, pUserData);
+            return (mal_uint32)mal_channel_router_read_deinterleaved(&pDSP->channelRouter, frameCount, ppSamplesOut, pUserData);
         }
     }
 }
@@ -18693,7 +20341,7 @@ mal_uint32 mal_dsp__channel_router_on_read_deinterleaved(mal_channel_router* pRo
         return (mal_uint32)mal_format_converter_read_deinterleaved(&pDSP->formatConverterIn, frameCount, ppSamplesOut, pUserData);
     } else {
         if (pDSP->isSRCRequired) {
-            return (mal_uint32)mal_src_read_deinterleaved_ex(&pDSP->src, frameCount, ppSamplesOut, pData->flush, pUserData);
+            return (mal_uint32)mal_src_read_deinterleaved(&pDSP->src, frameCount, ppSamplesOut, pUserData);
         } else {
             return (mal_uint32)mal_format_converter_read_deinterleaved(&pDSP->formatConverterIn, frameCount, ppSamplesOut, pUserData);
         }
@@ -18799,15 +20447,15 @@ mal_result mal_dsp_init(const mal_dsp_config* pConfig, mal_dsp* pDSP)
 
     // Pre format conversion.
     {
-        mal_format_converter_config preFormatConverterConfig;
-        mal_zero_object(&preFormatConverterConfig);
-        preFormatConverterConfig.formatIn = pConfig->formatIn;
-        preFormatConverterConfig.formatOut = mal_format_f32;
-        preFormatConverterConfig.channels = pConfig->channelsIn;
-        preFormatConverterConfig.streamFormatIn = mal_stream_format_pcm;
-        preFormatConverterConfig.streamFormatOut = mal_stream_format_pcm;
-        preFormatConverterConfig.onRead = mal_dsp__pre_format_converter_on_read;
-        preFormatConverterConfig.pUserData = pDSP;
+        mal_format_converter_config preFormatConverterConfig = mal_format_converter_config_init(
+            pConfig->formatIn,
+            mal_format_f32,
+            pConfig->channelsIn,
+            mal_dsp__pre_format_converter_on_read,
+            pDSP
+        );
+        preFormatConverterConfig.ditherMode = pConfig->ditherMode;
+
         result = mal_format_converter_init(&preFormatConverterConfig, &pDSP->formatConverterIn);
         if (result != MAL_SUCCESS) {
             return result;
@@ -18817,14 +20465,11 @@ mal_result mal_dsp_init(const mal_dsp_config* pConfig, mal_dsp* pDSP)
     // Post format conversion. The exact configuration for this depends on whether or not we are reading data directly from the client
     // or from an earlier stage in the pipeline.
     {
-        mal_format_converter_config postFormatConverterConfig;
-        mal_zero_object(&postFormatConverterConfig);
+        mal_format_converter_config postFormatConverterConfig = mal_format_converter_config_init_new();
         postFormatConverterConfig.formatIn = pConfig->formatIn;
         postFormatConverterConfig.formatOut = pConfig->formatOut;
         postFormatConverterConfig.channels = pConfig->channelsOut;
-        postFormatConverterConfig.streamFormatIn = mal_stream_format_pcm;
-        postFormatConverterConfig.streamFormatOut = mal_stream_format_pcm;
-        postFormatConverterConfig.pUserData = pDSP;
+        postFormatConverterConfig.ditherMode = pConfig->ditherMode;
         if (pDSP->isPreFormatConversionRequired) {
             postFormatConverterConfig.onReadDeinterleaved = mal_dsp__post_format_converter_on_read_deinterleaved;
             postFormatConverterConfig.formatIn = mal_format_f32;
@@ -18840,14 +20485,16 @@ mal_result mal_dsp_init(const mal_dsp_config* pConfig, mal_dsp* pDSP)
 
     // SRC
     {
-        mal_src_config srcConfig;
-        mal_zero_object(&srcConfig);
-        srcConfig.sampleRateIn = pConfig->sampleRateIn;
-        srcConfig.sampleRateOut = pConfig->sampleRateOut;
-        srcConfig.channels = (pConfig->channelsIn < pConfig->channelsOut) ? pConfig->channelsIn : pConfig->channelsOut;
+        mal_src_config srcConfig = mal_src_config_init(
+            pConfig->sampleRateIn,
+            pConfig->sampleRateOut,
+            ((pConfig->channelsIn < pConfig->channelsOut) ? pConfig->channelsIn : pConfig->channelsOut),
+            mal_dsp__src_on_read_deinterleaved,
+            pDSP
+        );
         srcConfig.algorithm = pConfig->srcAlgorithm;
-        srcConfig.onReadDeinterleaved = mal_dsp__src_on_read_deinterleaved;
-        srcConfig.pUserData = pDSP;
+        mal_copy_memory(&srcConfig.sinc, &pConfig->sinc, sizeof(pConfig->sinc));
+
         result = mal_src_init(&srcConfig, &pDSP->src);
         if (result != MAL_SUCCESS) {
             return result;
@@ -18864,6 +20511,7 @@ mal_result mal_dsp_init(const mal_dsp_config* pConfig, mal_dsp* pDSP)
             pConfig->channelMixMode,
             mal_dsp__channel_router_on_read_deinterleaved,
             pDSP);
+
         result = mal_channel_router_init(&routerConfig, &pDSP->channelRouter);
         if (result != MAL_SUCCESS) {
             return result;
@@ -18924,11 +20572,6 @@ mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOu
 }
 
 mal_uint64 mal_dsp_read(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut, void* pUserData)
-{
-    return mal_dsp_read_ex(pDSP, frameCount, pFramesOut, MAL_FALSE, pUserData);
-}
-
-mal_uint64 mal_dsp_read_ex(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush, void* pUserData)
 {
     if (pDSP == NULL || pFramesOut == NULL) return 0;
 
@@ -18965,7 +20608,6 @@ mal_uint64 mal_dsp_read_ex(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOu
 
     mal_dsp_callback_data data;
     data.pDSP = pDSP;
-    data.flush = flush;
     data.pUserDataForClient = pUserData;
     return mal_format_converter_read(&pDSP->formatConverterOut, frameCount, pFramesOut, &data);
 }
@@ -19096,7 +20738,7 @@ mal_uint64 mal_convert_frames_ex(void* pOut, mal_format formatOut, mal_uint32 ch
         return 0;
     }
 
-    return mal_dsp_read_ex(&dsp, frameCountOut, pOut, MAL_TRUE, dsp.pUserData);
+    return mal_dsp_read(&dsp, frameCountOut, pOut, dsp.pUserData);
 }
 
 
@@ -19110,6 +20752,21 @@ mal_uint64 mal_convert_frames_ex(void* pOut, mal_format formatOut, mal_uint32 ch
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
+void* mal_malloc(size_t sz)
+{
+    return MAL_MALLOC(sz);
+}
+
+void* mal_realloc(void* p, size_t sz)
+{
+    return MAL_REALLOC(p, sz);
+}
+
+void mal_free(void* p)
+{
+    MAL_FREE(p);
+}
+
 void* mal_aligned_malloc(size_t sz, size_t alignment)
 {
     if (alignment == 0) {
@@ -19176,6 +20833,109 @@ void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint
 }
 
 
+typedef struct
+{
+    mal_uint8* pInputFrames;
+    mal_uint32 framesRemaining;
+} mal_calculate_cpu_speed_factor_data;
+
+mal_uint32 mal_calculate_cpu_speed_factor__on_read(mal_dsp* pDSP, mal_uint32 framesToRead, void* pFramesOut, void* pUserData)
+{
+    mal_calculate_cpu_speed_factor_data* pData = (mal_calculate_cpu_speed_factor_data*)pUserData;
+    mal_assert(pData != NULL);
+
+    if (framesToRead > pData->framesRemaining) {
+        framesToRead = pData->framesRemaining;
+    }
+
+    mal_copy_memory(pFramesOut, pData->pInputFrames, framesToRead*pDSP->formatConverterIn.config.channels * sizeof(*pData->pInputFrames));
+
+    pData->pInputFrames += framesToRead;
+    pData->framesRemaining -= framesToRead;
+
+    return framesToRead;
+}
+
+float mal_calculate_cpu_speed_factor()
+{
+    // Our profiling test is based on how quick it can process 1 second worth of samples through mini_al's data conversion pipeline.
+
+    // This factor is multiplied with the profiling time. May need to fiddle with this to get an accurate value.
+    float f = 1000;
+
+    // Experiment: Reduce the factor a little when debug mode is used to reduce a blowout.
+#if !defined(NDEBUG) || defined(_DEBUG)
+    f /= 2;
+#endif
+
+    mal_uint32 sampleRateIn  = 44100;
+    mal_uint32 sampleRateOut = 48000;
+    mal_uint32 channelsIn    = 2;
+    mal_uint32 channelsOut   = 6;
+
+    // Using the heap here to avoid an unnecessary static memory allocation. Also too big for the stack.
+    mal_uint8* pInputFrames = (mal_uint8*)mal_aligned_malloc(sampleRateIn * channelsIn * sizeof(*pInputFrames), MAL_SIMD_ALIGNMENT);
+    if (pInputFrames == NULL) {
+        return 1;
+    }
+
+    float* pOutputFrames = (float*)mal_aligned_malloc(sampleRateOut * channelsOut * sizeof(*pOutputFrames), MAL_SIMD_ALIGNMENT);
+    if (pOutputFrames == NULL) {
+        mal_aligned_free(pInputFrames);
+        return 1;
+    }
+
+    mal_calculate_cpu_speed_factor_data data;
+    data.pInputFrames = pInputFrames;
+    data.framesRemaining = sampleRateIn;
+    
+    mal_dsp_config config = mal_dsp_config_init(mal_format_u8, channelsIn, sampleRateIn, mal_format_f32, channelsOut, sampleRateOut, mal_calculate_cpu_speed_factor__on_read, &data);
+    mal_dsp dsp;
+    mal_result result = mal_dsp_init(&config, &dsp);
+    if (result != MAL_SUCCESS) {
+        mal_aligned_free(pInputFrames);
+        mal_aligned_free(pOutputFrames);
+        return 1;
+    }
+
+
+    int iterationCount = 2;
+
+    mal_timer timer;
+    mal_timer_init(&timer);
+    double startTime = mal_timer_get_time_in_seconds(&timer);
+    {
+        for (int i = 0; i < iterationCount; ++i) {
+            mal_dsp_read(&dsp, sampleRateOut, pOutputFrames, &data);
+            data.pInputFrames = pInputFrames;
+            data.framesRemaining = sampleRateIn;
+        }
+    }
+    double executionTimeInSeconds = mal_timer_get_time_in_seconds(&timer) - startTime;
+    executionTimeInSeconds /= iterationCount;
+
+    
+    mal_aligned_free(pInputFrames);
+    mal_aligned_free(pOutputFrames);
+
+    return (float)(executionTimeInSeconds * f);
+}
+
+mal_uint32 mal_scale_buffer_size(mal_uint32 baseBufferSize, float scale)
+{
+    return mal_max(1, (mal_uint32)(baseBufferSize*scale));
+}
+
+mal_uint32 mal_calculate_default_buffer_size_in_frames(mal_performance_profile performanceProfile, mal_uint32 sampleRate, float scale)
+{
+    if (performanceProfile == mal_performance_profile_low_latency) {
+        return mal_scale_buffer_size((sampleRate/1000) * MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY, scale);
+    } else {
+        return mal_scale_buffer_size((sampleRate/1000) * MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE, scale);
+    }
+}
+
+
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -19190,10 +20950,10 @@ mal_decoder_config mal_decoder_config_init(mal_format outputFormat, mal_uint32 o
 {
     mal_decoder_config config;
     mal_zero_object(&config);
-    config.outputFormat = outputFormat;
-    config.outputChannels = outputChannels;
-    config.outputSampleRate = outputSampleRate;
-    mal_get_standard_channel_map(mal_standard_channel_map_default, config.outputChannels, config.outputChannelMap);
+    config.format = outputFormat;
+    config.channels = outputChannels;
+    config.sampleRate = outputSampleRate;
+    mal_get_standard_channel_map(mal_standard_channel_map_default, config.channels, config.channelMap);
 
     return config;
 }
@@ -19215,28 +20975,28 @@ mal_result mal_decoder__init_dsp(mal_decoder* pDecoder, const mal_decoder_config
     mal_assert(pDecoder != NULL);
 
     // Output format.
-    if (pConfig->outputFormat == mal_format_unknown) {
+    if (pConfig->format == mal_format_unknown) {
         pDecoder->outputFormat = pDecoder->internalFormat;
     } else {
-        pDecoder->outputFormat = pConfig->outputFormat;
+        pDecoder->outputFormat = pConfig->format;
     }
 
-    if (pConfig->outputChannels == 0) {
+    if (pConfig->channels == 0) {
         pDecoder->outputChannels = pDecoder->internalChannels;
     } else {
-        pDecoder->outputChannels = pConfig->outputChannels;
+        pDecoder->outputChannels = pConfig->channels;
     }
 
-    if (pConfig->outputSampleRate == 0) {
+    if (pConfig->sampleRate == 0) {
         pDecoder->outputSampleRate = pDecoder->internalSampleRate;
     } else {
-        pDecoder->outputSampleRate = pConfig->outputSampleRate;
+        pDecoder->outputSampleRate = pConfig->sampleRate;
     }
 
-    if (mal_channel_map_blank(pDecoder->outputChannels, pConfig->outputChannelMap)) {
+    if (mal_channel_map_blank(pDecoder->outputChannels, pConfig->channelMap)) {
         mal_get_standard_channel_map(mal_standard_channel_map_default, pDecoder->outputChannels, pDecoder->outputChannelMap);
     } else {
-        mal_copy_memory(pDecoder->outputChannelMap, pConfig->outputChannelMap, sizeof(pConfig->outputChannelMap));
+        mal_copy_memory(pDecoder->outputChannelMap, pConfig->channelMap, sizeof(pConfig->channelMap));
     }
 
 
@@ -19817,7 +21577,7 @@ mal_result mal_decoder_init_mp3__internal(const mal_decoder_config* pConfig, mal
     drmp3_config mp3Config;
     mal_zero_object(&mp3Config);
     mp3Config.outputChannels = 2;
-    mp3Config.outputSampleRate = (pConfig->outputSampleRate != 0) ? pConfig->outputSampleRate : 44100;
+    mp3Config.outputSampleRate = (pConfig->sampleRate != 0) ? pConfig->sampleRate : 44100;
     if (!drmp3_init(pMP3, mal_decoder_internal_on_read__mp3, mal_decoder_internal_on_seek__mp3, pDecoder, &mp3Config)) {
         return MAL_ERROR;
     }
@@ -19843,6 +21603,92 @@ mal_result mal_decoder_init_mp3__internal(const mal_decoder_config* pConfig, mal
 }
 #endif
 
+// Raw
+mal_result mal_decoder_internal_on_seek_to_frame__raw(mal_decoder* pDecoder, mal_uint64 frameIndex)
+{
+    mal_assert(pDecoder != NULL);
+
+    if (pDecoder->onSeek == NULL) {
+        return MAL_ERROR;
+    }
+
+    mal_bool32 result = MAL_FALSE;
+
+    // The callback uses a 32 bit integer whereas we use a 64 bit unsigned integer. We just need to continuously seek until we're at the correct position.
+    mal_uint64 totalBytesToSeek = frameIndex * mal_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
+    if (totalBytesToSeek < 0x7FFFFFFF) {
+        // Simple case.
+        result = pDecoder->onSeek(pDecoder, (int)(frameIndex * mal_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels)), mal_seek_origin_start);
+    } else {
+        // Complex case. Start by doing a seek relative to the start. Then keep looping using offset seeking.
+        result = pDecoder->onSeek(pDecoder, 0x7FFFFFFF, mal_seek_origin_start);
+        if (result == MAL_TRUE) {
+            totalBytesToSeek -= 0x7FFFFFFF;
+
+            while (totalBytesToSeek > 0) {
+                mal_uint64 bytesToSeekThisIteration = totalBytesToSeek;
+                if (bytesToSeekThisIteration > 0x7FFFFFFF) {
+                    bytesToSeekThisIteration = 0x7FFFFFFF;
+                }
+
+                result = pDecoder->onSeek(pDecoder, (int)bytesToSeekThisIteration, mal_seek_origin_current);
+                if (result != MAL_TRUE) {
+                    break;
+                }
+
+                totalBytesToSeek -= bytesToSeekThisIteration;
+            }
+        }
+    }
+
+    if (result) {
+        return MAL_SUCCESS;
+    } else {
+        return MAL_ERROR;
+    }
+}
+
+mal_result mal_decoder_internal_on_uninit__raw(mal_decoder* pDecoder)
+{
+    (void)pDecoder;
+    return MAL_SUCCESS;
+}
+
+mal_uint32 mal_decoder_internal_on_read_frames__raw(mal_dsp* pDSP, mal_uint32 frameCount, void* pSamplesOut, void* pUserData)
+{
+    (void)pDSP;
+
+    mal_decoder* pDecoder = (mal_decoder*)pUserData;
+    mal_assert(pDecoder != NULL);
+
+    // For raw decoding we just read directly from the decoder's callbacks.
+    mal_uint32 bpf = mal_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
+    return pDecoder->onRead(pDecoder, pSamplesOut, frameCount * bpf) / bpf;
+}
+
+mal_result mal_decoder_init_raw__internal(const mal_decoder_config* pConfigIn, const mal_decoder_config* pConfigOut, mal_decoder* pDecoder)
+{
+    mal_assert(pConfigIn != NULL);
+    mal_assert(pConfigOut != NULL);
+    mal_assert(pDecoder != NULL);
+
+    pDecoder->onSeekToFrame = mal_decoder_internal_on_seek_to_frame__raw;
+    pDecoder->onUninit = mal_decoder_internal_on_uninit__raw;
+
+    // Internal format.
+    pDecoder->internalFormat = pConfigIn->format;
+    pDecoder->internalChannels = pConfigIn->channels;
+    pDecoder->internalSampleRate = pConfigIn->sampleRate;
+    mal_channel_map_copy(pDecoder->internalChannelMap, pConfigIn->channelMap, pConfigIn->channels);
+
+    mal_result result = mal_decoder__init_dsp(pDecoder, pConfigOut, mal_decoder_internal_on_read_frames__raw);
+    if (result != MAL_SUCCESS) {
+        return result;
+    }
+
+    return MAL_SUCCESS;
+}
+
 mal_result mal_decoder__preinit(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
 {
     mal_assert(pConfig != NULL);
@@ -19929,21 +21775,29 @@ mal_result mal_decoder_init_mp3(mal_decoder_read_proc onRead, mal_decoder_seek_p
 #endif
 }
 
-mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
+mal_result mal_decoder_init_raw(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfigIn, const mal_decoder_config* pConfigOut, mal_decoder* pDecoder)
 {
-    mal_decoder_config config = mal_decoder_config_init_copy(pConfig);
+    mal_decoder_config config = mal_decoder_config_init_copy(pConfigOut);
 
     mal_result result = mal_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
     if (result != MAL_SUCCESS) {
         return result;
     }
 
+    return mal_decoder_init_raw__internal(pConfigIn, &config, pDecoder);
+}
+
+mal_result mal_decoder_init__internal(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
+{
+    mal_assert(pConfig != NULL);
+    mal_assert(pDecoder != NULL);
+
     // We use trial and error to open a decoder.
-    result = MAL_NO_BACKEND;
+    mal_result result = MAL_NO_BACKEND;
 
 #ifdef MAL_HAS_WAV
     if (result != MAL_SUCCESS) {
-        result = mal_decoder_init_wav__internal(&config, pDecoder);
+        result = mal_decoder_init_wav__internal(pConfig, pDecoder);
         if (result != MAL_SUCCESS) {
             onSeek(pDecoder, 0, mal_seek_origin_start);
         }
@@ -19951,23 +21805,23 @@ mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc
 #endif
 #ifdef MAL_HAS_FLAC
     if (result != MAL_SUCCESS) {
-        result = mal_decoder_init_flac__internal(&config, pDecoder);
+        result = mal_decoder_init_flac__internal(pConfig, pDecoder);
         if (result != MAL_SUCCESS) {
             onSeek(pDecoder, 0, mal_seek_origin_start);
         }
     }
 #endif
-#ifdef MAL_HAS_MP3
+#ifdef MAL_HAS_VORBIS
     if (result != MAL_SUCCESS) {
-        result = mal_decoder_init_mp3__internal(&config, pDecoder);
+        result = mal_decoder_init_vorbis__internal(pConfig, pDecoder);
         if (result != MAL_SUCCESS) {
             onSeek(pDecoder, 0, mal_seek_origin_start);
         }
     }
 #endif
-#ifdef MAL_HAS_VORBIS
+#ifdef MAL_HAS_MP3
     if (result != MAL_SUCCESS) {
-        result = mal_decoder_init_vorbis__internal(&config, pDecoder);
+        result = mal_decoder_init_mp3__internal(pConfig, pDecoder);
         if (result != MAL_SUCCESS) {
             onSeek(pDecoder, 0, mal_seek_origin_start);
         }
@@ -19981,6 +21835,18 @@ mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc
     return result;
 }
 
+mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
+{
+    mal_decoder_config config = mal_decoder_config_init_copy(pConfig);
+
+    mal_result result = mal_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
+    if (result != MAL_SUCCESS) {
+        return result;
+    }
+
+    return mal_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder);
+}
+
 
 size_t mal_decoder__on_read_memory(mal_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
 {
@@ -20027,12 +21893,11 @@ mal_bool32 mal_decoder__on_seek_memory(mal_decoder* pDecoder, int byteOffset, ma
 
 mal_result mal_decoder__preinit_memory(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
 {
-    if (pDecoder == NULL) {
-        return MAL_INVALID_ARGS;
+    mal_result result = mal_decoder__preinit(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, pDecoder);
+    if (result != MAL_SUCCESS) {
+        return result;
     }
 
-    mal_zero_object(pDecoder);
-
     if (pData == NULL || dataSize == 0) {
         return MAL_INVALID_ARGS;
     }
@@ -20047,57 +21912,95 @@ mal_result mal_decoder__preinit_memory(const void* pData, size_t dataSize, const
 
 mal_result mal_decoder_init_memory(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
 {
-    mal_result result = mal_decoder__preinit_memory(pData, dataSize, pConfig, pDecoder);
+    mal_decoder_config config = mal_decoder_config_init_copy(pConfig);  // Make sure the config is not NULL.
+
+    mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
     if (result != MAL_SUCCESS) {
         return result;
     }
 
-    return mal_decoder_init(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, pDecoder);
+    return mal_decoder_init__internal(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, &config, pDecoder);
 }
 
 mal_result mal_decoder_init_memory_wav(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
 {
-    mal_result result = mal_decoder__preinit_memory(pData, dataSize, pConfig, pDecoder);
+    mal_decoder_config config = mal_decoder_config_init_copy(pConfig);  // Make sure the config is not NULL.
+
+    mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
     if (result != MAL_SUCCESS) {
         return result;
     }
 
-    return mal_decoder_init_wav(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, pDecoder);
+#ifdef MAL_HAS_WAV
+    return mal_decoder_init_wav__internal(&config, pDecoder);
+#else
+    return MAL_NO_BACKEND;
+#endif
 }
 
 mal_result mal_decoder_init_memory_flac(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
 {
-    mal_result result = mal_decoder__preinit_memory(pData, dataSize, pConfig, pDecoder);
+    mal_decoder_config config = mal_decoder_config_init_copy(pConfig);  // Make sure the config is not NULL.
+
+    mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
     if (result != MAL_SUCCESS) {
         return result;
     }
 
-    return mal_decoder_init_flac(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, pDecoder);
+#ifdef MAL_HAS_FLAC
+    return mal_decoder_init_flac__internal(&config, pDecoder);
+#else
+    return MAL_NO_BACKEND;
+#endif
 }
 
 mal_result mal_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
 {
-    mal_result result = mal_decoder__preinit_memory(pData, dataSize, pConfig, pDecoder);
+    mal_decoder_config config = mal_decoder_config_init_copy(pConfig);  // Make sure the config is not NULL.
+
+    mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
     if (result != MAL_SUCCESS) {
         return result;
     }
 
-    return mal_decoder_init_vorbis(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, pDecoder);
+#ifdef MAL_HAS_VORBIS
+    return mal_decoder_init_vorbis__internal(&config, pDecoder);
+#else
+    return MAL_NO_BACKEND;
+#endif
 }
 
 mal_result mal_decoder_init_memory_mp3(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
 {
-    mal_result result = mal_decoder__preinit_memory(pData, dataSize, pConfig, pDecoder);
+    mal_decoder_config config = mal_decoder_config_init_copy(pConfig);  // Make sure the config is not NULL.
+
+    mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
+    if (result != MAL_SUCCESS) {
+        return result;
+    }
+
+#ifdef MAL_HAS_MP3
+    return mal_decoder_init_mp3__internal(&config, pDecoder);
+#else
+    return MAL_NO_BACKEND;
+#endif
+}
+
+mal_result mal_decoder_init_memory_raw(const void* pData, size_t dataSize, const mal_decoder_config* pConfigIn, const mal_decoder_config* pConfigOut, mal_decoder* pDecoder)
+{
+    mal_decoder_config config = mal_decoder_config_init_copy(pConfigOut);  // Make sure the config is not NULL.
+
+    mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
     if (result != MAL_SUCCESS) {
         return result;
     }
 
-    return mal_decoder_init_mp3(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, pDecoder);
+    return mal_decoder_init_raw__internal(pConfigIn, &config, pDecoder);
 }
 
 #ifndef MAL_NO_STDIO
 #include <stdio.h>
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) && !defined(__DMC__)
 #include <strings.h>    // For strcasecmp().
 #endif
 
@@ -20157,7 +22060,7 @@ mal_bool32 mal_path_extension_equal(const char* path, const char* extension)
     const char* ext1 = extension;
     const char* ext2 = mal_path_extension(path);
 
-#ifdef _MSC_VER
+#if defined(_MSC_VER) || defined(__DMC__)
     return _stricmp(ext1, ext2) == 0;
 #else
     return strcasecmp(ext1, ext2) == 0;
@@ -20311,7 +22214,7 @@ mal_uint64 mal_decoder_read(mal_decoder* pDecoder, mal_uint64 frameCount, void*
 {
     if (pDecoder == NULL) return 0;
 
-    return mal_dsp_read_ex(&pDecoder->dsp, frameCount, pFramesOut, MAL_TRUE, pDecoder->dsp.pUserData);
+    return mal_dsp_read(&pDecoder->dsp, frameCount, pFramesOut, pDecoder->dsp.pUserData);
 }
 
 mal_result mal_decoder_seek_to_frame(mal_decoder* pDecoder, mal_uint64 frameIndex)
@@ -20325,6 +22228,125 @@ mal_result mal_decoder_seek_to_frame(mal_decoder* pDecoder, mal_uint64 frameInde
     // Should never get here, but if we do it means onSeekToFrame was not set by the backend.
     return MAL_INVALID_ARGS;
 }
+
+
+mal_result mal_decoder__full_decode_and_uninit(mal_decoder* pDecoder, mal_decoder_config* pConfigOut, mal_uint64* pFrameCountOut, void** ppDataOut)
+{
+    mal_assert(pDecoder != NULL);
+    
+    mal_uint64 totalFrameCount = 0;
+    mal_uint64 bpf = mal_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
+
+    // The frame count is unknown until we try reading. Thus, we just run in a loop.
+    mal_uint64 dataCapInFrames = 0;
+    void* pDataOut = NULL;
+    for (;;) {
+        // Make room if there's not enough.
+        if (totalFrameCount == dataCapInFrames) {
+            mal_uint64 newDataCapInFrames = dataCapInFrames*2;
+            if (newDataCapInFrames == 0) {
+                newDataCapInFrames = 4096;
+            }
+
+            if ((newDataCapInFrames * bpf) > SIZE_MAX) {
+                mal_free(pDataOut);
+                return MAL_TOO_LARGE;
+            }
+
+
+            void* pNewDataOut = (void*)mal_realloc(pDataOut, (size_t)(newDataCapInFrames * bpf));
+            if (pNewDataOut == NULL) {
+                mal_free(pDataOut);
+                return MAL_OUT_OF_MEMORY;
+            }
+
+            dataCapInFrames = newDataCapInFrames;
+            pDataOut = pNewDataOut;
+        }
+
+        mal_uint64 frameCountToTryReading = dataCapInFrames - totalFrameCount;
+        mal_assert(frameCountToTryReading > 0);
+
+        mal_uint64 framesJustRead = mal_decoder_read(pDecoder, frameCountToTryReading, (mal_uint8*)pDataOut + (totalFrameCount * bpf));
+        totalFrameCount += framesJustRead;
+
+        if (framesJustRead < frameCountToTryReading) {
+            break;
+        }
+    }
+
+    
+    if (pConfigOut != NULL) {
+        pConfigOut->format = pDecoder->outputFormat;
+        pConfigOut->channels = pDecoder->outputChannels;
+        pConfigOut->sampleRate = pDecoder->outputSampleRate;
+        mal_channel_map_copy(pConfigOut->channelMap, pDecoder->outputChannelMap, pDecoder->outputChannels);
+    }
+
+    if (ppDataOut != NULL) {
+        *ppDataOut = pDataOut;
+    } else {
+        mal_free(pDataOut);
+    }
+
+    if (pFrameCountOut != NULL) {
+        *pFrameCountOut = totalFrameCount;
+    }
+
+    mal_decoder_uninit(pDecoder);
+    return MAL_SUCCESS;
+}
+
+#ifndef MAL_NO_STDIO
+mal_result mal_decode_file(const char* pFilePath, mal_decoder_config* pConfig, mal_uint64* pFrameCountOut, void** ppDataOut)
+{
+    if (pFrameCountOut != NULL) {
+        *pFrameCountOut = 0;
+    }
+    if (ppDataOut != NULL) {
+        *ppDataOut = NULL;
+    }
+
+    if (pFilePath == NULL) {
+        return MAL_INVALID_ARGS;
+    }
+
+    mal_decoder_config config = mal_decoder_config_init_copy(pConfig);
+    
+    mal_decoder decoder;
+    mal_result result = mal_decoder_init_file(pFilePath, &config, &decoder);
+    if (result != MAL_SUCCESS) {
+        return result;
+    }
+
+    return mal_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppDataOut);
+}
+#endif
+
+mal_result mal_decode_memory(const void* pData, size_t dataSize, mal_decoder_config* pConfig, mal_uint64* pFrameCountOut, void** ppDataOut)
+{
+    if (pFrameCountOut != NULL) {
+        *pFrameCountOut = 0;
+    }
+    if (ppDataOut != NULL) {
+        *ppDataOut = NULL;
+    }
+
+    if (pData == NULL || dataSize == 0) {
+        return MAL_INVALID_ARGS;
+    }
+
+    mal_decoder_config config = mal_decoder_config_init_copy(pConfig);
+    
+    mal_decoder decoder;
+    mal_result result = mal_decoder_init_memory(pData, dataSize, &config, &decoder);
+    if (result != MAL_SUCCESS) {
+        return result;
+    }
+
+    return mal_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppDataOut);
+}
+
 #endif  // MAL_NO_DECODING
 
 
@@ -20383,13 +22405,15 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSineWave, mal_uint64 count, float*
 }
 
 
-#endif  // MAL_IMPLEMENTATION
+#endif  // MINI_AL_IMPLEMENTATION
 
 
 // REVISION HISTORY
 // ================
 //
 // v0.x - 2018-xx-xx
+//   - Changed MAL_IMPLEMENTATION to MINI_AL_IMPLEMENTATION for consistency with other libraries. The old
+//     way is still supported for now, but you should update as it may be removed in the future.
 //   - API CHANGE: Replace device enumeration APIs. mal_enumerate_devices() has been replaced with
 //     mal_context_get_devices(). An additional low-level device enumration API has been introduced called
 //     mal_context_enumerate_devices() which uses a callback to report devices.
@@ -20415,6 +22439,7 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSineWave, mal_uint64 count, float*
 //     distributions of MinGW.
 //   - Remove dependency on audioclient.h for the WASAPI backend. This fixes build issues with some
 //     distributions of MinGW.
+//   - Add support for dithering to format conversion.
 //   - Add support for configuring the priority of the worker thread.
 //   - Add a sine wave generator.
 //   - Improve efficiency of sample rate conversion.
@@ -20427,6 +22452,8 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSineWave, mal_uint64 count, float*
 //   - Make some APIs more const-correct.
 //   - Fix errors with OpenAL detection.
 //   - Fix some memory leaks.
+//   - Fix a bug with opening decoders from memory.
+//   - Add support for decoding from raw PCM data (mal_decoder_init_raw(), etc.)
 //   - Miscellaneous bug fixes.
 //   - Documentation updates.
 //