From 6b45777121fcd50d9b7d5b649c6710063b549892 Mon Sep 17 00:00:00 2001
From: Alexandre Julliard <julliard@winehq.org>
Date: Thu, 21 Oct 2021 11:45:51 +0200
Subject: [PATCH] libs: Import code from upstream FAudio 21.10.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
---
 configure                                 |   26 +
 configure.ac                              |    2 +
 libs/faudio/LICENSE                       |   25 +
 libs/faudio/Makefile.in                   |   22 +
 libs/faudio/include/F3DAudio.h            |  262 +
 libs/faudio/include/FACT.h                |  814 +++
 libs/faudio/include/FACT3D.h              |  127 +
 libs/faudio/include/FAPO.h                |  207 +
 libs/faudio/include/FAPOBase.h            |  264 +
 libs/faudio/include/FAPOFX.h              |  178 +
 libs/faudio/include/FAudio.h              | 1322 +++++
 libs/faudio/include/FAudioFX.h            |  308 ++
 libs/faudio/src/F3DAudio.c                | 1563 ++++++
 libs/faudio/src/FACT.c                    | 3021 +++++++++++
 libs/faudio/src/FACT3D.c                  |  172 +
 libs/faudio/src/FACT_internal.c           | 3336 ++++++++++++
 libs/faudio/src/FACT_internal.h           |  644 +++
 libs/faudio/src/FAPOBase.c                |  451 ++
 libs/faudio/src/FAPOFX.c                  |   89 +
 libs/faudio/src/FAPOFX_echo.c             |  248 +
 libs/faudio/src/FAPOFX_eq.c               |  257 +
 libs/faudio/src/FAPOFX_masteringlimiter.c |  247 +
 libs/faudio/src/FAPOFX_reverb.c           |  247 +
 libs/faudio/src/FAudio.c                  | 3201 ++++++++++++
 libs/faudio/src/FAudioFX_reverb.c         | 1969 ++++++++
 libs/faudio/src/FAudioFX_volumemeter.c    |  281 ++
 libs/faudio/src/FAudio_internal.c         | 1967 ++++++++
 libs/faudio/src/FAudio_internal.h         |  913 ++++
 libs/faudio/src/FAudio_internal_simd.c    | 1616 ++++++
 libs/faudio/src/FAudio_operationset.c     |  777 +++
 libs/faudio/src/FAudio_platform_win32.c   | 1583 ++++++
 libs/faudio/src/matrix_defaults.inl       |  147 +
 libs/faudio/src/stb.h                     |  406 ++
 libs/faudio/src/stb_vorbis.h              | 5572 +++++++++++++++++++++
 34 files changed, 32264 insertions(+)
 create mode 100644 libs/faudio/LICENSE
 create mode 100644 libs/faudio/Makefile.in
 create mode 100644 libs/faudio/include/F3DAudio.h
 create mode 100644 libs/faudio/include/FACT.h
 create mode 100644 libs/faudio/include/FACT3D.h
 create mode 100644 libs/faudio/include/FAPO.h
 create mode 100644 libs/faudio/include/FAPOBase.h
 create mode 100644 libs/faudio/include/FAPOFX.h
 create mode 100644 libs/faudio/include/FAudio.h
 create mode 100644 libs/faudio/include/FAudioFX.h
 create mode 100644 libs/faudio/src/F3DAudio.c
 create mode 100644 libs/faudio/src/FACT.c
 create mode 100644 libs/faudio/src/FACT3D.c
 create mode 100644 libs/faudio/src/FACT_internal.c
 create mode 100644 libs/faudio/src/FACT_internal.h
 create mode 100644 libs/faudio/src/FAPOBase.c
 create mode 100644 libs/faudio/src/FAPOFX.c
 create mode 100644 libs/faudio/src/FAPOFX_echo.c
 create mode 100644 libs/faudio/src/FAPOFX_eq.c
 create mode 100644 libs/faudio/src/FAPOFX_masteringlimiter.c
 create mode 100644 libs/faudio/src/FAPOFX_reverb.c
 create mode 100644 libs/faudio/src/FAudio.c
 create mode 100644 libs/faudio/src/FAudioFX_reverb.c
 create mode 100644 libs/faudio/src/FAudioFX_volumemeter.c
 create mode 100644 libs/faudio/src/FAudio_internal.c
 create mode 100644 libs/faudio/src/FAudio_internal.h
 create mode 100644 libs/faudio/src/FAudio_internal_simd.c
 create mode 100644 libs/faudio/src/FAudio_operationset.c
 create mode 100644 libs/faudio/src/FAudio_platform_win32.c
 create mode 100644 libs/faudio/src/matrix_defaults.inl
 create mode 100644 libs/faudio/src/stb.h
 create mode 100644 libs/faudio/src/stb_vorbis.h

diff --git a/configure b/configure
index 8d4ca1ac653..a76934d48ab 100755
--- a/configure
+++ b/configure
@@ -711,6 +711,8 @@ JPEG_PE_LIBS
 JPEG_PE_CFLAGS
 GSM_PE_LIBS
 GSM_PE_CFLAGS
+FAUDIO_PE_LIBS
+FAUDIO_PE_CFLAGS
 EXCESS_PRECISION_CFLAGS
 CROSSDEBUG
 DELAYLOADFLAG
@@ -1780,6 +1782,7 @@ enable_dmoguids
 enable_dxerr8
 enable_dxerr9
 enable_dxguid
+enable_faudio
 enable_gsm
 enable_jpeg
 enable_jxr
@@ -1926,6 +1929,8 @@ CPP
 OBJC
 OBJCFLAGS
 OBJCPP
+FAUDIO_PE_CFLAGS
+FAUDIO_PE_LIBS
 GSM_PE_CFLAGS
 GSM_PE_LIBS
 JPEG_PE_CFLAGS
@@ -2708,6 +2713,11 @@ Some influential environment variables:
   OBJC        Objective C compiler command
   OBJCFLAGS   Objective C compiler flags
   OBJCPP      Objective C preprocessor
+  FAUDIO_PE_CFLAGS
+              C compiler flags for the PE faudio, overriding the bundled
+              version
+  FAUDIO_PE_LIBS
+              Linker flags for the PE faudio, overriding the bundled version
   GSM_PE_CFLAGS
               C compiler flags for the PE gsm, overriding the bundled version
   GSM_PE_LIBS Linker flags for the PE gsm, overriding the bundled version
@@ -10680,6 +10690,19 @@ esac
 fi
 
 
+if ${FAUDIO_PE_CFLAGS:+false} :; then :
+  FAUDIO_PE_CFLAGS="-I\$(top_srcdir)/libs/faudio/include"
+else
+  enable_faudio=no
+fi
+if ${FAUDIO_PE_LIBS:+false} :; then :
+  FAUDIO_PE_LIBS="faudio mfplat mfreadwrite mfuuid propsys"
+else
+  enable_faudio=no
+fi
+$as_echo "$as_me:${as_lineno-$LINENO}: faudio cflags: $FAUDIO_PE_CFLAGS" >&5
+$as_echo "$as_me:${as_lineno-$LINENO}: faudio libs: $FAUDIO_PE_LIBS" >&5
+
 if ${GSM_PE_CFLAGS:+false} :; then :
   GSM_PE_CFLAGS="-I\$(top_srcdir)/libs/gsm/inc"
 else
@@ -18754,6 +18777,8 @@ QUICKTIME_LIBS = $QUICKTIME_LIBS
 CARBON_LIBS = $CARBON_LIBS
 METAL_LIBS = $METAL_LIBS
 EXCESS_PRECISION_CFLAGS = $EXCESS_PRECISION_CFLAGS
+FAUDIO_PE_CFLAGS = $FAUDIO_PE_CFLAGS
+FAUDIO_PE_LIBS = $FAUDIO_PE_LIBS
 GSM_PE_CFLAGS = $GSM_PE_CFLAGS
 GSM_PE_LIBS = $GSM_PE_LIBS
 JPEG_PE_CFLAGS = $JPEG_PE_CFLAGS
@@ -20052,6 +20077,7 @@ wine_fn_config_makefile libs/dmoguids enable_dmoguids
 wine_fn_config_makefile libs/dxerr8 enable_dxerr8
 wine_fn_config_makefile libs/dxerr9 enable_dxerr9
 wine_fn_config_makefile libs/dxguid enable_dxguid
+wine_fn_config_makefile libs/faudio enable_faudio
 wine_fn_config_makefile libs/gsm enable_gsm
 wine_fn_config_makefile libs/jpeg enable_jpeg
 wine_fn_config_makefile libs/jxr enable_jxr
diff --git a/configure.ac b/configure.ac
index 63bf4f25162..f1f52fe69dc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1052,6 +1052,7 @@ WINE_NOTICE_WITH(mingw,[test "x$CROSSTARGET" = "x"],
 
 dnl **** External libraries ****
 
+WINE_EXTLIB_FLAGS(FAUDIO, faudio, "faudio mfplat mfreadwrite mfuuid propsys", "-I\$(top_srcdir)/libs/faudio/include")
 WINE_EXTLIB_FLAGS(GSM, gsm, gsm, "-I\$(top_srcdir)/libs/gsm/inc")
 WINE_EXTLIB_FLAGS(JPEG, jpeg, jpeg, "-I\$(top_srcdir)/libs/jpeg")
 WINE_EXTLIB_FLAGS(JXR, jxr, jxr, "-I\$(top_srcdir)/libs/jxr/jxrgluelib -I\$(top_srcdir)/libs/jxr/image/sys")
@@ -3670,6 +3671,7 @@ WINE_CONFIG_MAKEFILE(libs/dmoguids)
 WINE_CONFIG_MAKEFILE(libs/dxerr8)
 WINE_CONFIG_MAKEFILE(libs/dxerr9)
 WINE_CONFIG_MAKEFILE(libs/dxguid)
+WINE_CONFIG_MAKEFILE(libs/faudio)
 WINE_CONFIG_MAKEFILE(libs/gsm)
 WINE_CONFIG_MAKEFILE(libs/jpeg)
 WINE_CONFIG_MAKEFILE(libs/jxr)
diff --git a/libs/faudio/LICENSE b/libs/faudio/LICENSE
new file mode 100644
index 00000000000..4877a26753f
--- /dev/null
+++ b/libs/faudio/LICENSE
@@ -0,0 +1,25 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
diff --git a/libs/faudio/Makefile.in b/libs/faudio/Makefile.in
new file mode 100644
index 00000000000..9058cac6543
--- /dev/null
+++ b/libs/faudio/Makefile.in
@@ -0,0 +1,22 @@
+EXTLIB    = libfaudio.a
+EXTRAINCL = -I$(srcdir)/include
+EXTRADEFS = -DFAUDIO_WIN32_PLATFORM -DHAVE_WMADEC
+
+C_SRCS = \
+	src/F3DAudio.c \
+	src/FACT.c \
+	src/FACT3D.c \
+	src/FACT_internal.c \
+	src/FAPOBase.c \
+	src/FAPOFX.c \
+	src/FAPOFX_echo.c \
+	src/FAPOFX_eq.c \
+	src/FAPOFX_masteringlimiter.c \
+	src/FAPOFX_reverb.c \
+	src/FAudio.c \
+	src/FAudioFX_reverb.c \
+	src/FAudioFX_volumemeter.c \
+	src/FAudio_internal.c \
+	src/FAudio_internal_simd.c \
+	src/FAudio_operationset.c \
+	src/FAudio_platform_win32.c
diff --git a/libs/faudio/include/F3DAudio.h b/libs/faudio/include/F3DAudio.h
new file mode 100644
index 00000000000..c8cdd3f9dda
--- /dev/null
+++ b/libs/faudio/include/F3DAudio.h
@@ -0,0 +1,262 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+/* This file has no documentation since the MSDN docs are still perfectly fine:
+ * https://docs.microsoft.com/en-us/windows/desktop/api/x3daudio/
+ */
+
+#ifndef F3DAUDIO_H
+#define F3DAUDIO_H
+
+#ifdef _WIN32
+#define F3DAUDIOAPI
+#else
+#define F3DAUDIOAPI
+#endif
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Constants */
+
+#ifndef _SPEAKER_POSITIONS_
+#define SPEAKER_FRONT_LEFT		0x00000001
+#define SPEAKER_FRONT_RIGHT		0x00000002
+#define SPEAKER_FRONT_CENTER		0x00000004
+#define SPEAKER_LOW_FREQUENCY		0x00000008
+#define SPEAKER_BACK_LEFT		0x00000010
+#define SPEAKER_BACK_RIGHT		0x00000020
+#define SPEAKER_FRONT_LEFT_OF_CENTER	0x00000040
+#define SPEAKER_FRONT_RIGHT_OF_CENTER	0x00000080
+#define SPEAKER_BACK_CENTER		0x00000100
+#define SPEAKER_SIDE_LEFT		0x00000200
+#define SPEAKER_SIDE_RIGHT		0x00000400
+#define SPEAKER_TOP_CENTER		0x00000800
+#define SPEAKER_TOP_FRONT_LEFT		0x00001000
+#define SPEAKER_TOP_FRONT_CENTER	0x00002000
+#define SPEAKER_TOP_FRONT_RIGHT		0x00004000
+#define SPEAKER_TOP_BACK_LEFT		0x00008000
+#define SPEAKER_TOP_BACK_CENTER		0x00010000
+#define SPEAKER_TOP_BACK_RIGHT		0x00020000
+#define _SPEAKER_POSITIONS_
+#endif
+
+#ifndef SPEAKER_MONO
+#define SPEAKER_MONO	SPEAKER_FRONT_CENTER
+#define SPEAKER_STEREO	(SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT)
+#define SPEAKER_2POINT1 \
+	(	SPEAKER_FRONT_LEFT	| \
+		SPEAKER_FRONT_RIGHT	| \
+		SPEAKER_LOW_FREQUENCY	)
+#define SPEAKER_SURROUND \
+	(	SPEAKER_FRONT_LEFT	| \
+		SPEAKER_FRONT_RIGHT	| \
+		SPEAKER_FRONT_CENTER	| \
+		SPEAKER_BACK_CENTER	)
+#define SPEAKER_QUAD \
+	(	SPEAKER_FRONT_LEFT	| \
+		SPEAKER_FRONT_RIGHT	| \
+		SPEAKER_BACK_LEFT	| \
+		SPEAKER_BACK_RIGHT	)
+#define SPEAKER_4POINT1 \
+	(	SPEAKER_FRONT_LEFT	| \
+		SPEAKER_FRONT_RIGHT	| \
+		SPEAKER_LOW_FREQUENCY	| \
+		SPEAKER_BACK_LEFT	| \
+		SPEAKER_BACK_RIGHT	)
+#define SPEAKER_5POINT1 \
+	(	SPEAKER_FRONT_LEFT	| \
+		SPEAKER_FRONT_RIGHT	| \
+		SPEAKER_FRONT_CENTER	| \
+		SPEAKER_LOW_FREQUENCY	| \
+		SPEAKER_BACK_LEFT	| \
+		SPEAKER_BACK_RIGHT	)
+#define SPEAKER_7POINT1 \
+	(	SPEAKER_FRONT_LEFT		| \
+		SPEAKER_FRONT_RIGHT		| \
+		SPEAKER_FRONT_CENTER		| \
+		SPEAKER_LOW_FREQUENCY		| \
+		SPEAKER_BACK_LEFT		| \
+		SPEAKER_BACK_RIGHT		| \
+		SPEAKER_FRONT_LEFT_OF_CENTER	| \
+		SPEAKER_FRONT_RIGHT_OF_CENTER	)
+#define SPEAKER_5POINT1_SURROUND \
+	(	SPEAKER_FRONT_LEFT	| \
+		SPEAKER_FRONT_RIGHT	| \
+		SPEAKER_FRONT_CENTER	| \
+		SPEAKER_LOW_FREQUENCY	| \
+		SPEAKER_SIDE_LEFT	| \
+		SPEAKER_SIDE_RIGHT	)
+#define SPEAKER_7POINT1_SURROUND \
+	(	SPEAKER_FRONT_LEFT	| \
+		SPEAKER_FRONT_RIGHT	| \
+		SPEAKER_FRONT_CENTER	| \
+		SPEAKER_LOW_FREQUENCY	| \
+		SPEAKER_BACK_LEFT	| \
+		SPEAKER_BACK_RIGHT	| \
+		SPEAKER_SIDE_LEFT	| \
+		SPEAKER_SIDE_RIGHT	)
+#define SPEAKER_XBOX SPEAKER_5POINT1
+#endif
+
+#define F3DAUDIO_PI			3.141592654f
+#define F3DAUDIO_2PI			6.283185307f
+
+#define F3DAUDIO_CALCULATE_MATRIX		0x00000001
+#define F3DAUDIO_CALCULATE_DELAY		0x00000002
+#define F3DAUDIO_CALCULATE_LPF_DIRECT		0x00000004
+#define F3DAUDIO_CALCULATE_LPF_REVERB		0x00000008
+#define F3DAUDIO_CALCULATE_REVERB		0x00000010
+#define F3DAUDIO_CALCULATE_DOPPLER		0x00000020
+#define F3DAUDIO_CALCULATE_EMITTER_ANGLE	0x00000040
+#define F3DAUDIO_CALCULATE_ZEROCENTER		0x00010000
+#define F3DAUDIO_CALCULATE_REDIRECT_TO_LFE	0x00020000
+
+/* Type Declarations */
+
+#define F3DAUDIO_HANDLE_BYTESIZE 20
+typedef uint8_t F3DAUDIO_HANDLE[F3DAUDIO_HANDLE_BYTESIZE];
+
+/* Structures */
+
+#pragma pack(push, 1)
+
+typedef struct F3DAUDIO_VECTOR
+{
+	float x;
+	float y;
+	float z;
+} F3DAUDIO_VECTOR;
+
+typedef struct F3DAUDIO_DISTANCE_CURVE_POINT
+{
+	float Distance;
+	float DSPSetting;
+} F3DAUDIO_DISTANCE_CURVE_POINT;
+
+typedef struct F3DAUDIO_DISTANCE_CURVE
+{
+	F3DAUDIO_DISTANCE_CURVE_POINT *pPoints;
+	uint32_t PointCount;
+} F3DAUDIO_DISTANCE_CURVE;
+
+typedef struct F3DAUDIO_CONE
+{
+	float InnerAngle;
+	float OuterAngle;
+	float InnerVolume;
+	float OuterVolume;
+	float InnerLPF;
+	float OuterLPF;
+	float InnerReverb;
+	float OuterReverb;
+} F3DAUDIO_CONE;
+
+typedef struct F3DAUDIO_LISTENER
+{
+	F3DAUDIO_VECTOR OrientFront;
+	F3DAUDIO_VECTOR OrientTop;
+	F3DAUDIO_VECTOR Position;
+	F3DAUDIO_VECTOR Velocity;
+	F3DAUDIO_CONE *pCone;
+} F3DAUDIO_LISTENER;
+
+typedef struct F3DAUDIO_EMITTER
+{
+	F3DAUDIO_CONE *pCone;
+	F3DAUDIO_VECTOR OrientFront;
+	F3DAUDIO_VECTOR OrientTop;
+	F3DAUDIO_VECTOR Position;
+	F3DAUDIO_VECTOR Velocity;
+	float InnerRadius;
+	float InnerRadiusAngle;
+	uint32_t ChannelCount;
+	float ChannelRadius;
+	float *pChannelAzimuths;
+	F3DAUDIO_DISTANCE_CURVE *pVolumeCurve;
+	F3DAUDIO_DISTANCE_CURVE *pLFECurve;
+	F3DAUDIO_DISTANCE_CURVE *pLPFDirectCurve;
+	F3DAUDIO_DISTANCE_CURVE *pLPFReverbCurve;
+	F3DAUDIO_DISTANCE_CURVE *pReverbCurve;
+	float CurveDistanceScaler;
+	float DopplerScaler;
+} F3DAUDIO_EMITTER;
+
+#ifndef F3DAUDIO_DSP_SETTINGS_DECL
+#define F3DAUDIO_DSP_SETTINGS_DECL
+typedef struct F3DAUDIO_DSP_SETTINGS F3DAUDIO_DSP_SETTINGS;
+#endif /* F3DAUDIO_DSP_SETTINGS_DECL */
+
+struct F3DAUDIO_DSP_SETTINGS
+{
+	float *pMatrixCoefficients;
+	float *pDelayTimes;
+	uint32_t SrcChannelCount;
+	uint32_t DstChannelCount;
+	float LPFDirectCoefficient;
+	float LPFReverbCoefficient;
+	float ReverbLevel;
+	float DopplerFactor;
+	float EmitterToListenerAngle;
+	float EmitterToListenerDistance;
+	float EmitterVelocityComponent;
+	float ListenerVelocityComponent;
+};
+
+#pragma pack(pop)
+
+/* Functions */
+
+F3DAUDIOAPI void F3DAudioInitialize(
+	uint32_t SpeakerChannelMask,
+	float SpeedOfSound,
+	F3DAUDIO_HANDLE Instance
+);
+
+F3DAUDIOAPI uint32_t F3DAudioInitialize8(
+	uint32_t SpeakerChannelMask,
+	float SpeedOfSound,
+	F3DAUDIO_HANDLE Instance
+);
+
+F3DAUDIOAPI void F3DAudioCalculate(
+	const F3DAUDIO_HANDLE Instance,
+	const F3DAUDIO_LISTENER *pListener,
+	const F3DAUDIO_EMITTER *pEmitter,
+	uint32_t Flags,
+	F3DAUDIO_DSP_SETTINGS *pDSPSettings
+);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* F3DAUDIO_H */
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/include/FACT.h b/libs/faudio/include/FACT.h
new file mode 100644
index 00000000000..579b7168812
--- /dev/null
+++ b/libs/faudio/include/FACT.h
@@ -0,0 +1,814 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+/* This file has no documentation since you are expected to already know how
+ * XACT works if you are still using these APIs!
+ */
+
+#ifndef FACT_H
+#define FACT_H
+
+#include "FAudio.h"
+
+#define FACTAPI FAUDIOAPI
+#ifdef _WIN32
+#define FACTCALL __stdcall
+#else
+#define FACTCALL
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Type Declarations */
+
+typedef struct FACTAudioEngine FACTAudioEngine;
+typedef struct FACTSoundBank FACTSoundBank;
+typedef struct FACTWaveBank FACTWaveBank;
+typedef struct FACTWave FACTWave;
+typedef struct FACTCue FACTCue;
+typedef struct FACTNotification FACTNotification;
+
+typedef struct FACTRendererDetails
+{
+	int16_t rendererID[0xFF]; /* Win32 wchar_t */
+	int16_t displayName[0xFF]; /* Win32 wchar_t */
+	int32_t defaultDevice;
+} FACTRendererDetails;
+
+typedef struct FACTOverlapped
+{
+	void *Internal; /* ULONG_PTR */
+	void *InternalHigh; /* ULONG_PTR */
+	FAUDIONAMELESS union
+	{
+		FAUDIONAMELESS struct
+		{
+			uint32_t Offset;
+			uint32_t OffsetHigh;
+		};
+		void *Pointer;
+	};
+	void *hEvent;
+} FACTOverlapped;
+
+typedef int32_t (FACTCALL * FACTReadFileCallback)(
+	void *hFile,
+	void *buffer,
+	uint32_t nNumberOfBytesToRead,
+	uint32_t *lpNumberOfBytesRead,
+	FACTOverlapped *lpOverlapped
+);
+
+typedef int32_t (FACTCALL * FACTGetOverlappedResultCallback)(
+	void *hFile,
+	FACTOverlapped *lpOverlapped,
+	uint32_t *lpNumberOfBytesTransferred,
+	int32_t bWait
+);
+
+typedef struct FACTFileIOCallbacks
+{
+	FACTReadFileCallback readFileCallback;
+	FACTGetOverlappedResultCallback getOverlappedResultCallback;
+} FACTFileIOCallbacks;
+
+typedef void (FACTCALL * FACTNotificationCallback)(
+	const FACTNotification *pNotification
+);
+
+/* FIXME: ABI bug! This should be pack(1) explicitly. Do not memcpy this! */
+typedef struct FACTRuntimeParameters
+{
+	uint32_t lookAheadTime;
+	void *pGlobalSettingsBuffer;
+	uint32_t globalSettingsBufferSize;
+	uint32_t globalSettingsFlags;
+	uint32_t globalSettingsAllocAttributes;
+	FACTFileIOCallbacks fileIOCallbacks;
+	FACTNotificationCallback fnNotificationCallback;
+	int16_t *pRendererID; /* Win32 wchar_t* */
+	FAudio *pXAudio2;
+	FAudioMasteringVoice *pMasteringVoice;
+} FACTRuntimeParameters;
+
+typedef struct FACTStreamingParameters
+{
+	void *file;
+	uint32_t offset;
+	uint32_t flags;
+	uint16_t packetSize; /* Measured in DVD sectors, or 2048 bytes */
+} FACTStreamingParameters;
+
+#define FACT_WAVEBANK_TYPE_BUFFER		0x00000000
+#define FACT_WAVEBANK_TYPE_STREAMING		0x00000001
+#define FACT_WAVEBANK_TYPE_MASK			0x00000001
+
+#define FACT_WAVEBANK_FLAGS_ENTRYNAMES		0x00010000
+#define FACT_WAVEBANK_FLAGS_COMPACT		0x00020000
+#define FACT_WAVEBANK_FLAGS_SYNC_DISABLED	0x00040000
+#define FACT_WAVEBANK_FLAGS_SEEKTABLES		0x00080000
+#define FACT_WAVEBANK_FLAGS_MASK		0x000F0000
+
+typedef enum FACTWaveBankSegIdx
+{
+	FACT_WAVEBANK_SEGIDX_BANKDATA = 0,
+	FACT_WAVEBANK_SEGIDX_ENTRYMETADATA,
+	FACT_WAVEBANK_SEGIDX_SEEKTABLES,
+	FACT_WAVEBANK_SEGIDX_ENTRYNAMES,
+	FACT_WAVEBANK_SEGIDX_ENTRYWAVEDATA,
+	FACT_WAVEBANK_SEGIDX_COUNT
+} FACTWaveBankSegIdx;
+
+#pragma pack(push, 1)
+
+typedef struct FACTWaveBankRegion
+{
+	uint32_t dwOffset;
+	uint32_t dwLength;
+} FACTWaveBankRegion;
+
+typedef struct FACTWaveBankSampleRegion
+{
+	uint32_t dwStartSample;
+	uint32_t dwTotalSamples;
+} FACTWaveBankSampleRegion;
+
+typedef struct FACTWaveBankHeader
+{
+	uint32_t dwSignature;
+	uint32_t dwVersion;
+	uint32_t dwHeaderVersion;
+	FACTWaveBankRegion Segments[FACT_WAVEBANK_SEGIDX_COUNT];
+} FACTWaveBankHeader;
+
+typedef union FACTWaveBankMiniWaveFormat
+{
+	FAUDIONAMELESS struct
+	{
+		uint32_t wFormatTag : 2;
+		uint32_t nChannels : 3;
+		uint32_t nSamplesPerSec : 18;
+		uint32_t wBlockAlign : 8;
+		uint32_t wBitsPerSample : 1;
+	};
+	uint32_t dwValue;
+} FACTWaveBankMiniWaveFormat;
+
+typedef struct FACTWaveBankEntry
+{
+	FAUDIONAMELESS union
+	{
+		FAUDIONAMELESS struct
+		{
+			uint32_t dwFlags : 4;
+			uint32_t Duration : 28;
+		};
+		uint32_t dwFlagsAndDuration;
+	};
+	FACTWaveBankMiniWaveFormat Format;
+	FACTWaveBankRegion PlayRegion;
+	FACTWaveBankSampleRegion LoopRegion;
+} FACTWaveBankEntry;
+
+typedef struct FACTWaveBankEntryCompact
+{
+	uint32_t dwOffset : 21;
+	uint32_t dwLengthDeviation : 11;
+} FACTWaveBankEntryCompact;
+
+typedef struct FACTWaveBankData
+{
+	uint32_t dwFlags;
+	uint32_t dwEntryCount;
+	char szBankName[64];
+	uint32_t dwEntryMetaDataElementSize;
+	uint32_t dwEntryNameElementSize;
+	uint32_t dwAlignment;
+	FACTWaveBankMiniWaveFormat CompactFormat;
+	uint64_t BuildTime;
+} FACTWaveBankData;
+
+#pragma pack(pop)
+
+typedef struct FACTWaveProperties
+{
+	char friendlyName[64];
+	FACTWaveBankMiniWaveFormat format;
+	uint32_t durationInSamples;
+	FACTWaveBankSampleRegion loopRegion;
+	int32_t streaming;
+} FACTWaveProperties;
+
+typedef struct FACTWaveInstanceProperties
+{
+	FACTWaveProperties properties;
+	int32_t backgroundMusic;
+} FACTWaveInstanceProperties;
+
+typedef struct FACTCueProperties
+{
+	char friendlyName[0xFF];
+	int32_t interactive;
+	uint16_t iaVariableIndex;
+	uint16_t numVariations;
+	uint8_t maxInstances;
+	uint8_t currentInstances;
+} FACTCueProperties;
+
+typedef struct FACTTrackProperties
+{
+	uint32_t duration;
+	uint16_t numVariations;
+	uint8_t numChannels;
+	uint16_t waveVariation;
+	uint8_t loopCount;
+} FACTTrackProperties;
+
+typedef struct FACTVariationProperties
+{
+	uint16_t index;
+	uint8_t weight;
+	float iaVariableMin;
+	float iaVariableMax;
+	int32_t linger;
+} FACTVariationProperties;
+
+typedef struct FACTSoundProperties
+{
+	uint16_t category;
+	uint8_t priority;
+	int16_t pitch;
+	float volume;
+	uint16_t numTracks;
+	FACTTrackProperties arrTrackProperties[1];
+} FACTSoundProperties;
+
+typedef struct FACTSoundVariationProperties
+{
+	FACTVariationProperties variationProperties;
+	FACTSoundProperties soundProperties;
+} FACTSoundVariationProperties;
+
+typedef struct FACTCueInstanceProperties
+{
+	uint32_t allocAttributes;
+	FACTCueProperties cueProperties;
+	FACTSoundVariationProperties activeVariationProperties;
+} FACTCueInstanceProperties;
+
+#pragma pack(push, 1)
+
+typedef struct FACTNotificationDescription
+{
+	uint8_t type;
+	uint8_t flags;
+	FACTSoundBank *pSoundBank;
+	FACTWaveBank *pWaveBank;
+	FACTCue *pCue;
+	FACTWave *pWave;
+	uint16_t cueIndex;
+	uint16_t waveIndex;
+	void* pvContext;
+} FACTNotificationDescription;
+
+typedef struct FACTNotificationCue
+{
+	uint16_t cueIndex;
+	FACTSoundBank *pSoundBank;
+	FACTCue *pCue;
+} FACTNotificationCue;
+
+typedef struct FACTNotificationMarker
+{
+	uint16_t cueIndex;
+	FACTSoundBank *pSoundBank;
+	FACTCue *pCue;
+	uint32_t marker;
+} FACTNotificationMarker;
+
+typedef struct FACTNotificationSoundBank
+{
+	FACTSoundBank *pSoundBank;
+} FACTNotificationSoundBank;
+
+typedef struct FACTNotificationWaveBank
+{
+	FACTWaveBank *pWaveBank;
+} FACTNotificationWaveBank;
+
+typedef struct FACTNotificationVariable
+{
+	uint16_t cueIndex;
+	FACTSoundBank *pSoundBank;
+	FACTCue *pCue;
+	uint16_t variableIndex;
+	float variableValue;
+	int32_t local;
+} FACTNotificationVariable;
+
+typedef struct FACTNotificationGUI
+{
+	uint32_t reserved;
+} FACTNotificationGUI;
+
+typedef struct FACTNotificationWave
+{
+	FACTWaveBank *pWaveBank;
+	uint16_t waveIndex;
+	uint16_t cueIndex;
+	FACTSoundBank *pSoundBank;
+	FACTCue *pCue;
+	FACTWave *pWave;
+} FACTNotificationWave;
+
+struct FACTNotification
+{
+	uint8_t type;
+	int32_t timeStamp;
+	void *pvContext;
+	FAUDIONAMELESS union
+	{
+		FACTNotificationCue cue;
+		FACTNotificationMarker marker;
+		FACTNotificationSoundBank soundBank;
+		FACTNotificationWaveBank waveBank;
+		FACTNotificationVariable variable;
+		FACTNotificationGUI gui;
+		FACTNotificationWave wave;
+	};
+};
+
+#pragma pack(pop)
+
+/* Constants */
+
+#define FACT_CONTENT_VERSION 46
+
+static const uint32_t FACT_FLAG_MANAGEDATA =		0x00000001;
+
+static const uint32_t FACT_FLAG_STOP_RELEASE =		0x00000000;
+static const uint32_t FACT_FLAG_STOP_IMMEDIATE =	0x00000001;
+
+static const uint32_t FACT_FLAG_BACKGROUND_MUSIC =	0x00000002;
+static const uint32_t FACT_FLAG_UNITS_MS =		0x00000004;
+static const uint32_t FACT_FLAG_UNITS_SAMPLES =		0x00000008;
+
+static const uint32_t FACT_STATE_CREATED =		0x00000001;
+static const uint32_t FACT_STATE_PREPARING =		0x00000002;
+static const uint32_t FACT_STATE_PREPARED =		0x00000004;
+static const uint32_t FACT_STATE_PLAYING =		0x00000008;
+static const uint32_t FACT_STATE_STOPPING =		0x00000010;
+static const uint32_t FACT_STATE_STOPPED =		0x00000020;
+static const uint32_t FACT_STATE_PAUSED =		0x00000040;
+static const uint32_t FACT_STATE_INUSE =		0x00000080;
+static const uint32_t FACT_STATE_PREPAREFAILED =	0x80000000;
+
+static const int16_t FACTPITCH_MIN =		-1200;
+static const int16_t FACTPITCH_MAX =		 1200;
+static const int16_t FACTPITCH_MIN_TOTAL =	-2400;
+static const int16_t FACTPITCH_MAX_TOTAL =	 2400;
+
+static const float FACTVOLUME_MIN = 0.0f;
+static const float FACTVOLUME_MAX = 16777216.0f;
+
+static const uint16_t FACTINDEX_INVALID =		0xFFFF;
+static const uint16_t FACTVARIABLEINDEX_INVALID =	0xFFFF;
+static const uint16_t FACTCATEGORY_INVALID =		0xFFFF;
+
+static const uint8_t FACTNOTIFICATIONTYPE_CUEPREPARED =				1;
+static const uint8_t FACTNOTIFICATIONTYPE_CUEPLAY =				2;
+static const uint8_t FACTNOTIFICATIONTYPE_CUESTOP =				3;
+static const uint8_t FACTNOTIFICATIONTYPE_CUEDESTROYED =			4;
+static const uint8_t FACTNOTIFICATIONTYPE_MARKER =				5;
+static const uint8_t FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED =			6;
+static const uint8_t FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED =			7;
+static const uint8_t FACTNOTIFICATIONTYPE_LOCALVARIABLECHANGED =		8;
+static const uint8_t FACTNOTIFICATIONTYPE_GLOBALVARIABLECHANGED =		9;
+static const uint8_t FACTNOTIFICATIONTYPE_GUICONNECTED =			10;
+static const uint8_t FACTNOTIFICATIONTYPE_GUIDISCONNECTED =			11;
+static const uint8_t FACTNOTIFICATIONTYPE_WAVEPREPARED =			12;
+static const uint8_t FACTNOTIFICATIONTYPE_WAVEPLAY =				13;
+static const uint8_t FACTNOTIFICATIONTYPE_WAVESTOP =				14;
+static const uint8_t FACTNOTIFICATIONTYPE_WAVELOOPED =				15;
+static const uint8_t FACTNOTIFICATIONTYPE_WAVEDESTROYED =			16;
+static const uint8_t FACTNOTIFICATIONTYPE_WAVEBANKPREPARED =			17;
+static const uint8_t FACTNOTIFICATIONTYPE_WAVEBANKSTREAMING_INVALIDCONTENT =	18;
+
+static const uint8_t FACT_FLAG_NOTIFICATION_PERSIST = 0x01;
+
+#define FACT_ENGINE_LOOKAHEAD_DEFAULT 250
+
+#define FACT_MAX_WMA_AVG_BYTES_PER_SEC_ENTRIES 7
+static const uint32_t aWMAAvgBytesPerSec[] =
+{
+	12000,
+	24000,
+	4000,
+	6000,
+	8000,
+	20000,
+	2500
+};
+
+#define FACT_MAX_WMA_BLOCK_ALIGN_ENTRIES 17
+static const uint32_t aWMABlockAlign[] =
+{
+	929,
+	1487,
+	1280,
+	2230,
+	8917,
+	8192,
+	4459,
+	5945,
+	2304,
+	1536,
+	1485,
+	1008,
+	2731,
+	4096,
+	6827,
+	5462,
+	1280
+};
+
+/* AudioEngine Interface */
+
+FACTAPI uint32_t FACTCreateEngine(
+	uint32_t dwCreationFlags,
+	FACTAudioEngine **ppEngine
+);
+
+/* See "extensions/CustomAllocatorEXT.txt" for more details. */
+FACTAPI uint32_t FACTCreateEngineWithCustomAllocatorEXT(
+	uint32_t dwCreationFlags,
+	FACTAudioEngine **ppEngine,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc
+);
+
+FACTAPI uint32_t FACTAudioEngine_AddRef(FACTAudioEngine *pEngine);
+
+FACTAPI uint32_t FACTAudioEngine_Release(FACTAudioEngine *pEngine);
+
+/* FIXME: QueryInterface? Or just ignore COM garbage... -flibit */
+
+FACTAPI uint32_t FACTAudioEngine_GetRendererCount(
+	FACTAudioEngine *pEngine,
+	uint16_t *pnRendererCount
+);
+
+FACTAPI uint32_t FACTAudioEngine_GetRendererDetails(
+	FACTAudioEngine *pEngine,
+	uint16_t nRendererIndex,
+	FACTRendererDetails *pRendererDetails
+);
+
+FACTAPI uint32_t FACTAudioEngine_GetFinalMixFormat(
+	FACTAudioEngine *pEngine,
+	FAudioWaveFormatExtensible *pFinalMixFormat
+);
+
+FACTAPI uint32_t FACTAudioEngine_Initialize(
+	FACTAudioEngine *pEngine,
+	const FACTRuntimeParameters *pParams
+);
+
+FACTAPI uint32_t FACTAudioEngine_ShutDown(FACTAudioEngine *pEngine);
+
+FACTAPI uint32_t FACTAudioEngine_DoWork(FACTAudioEngine *pEngine);
+
+FACTAPI uint32_t FACTAudioEngine_CreateSoundBank(
+	FACTAudioEngine *pEngine,
+	const void *pvBuffer,
+	uint32_t dwSize,
+	uint32_t dwFlags,
+	uint32_t dwAllocAttributes,
+	FACTSoundBank **ppSoundBank
+);
+
+FACTAPI uint32_t FACTAudioEngine_CreateInMemoryWaveBank(
+	FACTAudioEngine *pEngine,
+	const void *pvBuffer,
+	uint32_t dwSize,
+	uint32_t dwFlags,
+	uint32_t dwAllocAttributes,
+	FACTWaveBank **ppWaveBank
+);
+
+FACTAPI uint32_t FACTAudioEngine_CreateStreamingWaveBank(
+	FACTAudioEngine *pEngine,
+	const FACTStreamingParameters *pParms,
+	FACTWaveBank **ppWaveBank
+);
+
+FACTAPI uint32_t FACTAudioEngine_PrepareWave(
+	FACTAudioEngine *pEngine,
+	uint32_t dwFlags,
+	const char *szWavePath,
+	uint32_t wStreamingPacketSize,
+	uint32_t dwAlignment,
+	uint32_t dwPlayOffset,
+	uint8_t nLoopCount,
+	FACTWave **ppWave
+);
+
+FACTAPI uint32_t FACTAudioEngine_PrepareInMemoryWave(
+	FACTAudioEngine *pEngine,
+	uint32_t dwFlags,
+	FACTWaveBankEntry entry,
+	uint32_t *pdwSeekTable, /* Optional! */
+	uint8_t *pbWaveData,
+	uint32_t dwPlayOffset,
+	uint8_t nLoopCount,
+	FACTWave **ppWave
+);
+
+FACTAPI uint32_t FACTAudioEngine_PrepareStreamingWave(
+	FACTAudioEngine *pEngine,
+	uint32_t dwFlags,
+	FACTWaveBankEntry entry,
+	FACTStreamingParameters streamingParams,
+	uint32_t dwAlignment,
+	uint32_t *pdwSeekTable, /* Optional! */
+	uint8_t *pbWaveData, /* ABI bug, do not use! */
+	uint32_t dwPlayOffset,
+	uint8_t nLoopCount,
+	FACTWave **ppWave
+);
+
+FACTAPI uint32_t FACTAudioEngine_RegisterNotification(
+	FACTAudioEngine *pEngine,
+	const FACTNotificationDescription *pNotificationDescription
+);
+
+FACTAPI uint32_t FACTAudioEngine_UnRegisterNotification(
+	FACTAudioEngine *pEngine,
+	const FACTNotificationDescription *pNotificationDescription
+);
+
+FACTAPI uint16_t FACTAudioEngine_GetCategory(
+	FACTAudioEngine *pEngine,
+	const char *szFriendlyName
+);
+
+FACTAPI uint32_t FACTAudioEngine_Stop(
+	FACTAudioEngine *pEngine,
+	uint16_t nCategory,
+	uint32_t dwFlags
+);
+
+FACTAPI uint32_t FACTAudioEngine_SetVolume(
+	FACTAudioEngine *pEngine,
+	uint16_t nCategory,
+	float volume
+);
+
+FACTAPI uint32_t FACTAudioEngine_Pause(
+	FACTAudioEngine *pEngine,
+	uint16_t nCategory,
+	int32_t fPause
+);
+
+FACTAPI uint16_t FACTAudioEngine_GetGlobalVariableIndex(
+	FACTAudioEngine *pEngine,
+	const char *szFriendlyName
+);
+
+FACTAPI uint32_t FACTAudioEngine_SetGlobalVariable(
+	FACTAudioEngine *pEngine,
+	uint16_t nIndex,
+	float nValue
+);
+
+FACTAPI uint32_t FACTAudioEngine_GetGlobalVariable(
+	FACTAudioEngine *pEngine,
+	uint16_t nIndex,
+	float *pnValue
+);
+
+/* SoundBank Interface */
+
+FACTAPI uint16_t FACTSoundBank_GetCueIndex(
+	FACTSoundBank *pSoundBank,
+	const char *szFriendlyName
+);
+
+FACTAPI uint32_t FACTSoundBank_GetNumCues(
+	FACTSoundBank *pSoundBank,
+	uint16_t *pnNumCues
+);
+
+FACTAPI uint32_t FACTSoundBank_GetCueProperties(
+	FACTSoundBank *pSoundBank,
+	uint16_t nCueIndex,
+	FACTCueProperties *pProperties
+);
+
+FACTAPI uint32_t FACTSoundBank_Prepare(
+	FACTSoundBank *pSoundBank,
+	uint16_t nCueIndex,
+	uint32_t dwFlags,
+	int32_t timeOffset,
+	FACTCue** ppCue
+);
+
+FACTAPI uint32_t FACTSoundBank_Play(
+	FACTSoundBank *pSoundBank,
+	uint16_t nCueIndex,
+	uint32_t dwFlags,
+	int32_t timeOffset,
+	FACTCue** ppCue /* Optional! */
+);
+
+#ifndef F3DAUDIO_DSP_SETTINGS_DECL
+#define F3DAUDIO_DSP_SETTINGS_DECL
+typedef struct F3DAUDIO_DSP_SETTINGS F3DAUDIO_DSP_SETTINGS;
+#endif /* F3DAUDIO_DSP_SETTINGS_DECL */
+
+FACTAPI uint32_t FACTSoundBank_Play3D(
+	FACTSoundBank *pSoundBank,
+	uint16_t nCueIndex,
+	uint32_t dwFlags,
+	int32_t timeOffset,
+	F3DAUDIO_DSP_SETTINGS *pDSPSettings,
+	FACTCue** ppCue /* Optional! */
+);
+
+FACTAPI uint32_t FACTSoundBank_Stop(
+	FACTSoundBank *pSoundBank,
+	uint16_t nCueIndex,
+	uint32_t dwFlags
+);
+
+FACTAPI uint32_t FACTSoundBank_Destroy(FACTSoundBank *pSoundBank);
+
+FACTAPI uint32_t FACTSoundBank_GetState(
+	FACTSoundBank *pSoundBank,
+	uint32_t *pdwState
+);
+
+/* WaveBank Interface */
+
+FACTAPI uint32_t FACTWaveBank_Destroy(FACTWaveBank *pWaveBank);
+
+FACTAPI uint32_t FACTWaveBank_GetState(
+	FACTWaveBank *pWaveBank,
+	uint32_t *pdwState
+);
+
+FACTAPI uint32_t FACTWaveBank_GetNumWaves(
+	FACTWaveBank *pWaveBank,
+	uint16_t *pnNumWaves
+);
+
+FACTAPI uint16_t FACTWaveBank_GetWaveIndex(
+	FACTWaveBank *pWaveBank,
+	const char *szFriendlyName
+);
+
+FACTAPI uint32_t FACTWaveBank_GetWaveProperties(
+	FACTWaveBank *pWaveBank,
+	uint16_t nWaveIndex,
+	FACTWaveProperties *pWaveProperties
+);
+
+FACTAPI uint32_t FACTWaveBank_Prepare(
+	FACTWaveBank *pWaveBank,
+	uint16_t nWaveIndex,
+	uint32_t dwFlags,
+	uint32_t dwPlayOffset,
+	uint8_t nLoopCount,
+	FACTWave **ppWave
+);
+
+FACTAPI uint32_t FACTWaveBank_Play(
+	FACTWaveBank *pWaveBank,
+	uint16_t nWaveIndex,
+	uint32_t dwFlags,
+	uint32_t dwPlayOffset,
+	uint8_t nLoopCount,
+	FACTWave **ppWave
+);
+
+FACTAPI uint32_t FACTWaveBank_Stop(
+	FACTWaveBank *pWaveBank,
+	uint16_t nWaveIndex,
+	uint32_t dwFlags
+);
+
+/* Wave Interface */
+
+FACTAPI uint32_t FACTWave_Destroy(FACTWave *pWave);
+
+FACTAPI uint32_t FACTWave_Play(FACTWave *pWave);
+
+FACTAPI uint32_t FACTWave_Stop(FACTWave *pWave, uint32_t dwFlags);
+
+FACTAPI uint32_t FACTWave_Pause(FACTWave *pWave, int32_t fPause);
+
+FACTAPI uint32_t FACTWave_GetState(FACTWave *pWave, uint32_t *pdwState);
+
+FACTAPI uint32_t FACTWave_SetPitch(FACTWave *pWave, int16_t pitch);
+
+FACTAPI uint32_t FACTWave_SetVolume(FACTWave *pWave, float volume);
+
+FACTAPI uint32_t FACTWave_SetMatrixCoefficients(
+	FACTWave *pWave,
+	uint32_t uSrcChannelCount,
+	uint32_t uDstChannelCount,
+	float *pMatrixCoefficients
+);
+
+FACTAPI uint32_t FACTWave_GetProperties(
+	FACTWave *pWave,
+	FACTWaveInstanceProperties *pProperties
+);
+
+/* Cue Interface */
+
+FACTAPI uint32_t FACTCue_Destroy(FACTCue *pCue);
+
+FACTAPI uint32_t FACTCue_Play(FACTCue *pCue);
+
+FACTAPI uint32_t FACTCue_Stop(FACTCue *pCue, uint32_t dwFlags);
+
+FACTAPI uint32_t FACTCue_GetState(FACTCue *pCue, uint32_t *pdwState);
+
+FACTAPI uint32_t FACTCue_SetMatrixCoefficients(
+	FACTCue *pCue,
+	uint32_t uSrcChannelCount,
+	uint32_t uDstChannelCount,
+	float *pMatrixCoefficients
+);
+
+FACTAPI uint16_t FACTCue_GetVariableIndex(
+	FACTCue *pCue,
+	const char *szFriendlyName
+);
+
+FACTAPI uint32_t FACTCue_SetVariable(
+	FACTCue *pCue,
+	uint16_t nIndex,
+	float nValue
+);
+
+FACTAPI uint32_t FACTCue_GetVariable(
+	FACTCue *pCue,
+	uint16_t nIndex,
+	float *nValue
+);
+
+FACTAPI uint32_t FACTCue_Pause(FACTCue *pCue, int32_t fPause);
+
+FACTAPI uint32_t FACTCue_GetProperties(
+	FACTCue *pCue,
+	FACTCueInstanceProperties **ppProperties
+);
+
+FACTAPI uint32_t FACTCue_SetOutputVoices(
+	FACTCue *pCue,
+	const FAudioVoiceSends *pSendList /* Optional! */
+);
+
+FACTAPI uint32_t FACTCue_SetOutputVoiceMatrix(
+	FACTCue *pCue,
+	const FAudioVoice *pDestinationVoice, /* Optional! */
+	uint32_t SourceChannels,
+	uint32_t DestinationChannels,
+	const float *pLevelMatrix /* SourceChannels * DestinationChannels */
+);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* FACT_H */
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/include/FACT3D.h b/libs/faudio/include/FACT3D.h
new file mode 100644
index 00000000000..6ac02a82986
--- /dev/null
+++ b/libs/faudio/include/FACT3D.h
@@ -0,0 +1,127 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+/* This file has no documentation since you are expected to already know how
+ * XACT works if you are still using these APIs!
+ */
+
+#ifndef FACT3D_H
+#define FACT3D_H
+
+#include "F3DAudio.h"
+#include "FACT.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Constants */
+
+#define LEFT_AZIMUTH			(3.0f * F3DAUDIO_PI / 2.0f)
+#define RIGHT_AZIMUTH			(F3DAUDIO_PI / 2.0f)
+#define FRONT_LEFT_AZIMUTH		(7.0f * F3DAUDIO_PI / 4.0f)
+#define FRONT_RIGHT_AZIMUTH		(F3DAUDIO_PI / 4.0f)
+#define FRONT_CENTER_AZIMUTH		0.0f
+#define LOW_FREQUENCY_AZIMUTH		F3DAUDIO_2PI
+#define BACK_LEFT_AZIMUTH		(5.0f * F3DAUDIO_PI / 4.0f)
+#define BACK_RIGHT_AZIMUTH		(3.0f * F3DAUDIO_PI / 4.0f)
+#define BACK_CENTER_AZIMUTH		F3DAUDIO_PI
+#define FRONT_LEFT_OF_CENTER_AZIMUTH	(15.0f * F3DAUDIO_PI / 8.0f)
+#define FRONT_RIGHT_OF_CENTER_AZIMUTH	(F3DAUDIO_PI / 8.0f)
+
+static const float aStereoLayout[] =
+{
+	LEFT_AZIMUTH,
+	RIGHT_AZIMUTH
+};
+static const float a2Point1Layout[] =
+{
+	LEFT_AZIMUTH,
+	RIGHT_AZIMUTH,
+	LOW_FREQUENCY_AZIMUTH
+};
+static const float aQuadLayout[] =
+{
+	FRONT_LEFT_AZIMUTH,
+	FRONT_RIGHT_AZIMUTH,
+	BACK_LEFT_AZIMUTH,
+	BACK_RIGHT_AZIMUTH
+};
+static const float a4Point1Layout[] =
+{
+	FRONT_LEFT_AZIMUTH,
+	FRONT_RIGHT_AZIMUTH,
+	LOW_FREQUENCY_AZIMUTH,
+	BACK_LEFT_AZIMUTH,
+	BACK_RIGHT_AZIMUTH
+};
+static const float a5Point1Layout[] =
+{
+	FRONT_LEFT_AZIMUTH,
+	FRONT_RIGHT_AZIMUTH,
+	FRONT_CENTER_AZIMUTH,
+	LOW_FREQUENCY_AZIMUTH,
+	BACK_LEFT_AZIMUTH,
+	BACK_RIGHT_AZIMUTH
+};
+static const float a7Point1Layout[] =
+{
+	FRONT_LEFT_AZIMUTH,
+	FRONT_RIGHT_AZIMUTH,
+	FRONT_CENTER_AZIMUTH,
+	LOW_FREQUENCY_AZIMUTH,
+	BACK_LEFT_AZIMUTH,
+	BACK_RIGHT_AZIMUTH,
+	LEFT_AZIMUTH,
+	RIGHT_AZIMUTH
+};
+
+/* Functions */
+
+FACTAPI uint32_t FACT3DInitialize(
+	FACTAudioEngine *pEngine,
+	F3DAUDIO_HANDLE F3DInstance
+);
+
+FACTAPI uint32_t FACT3DCalculate(
+	F3DAUDIO_HANDLE F3DInstance,
+	const F3DAUDIO_LISTENER *pListener,
+	F3DAUDIO_EMITTER *pEmitter,
+	F3DAUDIO_DSP_SETTINGS *pDSPSettings
+);
+
+FACTAPI uint32_t FACT3DApply(
+	F3DAUDIO_DSP_SETTINGS *pDSPSettings,
+	FACTCue *pCue
+);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* FACT3D_H */
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/include/FAPO.h b/libs/faudio/include/FAPO.h
new file mode 100644
index 00000000000..0609770047f
--- /dev/null
+++ b/libs/faudio/include/FAPO.h
@@ -0,0 +1,207 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+/* This file has no documentation since the MSDN docs are still perfectly fine:
+ * https://docs.microsoft.com/en-us/windows/desktop/api/xapo/
+ *
+ * Of course, the APIs aren't exactly the same since XAPO is super dependent on
+ * C++. Instead, we use a struct full of functions to mimic a vtable.
+ *
+ * The only serious difference is that our FAPO (yes, really) always has the
+ * Get/SetParameters function pointers, for simplicity. You can ignore these if
+ * your effect does not have parameters, as they will never get called unless
+ * it is explicitly requested by the application.
+ */
+
+#ifndef FAPO_H
+#define FAPO_H
+
+#include "FAudio.h"
+
+#define FAPOAPI FAUDIOAPI
+#define FAPOCALL FAUDIOCALL
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Enumerations */
+
+typedef enum FAPOBufferFlags
+{
+	FAPO_BUFFER_SILENT,
+	FAPO_BUFFER_VALID
+} FAPOBufferFlags;
+
+/* Structures */
+
+#pragma pack(push, 1)
+
+typedef struct FAPORegistrationProperties
+{
+	FAudioGUID clsid;
+	int16_t FriendlyName[256]; /* Win32 wchar_t */
+	int16_t CopyrightInfo[256]; /* Win32 wchar_t */
+	uint32_t MajorVersion;
+	uint32_t MinorVersion;
+	uint32_t Flags;
+	uint32_t MinInputBufferCount;
+	uint32_t MaxInputBufferCount;
+	uint32_t MinOutputBufferCount;
+	uint32_t MaxOutputBufferCount;
+} FAPORegistrationProperties;
+
+typedef struct FAPOLockForProcessBufferParameters
+{
+	const FAudioWaveFormatEx *pFormat;
+	uint32_t MaxFrameCount;
+} FAPOLockForProcessBufferParameters;
+
+typedef struct FAPOProcessBufferParameters
+{
+	void* pBuffer;
+	FAPOBufferFlags BufferFlags;
+	uint32_t ValidFrameCount;
+} FAPOProcessBufferParameters;
+
+#pragma pack(pop)
+
+/* Constants */
+
+#define FAPO_MIN_CHANNELS 1
+#define FAPO_MAX_CHANNELS 64
+
+#define FAPO_MIN_FRAMERATE 1000
+#define FAPO_MAX_FRAMERATE 200000
+
+#define FAPO_REGISTRATION_STRING_LENGTH 256
+
+#define FAPO_FLAG_CHANNELS_MUST_MATCH		0x00000001
+#define FAPO_FLAG_FRAMERATE_MUST_MATCH		0x00000002
+#define FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH	0x00000004
+#define FAPO_FLAG_BUFFERCOUNT_MUST_MATCH	0x00000008
+#define FAPO_FLAG_INPLACE_REQUIRED		0x00000020
+#define FAPO_FLAG_INPLACE_SUPPORTED		0x00000010
+
+/* FAPO Interface */
+
+#ifndef FAPO_DECL
+#define FAPO_DECL
+typedef struct FAPO FAPO;
+#endif /* FAPO_DECL */
+
+typedef int32_t (FAPOCALL * AddRefFunc)(
+	void *fapo
+);
+typedef int32_t (FAPOCALL * ReleaseFunc)(
+	void *fapo
+);
+typedef uint32_t (FAPOCALL * GetRegistrationPropertiesFunc)(
+	void* fapo,
+	FAPORegistrationProperties **ppRegistrationProperties
+);
+typedef uint32_t (FAPOCALL * IsInputFormatSupportedFunc)(
+	void* fapo,
+	const FAudioWaveFormatEx *pOutputFormat,
+	const FAudioWaveFormatEx *pRequestedInputFormat,
+	FAudioWaveFormatEx **ppSupportedInputFormat
+);
+typedef uint32_t (FAPOCALL * IsOutputFormatSupportedFunc)(
+	void* fapo,
+	const FAudioWaveFormatEx *pInputFormat,
+	const FAudioWaveFormatEx *pRequestedOutputFormat,
+	FAudioWaveFormatEx **ppSupportedOutputFormat
+);
+typedef uint32_t (FAPOCALL * InitializeFunc)(
+	void* fapo,
+	const void* pData,
+	uint32_t DataByteSize
+);
+typedef void (FAPOCALL * ResetFunc)(
+	void* fapo
+);
+typedef uint32_t (FAPOCALL * LockForProcessFunc)(
+	void* fapo,
+	uint32_t InputLockedParameterCount,
+	const FAPOLockForProcessBufferParameters *pInputLockedParameters,
+	uint32_t OutputLockedParameterCount,
+	const FAPOLockForProcessBufferParameters *pOutputLockedParameters
+);
+typedef void (FAPOCALL * UnlockForProcessFunc)(
+	void* fapo
+);
+typedef void (FAPOCALL * ProcessFunc)(
+	void* fapo,
+	uint32_t InputProcessParameterCount,
+	const FAPOProcessBufferParameters* pInputProcessParameters,
+	uint32_t OutputProcessParameterCount,
+	FAPOProcessBufferParameters* pOutputProcessParameters,
+	int32_t IsEnabled
+);
+typedef uint32_t (FAPOCALL * CalcInputFramesFunc)(
+	void* fapo,
+	uint32_t OutputFrameCount
+);
+typedef uint32_t (FAPOCALL * CalcOutputFramesFunc)(
+	void* fapo,
+	uint32_t InputFrameCount
+);
+typedef void (FAPOCALL * SetParametersFunc)(
+	void* fapo,
+	const void* pParameters,
+	uint32_t ParameterByteSize
+);
+typedef void (FAPOCALL * GetParametersFunc)(
+	void* fapo,
+	void* pParameters,
+	uint32_t ParameterByteSize
+);
+
+struct FAPO
+{
+	AddRefFunc AddRef;
+	ReleaseFunc Release;
+	GetRegistrationPropertiesFunc GetRegistrationProperties;
+	IsInputFormatSupportedFunc IsInputFormatSupported;
+	IsOutputFormatSupportedFunc IsOutputFormatSupported;
+	InitializeFunc Initialize;
+	ResetFunc Reset;
+	LockForProcessFunc LockForProcess;
+	UnlockForProcessFunc UnlockForProcess;
+	ProcessFunc Process;
+	CalcInputFramesFunc CalcInputFrames;
+	CalcOutputFramesFunc CalcOutputFrames;
+	SetParametersFunc SetParameters;
+	GetParametersFunc GetParameters;
+};
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* FAPO_H */
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/include/FAPOBase.h b/libs/faudio/include/FAPOBase.h
new file mode 100644
index 00000000000..c2fcdd2981c
--- /dev/null
+++ b/libs/faudio/include/FAPOBase.h
@@ -0,0 +1,264 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+/* This file has no documentation since the MSDN docs are still perfectly fine:
+ * https://docs.microsoft.com/en-us/windows/desktop/api/xapobase/
+ *
+ * Of course, the APIs aren't exactly the same since XAPO is super dependent on
+ * C++. Instead, we use a struct full of functions to mimic a vtable.
+ *
+ * To mimic the CXAPOParametersBase experience, initialize the object like this:
+ *
+ * extern FAPORegistrationProperties MyFAPOProperties;
+ * extern int32_t producer;
+ * typedef struct MyFAPOParams
+ * {
+ *	uint32_t something;
+ * } MyFAPOParams;
+ * typedef struct MyFAPO
+ * {
+ *	FAPOBase base;
+ *	uint32_t somethingElse;
+ * } MyFAPO;
+ * void MyFAPO_Free(void* fapo)
+ * {
+ *	MyFAPO *mine = (MyFAPO*) fapo;
+ *	mine->base.pFree(mine->base.m_pParameterBlocks);
+ *	mine->base.pFree(fapo);
+ * }
+ *
+ * MyFAPO *result = (MyFAPO*) SDL_malloc(sizeof(MyFAPO));
+ * uint8_t *params = (uint8_t*) SDL_malloc(sizeof(MyFAPOParams) * 3);
+ * CreateFAPOBase(
+ *	&result->base,
+ *	&MyFAPOProperties,
+ *	params,
+ *	sizeof(MyFAPOParams),
+ *	producer
+ * );
+ * result->base.base.Initialize = (InitializeFunc) MyFAPO_Initialize;
+ * result->base.base.Process = (ProcessFunc) MyFAPO_Process;
+ * result->base.Destructor = MyFAPO_Free;
+ */
+
+#ifndef FAPOBASE_H
+#define FAPOBASE_H
+
+#include "FAPO.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Constants */
+
+#define FAPOBASE_DEFAULT_FORMAT_TAG		FAUDIO_FORMAT_IEEE_FLOAT
+#define FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS	FAPO_MIN_CHANNELS
+#define FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS	FAPO_MAX_CHANNELS
+#define FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE	FAPO_MIN_FRAMERATE
+#define FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE	FAPO_MAX_FRAMERATE
+#define FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE	32
+
+#define FAPOBASE_DEFAULT_FLAG ( \
+	FAPO_FLAG_CHANNELS_MUST_MATCH | \
+	FAPO_FLAG_FRAMERATE_MUST_MATCH | \
+	FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH | \
+	FAPO_FLAG_BUFFERCOUNT_MUST_MATCH | \
+	FAPO_FLAG_INPLACE_SUPPORTED \
+)
+
+#define FAPOBASE_DEFAULT_BUFFER_COUNT 1
+
+/* FAPOBase Interface */
+
+typedef struct FAPOBase FAPOBase;
+
+typedef void (FAPOCALL * OnSetParametersFunc)(
+	FAPOBase *fapo,
+	const void* parameters,
+	uint32_t parametersSize
+);
+
+#pragma pack(push, 8)
+struct FAPOBase
+{
+	/* Base Classes/Interfaces */
+	FAPO base;
+	void (FAPOCALL *Destructor)(void*);
+
+	/* Public Virtual Functions */
+	OnSetParametersFunc OnSetParameters;
+
+	/* Private Variables */
+	const FAPORegistrationProperties *m_pRegistrationProperties;
+	void* m_pfnMatrixMixFunction;
+	float *m_pfl32MatrixCoefficients;
+	uint32_t m_nSrcFormatType;
+	uint8_t m_fIsScalarMatrix;
+	uint8_t m_fIsLocked;
+	uint8_t *m_pParameterBlocks;
+	uint8_t *m_pCurrentParameters;
+	uint8_t *m_pCurrentParametersInternal;
+	uint32_t m_uCurrentParametersIndex;
+	uint32_t m_uParameterBlockByteSize;
+	uint8_t m_fNewerResultsReady;
+	uint8_t m_fProducer;
+
+	/* Protected Variables */
+	int32_t m_lReferenceCount; /* LONG */
+
+	/* Allocator callbacks, NOT part of XAPOBase spec! */
+	FAudioMallocFunc pMalloc;
+	FAudioFreeFunc pFree;
+	FAudioReallocFunc pRealloc;
+};
+#pragma pack(pop)
+
+FAPOAPI void CreateFAPOBase(
+	FAPOBase *fapo,
+	const FAPORegistrationProperties *pRegistrationProperties,
+	uint8_t *pParameterBlocks,
+	uint32_t uParameterBlockByteSize,
+	uint8_t fProducer
+);
+
+/* See "extensions/CustomAllocatorEXT.txt" for more information. */
+FAPOAPI void CreateFAPOBaseWithCustomAllocatorEXT(
+	FAPOBase *fapo,
+	const FAPORegistrationProperties *pRegistrationProperties,
+	uint8_t *pParameterBlocks,
+	uint32_t uParameterBlockByteSize,
+	uint8_t fProducer,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc
+);
+
+FAPOAPI int32_t FAPOBase_AddRef(FAPOBase *fapo);
+
+FAPOAPI int32_t FAPOBase_Release(FAPOBase *fapo);
+
+FAPOAPI uint32_t FAPOBase_GetRegistrationProperties(
+	FAPOBase *fapo,
+	FAPORegistrationProperties **ppRegistrationProperties
+);
+
+FAPOAPI uint32_t FAPOBase_IsInputFormatSupported(
+	FAPOBase *fapo,
+	const FAudioWaveFormatEx *pOutputFormat,
+	const FAudioWaveFormatEx *pRequestedInputFormat,
+	FAudioWaveFormatEx **ppSupportedInputFormat
+);
+
+FAPOAPI uint32_t FAPOBase_IsOutputFormatSupported(
+	FAPOBase *fapo,
+	const FAudioWaveFormatEx *pInputFormat,
+	const FAudioWaveFormatEx *pRequestedOutputFormat,
+	FAudioWaveFormatEx **ppSupportedOutputFormat
+);
+
+FAPOAPI uint32_t FAPOBase_Initialize(
+	FAPOBase *fapo,
+	const void* pData,
+	uint32_t DataByteSize
+);
+
+FAPOAPI void FAPOBase_Reset(FAPOBase *fapo);
+
+FAPOAPI uint32_t FAPOBase_LockForProcess(
+	FAPOBase *fapo,
+	uint32_t InputLockedParameterCount,
+	const FAPOLockForProcessBufferParameters *pInputLockedParameters,
+	uint32_t OutputLockedParameterCount,
+	const FAPOLockForProcessBufferParameters *pOutputLockedParameters
+);
+
+FAPOAPI void FAPOBase_UnlockForProcess(FAPOBase *fapo);
+
+FAPOAPI uint32_t FAPOBase_CalcInputFrames(
+	FAPOBase *fapo,
+	uint32_t OutputFrameCount
+);
+
+FAPOAPI uint32_t FAPOBase_CalcOutputFrames(
+	FAPOBase *fapo,
+	uint32_t InputFrameCount
+);
+
+FAPOAPI uint32_t FAPOBase_ValidateFormatDefault(
+	FAPOBase *fapo,
+	FAudioWaveFormatEx *pFormat,
+	uint8_t fOverwrite
+);
+
+FAPOAPI uint32_t FAPOBase_ValidateFormatPair(
+	FAPOBase *fapo,
+	const FAudioWaveFormatEx *pSupportedFormat,
+	FAudioWaveFormatEx *pRequestedFormat,
+	uint8_t fOverwrite
+);
+
+FAPOAPI void FAPOBase_ProcessThru(
+	FAPOBase *fapo,
+	void* pInputBuffer,
+	float *pOutputBuffer,
+	uint32_t FrameCount,
+	uint16_t InputChannelCount,
+	uint16_t OutputChannelCount,
+	uint8_t MixWithOutput
+);
+
+FAPOAPI void FAPOBase_SetParameters(
+	FAPOBase *fapo,
+	const void* pParameters,
+	uint32_t ParameterByteSize
+);
+
+FAPOAPI void FAPOBase_GetParameters(
+	FAPOBase *fapo,
+	void* pParameters,
+	uint32_t ParameterByteSize
+);
+
+FAPOAPI void FAPOBase_OnSetParameters(
+	FAPOBase *fapo,
+	const void* parameters,
+	uint32_t parametersSize
+);
+
+FAPOAPI uint8_t FAPOBase_ParametersChanged(FAPOBase *fapo);
+
+FAPOAPI uint8_t* FAPOBase_BeginProcess(FAPOBase *fapo);
+
+FAPOAPI void FAPOBase_EndProcess(FAPOBase *fapo);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* FAPOBASE_H */
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/include/FAPOFX.h b/libs/faudio/include/FAPOFX.h
new file mode 100644
index 00000000000..5aae441b2cc
--- /dev/null
+++ b/libs/faudio/include/FAPOFX.h
@@ -0,0 +1,178 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#ifndef FAPOFX_H
+#define FAPOFX_H
+
+#include "FAPO.h"
+
+#define FAPOFXAPI FAUDIOAPI
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* GUIDs */
+
+/* "Legacy" GUIDs are from XAPOFX <= 1.5. They were removed in XAudio 2.8 and later. */
+extern const FAudioGUID FAPOFX_CLSID_FXEQ, FAPOFX_CLSID_FXEQ_LEGACY;
+extern const FAudioGUID FAPOFX_CLSID_FXMasteringLimiter, FAPOFX_CLSID_FXMasteringLimiter_LEGACY;
+extern const FAudioGUID FAPOFX_CLSID_FXReverb, FAPOFX_CLSID_FXReverb_LEGACY;
+extern const FAudioGUID FAPOFX_CLSID_FXEcho, FAPOFX_CLSID_FXEcho_LEGACY;
+
+/* Structures */
+
+#pragma pack(push, 1)
+
+/* See FAPOFXEQ_* constants below.
+ * FrequencyCenter is in Hz, Gain is amplitude ratio, Bandwidth is Q factor.
+ */
+typedef struct FAPOFXEQParameters
+{
+	float FrequencyCenter0;
+	float Gain0;
+	float Bandwidth0;
+	float FrequencyCenter1;
+	float Gain1;
+	float Bandwidth1;
+	float FrequencyCenter2;
+	float Gain2;
+	float Bandwidth2;
+	float FrequencyCenter3;
+	float Gain3;
+	float Bandwidth3;
+} FAPOFXEQParameters;
+
+/* See FAPOFXMASTERINGLIMITER_* constants below. */
+typedef struct FAPOFXMasteringLimiterParameters
+{
+	uint32_t Release;	/* In milliseconds */
+	uint32_t Loudness;	/* In... uh, MSDN doesn't actually say what. */
+} FAPOFXMasteringLimiterParameters;
+
+/* See FAPOFXREVERB_* constants below.
+ * Both parameters are arbitrary and should be treated subjectively.
+ */
+typedef struct FAPOFXReverbParameters
+{
+	float Diffusion;
+	float RoomSize;
+} FAPOFXReverbParameters;
+
+/* See FAPOFXECHO_* constants below. */
+typedef struct FAPOFXEchoParameters
+{
+	float WetDryMix;	/* Percentage of processed signal vs original */
+	float Feedback;		/* Percentage to feed back into input */
+	float Delay;		/* In milliseconds */
+} FAPOFXEchoParameters;
+
+#pragma pack(pop)
+
+/* Constants */
+
+#define FAPOFXEQ_MIN_FRAMERATE 22000
+#define FAPOFXEQ_MAX_FRAMERATE 48000
+
+#define FAPOFXEQ_MIN_FREQUENCY_CENTER		20.0f
+#define FAPOFXEQ_MAX_FREQUENCY_CENTER		20000.0f
+#define FAPOFXEQ_DEFAULT_FREQUENCY_CENTER_0	100.0f
+#define FAPOFXEQ_DEFAULT_FREQUENCY_CENTER_1	800.0f
+#define FAPOFXEQ_DEFAULT_FREQUENCY_CENTER_2	2000.0f
+#define FAPOFXEQ_DEFAULT_FREQUENCY_CENTER_3	10000.0f
+
+#define FAPOFXEQ_MIN_GAIN	0.126f
+#define FAPOFXEQ_MAX_GAIN	7.94f
+#define FAPOFXEQ_DEFAULT_GAIN	1.0f
+
+#define FAPOFXEQ_MIN_BANDWIDTH		0.1f
+#define FAPOFXEQ_MAX_BANDWIDTH		2.0f
+#define FAPOFXEQ_DEFAULT_BANDWIDTH	1.0f
+
+#define FAPOFXMASTERINGLIMITER_MIN_RELEASE	1
+#define FAPOFXMASTERINGLIMITER_MAX_RELEASE	20
+#define FAPOFXMASTERINGLIMITER_DEFAULT_RELEASE	6
+
+#define FAPOFXMASTERINGLIMITER_MIN_LOUDNESS	1
+#define FAPOFXMASTERINGLIMITER_MAX_LOUDNESS	1800
+#define FAPOFXMASTERINGLIMITER_DEFAULT_LOUDNESS	1000
+
+#define FAPOFXREVERB_MIN_DIFFUSION	0.0f
+#define FAPOFXREVERB_MAX_DIFFUSION	1.0f
+#define FAPOFXREVERB_DEFAULT_DIFFUSION	0.9f
+
+#define FAPOFXREVERB_MIN_ROOMSIZE	0.0001f
+#define FAPOFXREVERB_MAX_ROOMSIZE	1.0f
+#define FAPOFXREVERB_DEFAULT_ROOMSIZE	0.6f
+
+#define FAPOFXECHO_MIN_WETDRYMIX	0.0f
+#define FAPOFXECHO_MAX_WETDRYMIX	1.0f
+#define FAPOFXECHO_DEFAULT_WETDRYMIX	0.5f
+
+#define FAPOFXECHO_MIN_FEEDBACK		0.0f
+#define FAPOFXECHO_MAX_FEEDBACK		1.0f
+#define FAPOFXECHO_DEFAULT_FEEDBACK	0.5f
+
+#define FAPOFXECHO_MIN_DELAY		1.0f
+#define FAPOFXECHO_MAX_DELAY		2000.0f
+#define FAPOFXECHO_DEFAULT_DELAY	500.0f
+
+/* Functions */
+
+/* Creates an effect from the pre-made FAPOFX effect library.
+ *
+ * clsid:		A reference to one of the FAPOFX_CLSID_* GUIDs
+ * pEffect:		Filled with the resulting FAPO object
+ * pInitData:		Starting parameters, pass NULL to use the default values
+ * InitDataByteSize:	Parameter struct size, pass 0 if pInitData is NULL
+ *
+ * Returns 0 on success.
+ */
+FAPOFXAPI uint32_t FAPOFX_CreateFX(
+	const FAudioGUID *clsid,
+	FAPO **pEffect,
+	const void *pInitData,
+	uint32_t InitDataByteSize
+);
+
+/* See "extensions/CustomAllocatorEXT.txt" for more details. */
+FAPOFXAPI uint32_t FAPOFX_CreateFXWithCustomAllocatorEXT(
+	const FAudioGUID *clsid,
+	FAPO **pEffect,
+	const void *pInitData,
+	uint32_t InitDataByteSize,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc
+);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* FAPOFX_H */
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/include/FAudio.h b/libs/faudio/include/FAudio.h
new file mode 100644
index 00000000000..046e7f4098c
--- /dev/null
+++ b/libs/faudio/include/FAudio.h
@@ -0,0 +1,1322 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#ifndef FAUDIO_H
+#define FAUDIO_H
+
+#ifdef _WIN32
+#define FAUDIOAPI
+#define FAUDIOCALL
+#else
+#define FAUDIOAPI
+#define FAUDIOCALL
+#endif
+
+#ifdef _MSC_VER
+#define FAUDIODEPRECATED(msg) __declspec(deprecated(msg))
+#else
+#define FAUDIODEPRECATED(msg) __attribute__((deprecated(msg)))
+#endif
+
+/* -Wpedantic nameless union/struct silencing */
+#ifndef FAUDIONAMELESS
+#ifdef __GNUC__
+#define FAUDIONAMELESS __extension__
+#else
+#define FAUDIONAMELESS
+#endif /* __GNUC__ */
+#endif /* FAUDIONAMELESS */
+
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Type Declarations */
+
+typedef struct FAudio FAudio;
+typedef struct FAudioVoice FAudioVoice;
+typedef FAudioVoice FAudioSourceVoice;
+typedef FAudioVoice FAudioSubmixVoice;
+typedef FAudioVoice FAudioMasteringVoice;
+typedef struct FAudioEngineCallback FAudioEngineCallback;
+typedef struct FAudioVoiceCallback FAudioVoiceCallback;
+
+/* Enumerations */
+
+typedef enum FAudioDeviceRole
+{
+	FAudioNotDefaultDevice =		0x0,
+	FAudioDefaultConsoleDevice =		0x1,
+	FAudioDefaultMultimediaDevice =		0x2,
+	FAudioDefaultCommunicationsDevice =	0x4,
+	FAudioDefaultGameDevice =		0x8,
+	FAudioGlobalDefaultDevice =		0xF,
+	FAudioInvalidDeviceRole = ~FAudioGlobalDefaultDevice
+} FAudioDeviceRole;
+
+typedef enum FAudioFilterType
+{
+	FAudioLowPassFilter,
+	FAudioBandPassFilter,
+	FAudioHighPassFilter,
+	FAudioNotchFilter
+} FAudioFilterType;
+
+typedef enum FAudioStreamCategory
+{
+	FAudioStreamCategory_Other,
+	FAudioStreamCategory_ForegroundOnlyMedia,
+	FAudioStreamCategory_BackgroundCapableMedia,
+	FAudioStreamCategory_Communications,
+	FAudioStreamCategory_Alerts,
+	FAudioStreamCategory_SoundEffects,
+	FAudioStreamCategory_GameEffects,
+	FAudioStreamCategory_GameMedia,
+	FAudioStreamCategory_GameChat,
+	FAudioStreamCategory_Speech,
+	FAudioStreamCategory_Movie,
+	FAudioStreamCategory_Media
+} FAudioStreamCategory;
+
+/* FIXME: The original enum violates ISO C and is platform specific anyway... */
+typedef uint32_t FAudioProcessor;
+#define FAUDIO_DEFAULT_PROCESSOR 0xFFFFFFFF
+
+/* Structures */
+
+#pragma pack(push, 1)
+
+typedef struct FAudioGUID
+{
+	uint32_t Data1;
+	uint16_t Data2;
+	uint16_t Data3;
+	uint8_t Data4[8];
+} FAudioGUID;
+
+/* See MSDN:
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/dd390970%28v=vs.85%29.aspx
+ */
+typedef struct FAudioWaveFormatEx
+{
+	uint16_t wFormatTag;
+	uint16_t nChannels;
+	uint32_t nSamplesPerSec;
+	uint32_t nAvgBytesPerSec;
+	uint16_t nBlockAlign;
+	uint16_t wBitsPerSample;
+	uint16_t cbSize;
+} FAudioWaveFormatEx;
+
+/* See MSDN:
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/dd390971(v=vs.85).aspx
+ */
+typedef struct FAudioWaveFormatExtensible
+{
+	FAudioWaveFormatEx Format;
+	union
+	{
+		uint16_t wValidBitsPerSample;
+		uint16_t wSamplesPerBlock;
+		uint16_t wReserved;
+	} Samples;
+	uint32_t dwChannelMask;
+	FAudioGUID SubFormat;
+} FAudioWaveFormatExtensible;
+
+typedef struct FAudioADPCMCoefSet
+{
+	int16_t iCoef1;
+	int16_t iCoef2;
+} FAudioADPCMCoefSet;
+
+typedef struct FAudioADPCMWaveFormat
+{
+	FAudioWaveFormatEx wfx;
+	uint16_t wSamplesPerBlock;
+	uint16_t wNumCoef;
+
+	/* MSVC warns on empty arrays in structs */
+	#ifdef _MSC_VER
+	#pragma warning(push)
+	#pragma warning(disable: 4200)
+	#endif
+
+	FAudioADPCMCoefSet aCoef[];
+	/* MSADPCM has 7 coefficient pairs:
+	 * {
+	 *	{ 256,    0 },
+	 *	{ 512, -256 },
+	 *	{   0,    0 },
+	 *	{ 192,   64 },
+	 *	{ 240,    0 },
+	 *	{ 460, -208 },
+	 *	{ 392, -232 }
+	 * }
+	 */
+
+	#ifdef _MSC_VER
+	#pragma warning(pop)
+	#endif
+} FAudioADPCMWaveFormat;
+
+typedef struct FAudioDeviceDetails
+{
+	int16_t DeviceID[256]; /* Win32 wchar_t */
+	int16_t DisplayName[256]; /* Win32 wchar_t */
+	FAudioDeviceRole Role;
+	FAudioWaveFormatExtensible OutputFormat;
+} FAudioDeviceDetails;
+
+typedef struct FAudioVoiceDetails
+{
+	uint32_t CreationFlags;
+	uint32_t ActiveFlags;
+	uint32_t InputChannels;
+	uint32_t InputSampleRate;
+} FAudioVoiceDetails;
+
+typedef struct FAudioSendDescriptor
+{
+	uint32_t Flags; /* 0 or FAUDIO_SEND_USEFILTER */
+	FAudioVoice *pOutputVoice;
+} FAudioSendDescriptor;
+
+typedef struct FAudioVoiceSends
+{
+	uint32_t SendCount;
+	FAudioSendDescriptor *pSends;
+} FAudioVoiceSends;
+
+#ifndef FAPO_DECL
+#define FAPO_DECL
+typedef struct FAPO FAPO;
+#endif /* FAPO_DECL */
+
+typedef struct FAudioEffectDescriptor
+{
+	FAPO *pEffect;
+	int32_t InitialState; /* 1 - Enabled, 0 - Disabled */
+	uint32_t OutputChannels;
+} FAudioEffectDescriptor;
+
+typedef struct FAudioEffectChain
+{
+	uint32_t EffectCount;
+	FAudioEffectDescriptor *pEffectDescriptors;
+} FAudioEffectChain;
+
+typedef struct FAudioFilterParameters
+{
+	FAudioFilterType Type;
+	float Frequency;	/* [0, FAUDIO_MAX_FILTER_FREQUENCY] */
+	float OneOverQ;		/* [0, FAUDIO_MAX_FILTER_ONEOVERQ] */
+} FAudioFilterParameters;
+
+typedef struct FAudioBuffer
+{
+	/* Either 0 or FAUDIO_END_OF_STREAM */
+	uint32_t Flags;
+	/* Pointer to wave data, memory block size.
+	 * Note that pAudioData is not copied; FAudio reads directly from your
+	 * pointer! This pointer must be valid until FAudio has finished using
+	 * it, at which point an OnBufferEnd callback will be generated.
+	 */
+	uint32_t AudioBytes;
+	const uint8_t *pAudioData;
+	/* Play region, in sample frames. */
+	uint32_t PlayBegin;
+	uint32_t PlayLength;
+	/* Loop region, in sample frames.
+	 * This can be used to loop a subregion of the wave instead of looping
+	 * the whole thing, i.e. if you have an intro/outro you can set these
+	 * to loop the middle sections instead. If you don't need this, set both
+	 * values to 0.
+	 */
+	uint32_t LoopBegin;
+	uint32_t LoopLength;
+	/* [0, FAUDIO_LOOP_INFINITE] */
+	uint32_t LoopCount;
+	/* This is sent to callbacks as pBufferContext */
+	void *pContext;
+} FAudioBuffer;
+
+typedef struct FAudioBufferWMA
+{
+	const uint32_t *pDecodedPacketCumulativeBytes;
+	uint32_t PacketCount;
+} FAudioBufferWMA;
+
+typedef struct FAudioVoiceState
+{
+	void *pCurrentBufferContext;
+	uint32_t BuffersQueued;
+	uint64_t SamplesPlayed;
+} FAudioVoiceState;
+
+typedef struct FAudioPerformanceData
+{
+	uint64_t AudioCyclesSinceLastQuery;
+	uint64_t TotalCyclesSinceLastQuery;
+	uint32_t MinimumCyclesPerQuantum;
+	uint32_t MaximumCyclesPerQuantum;
+	uint32_t MemoryUsageInBytes;
+	uint32_t CurrentLatencyInSamples;
+	uint32_t GlitchesSinceEngineStarted;
+	uint32_t ActiveSourceVoiceCount;
+	uint32_t TotalSourceVoiceCount;
+	uint32_t ActiveSubmixVoiceCount;
+	uint32_t ActiveResamplerCount;
+	uint32_t ActiveMatrixMixCount;
+	uint32_t ActiveXmaSourceVoices;
+	uint32_t ActiveXmaStreams;
+} FAudioPerformanceData;
+
+typedef struct FAudioDebugConfiguration
+{
+	/* See FAUDIO_LOG_* */
+	uint32_t TraceMask;
+	uint32_t BreakMask;
+	/* 0 or 1 */
+	int32_t LogThreadID;
+	int32_t LogFileline;
+	int32_t LogFunctionName;
+	int32_t LogTiming;
+} FAudioDebugConfiguration;
+
+#pragma pack(pop)
+
+/* This ISN'T packed. Strictly speaking it wouldn't have mattered anyway but eh.
+ * See https://github.com/microsoft/DirectXTK/issues/256
+ */
+typedef struct FAudioXMA2WaveFormatEx
+{
+	FAudioWaveFormatEx wfx;
+	uint16_t wNumStreams;
+	uint32_t dwChannelMask;
+	uint32_t dwSamplesEncoded;
+	uint32_t dwBytesPerBlock;
+	uint32_t dwPlayBegin;
+	uint32_t dwPlayLength;
+	uint32_t dwLoopBegin;
+	uint32_t dwLoopLength;
+	uint8_t  bLoopCount;
+	uint8_t  bEncoderVersion;
+	uint16_t wBlockCount;
+} FAudioXMA2WaveFormat;
+
+/* Constants */
+
+#define FAUDIO_E_OUT_OF_MEMORY		0x8007000e
+#define FAUDIO_E_INVALID_ARG		0x80070057
+#define FAUDIO_E_UNSUPPORTED_FORMAT	0x88890008
+#define FAUDIO_E_INVALID_CALL		0x88960001
+#define FAUDIO_E_DEVICE_INVALIDATED	0x88960004
+#define FAPO_E_FORMAT_UNSUPPORTED	0x88970001
+
+#define FAUDIO_MAX_BUFFER_BYTES		0x80000000
+#define FAUDIO_MAX_QUEUED_BUFFERS	64
+#define FAUDIO_MAX_AUDIO_CHANNELS	64
+#define FAUDIO_MIN_SAMPLE_RATE		1000
+#define FAUDIO_MAX_SAMPLE_RATE		200000
+#define FAUDIO_MAX_VOLUME_LEVEL		16777216.0f
+#define FAUDIO_MIN_FREQ_RATIO		(1.0f / 1024.0f)
+#define FAUDIO_MAX_FREQ_RATIO		1024.0f
+#define FAUDIO_DEFAULT_FREQ_RATIO	2.0f
+#define FAUDIO_MAX_FILTER_ONEOVERQ	1.5f
+#define FAUDIO_MAX_FILTER_FREQUENCY	1.0f
+#define FAUDIO_MAX_LOOP_COUNT		254
+
+#define FAUDIO_COMMIT_NOW		0
+#define FAUDIO_COMMIT_ALL		0
+#define FAUDIO_INVALID_OPSET		(uint32_t) (-1)
+#define FAUDIO_NO_LOOP_REGION		0
+#define FAUDIO_LOOP_INFINITE		255
+#define FAUDIO_DEFAULT_CHANNELS		0
+#define FAUDIO_DEFAULT_SAMPLERATE	0
+
+#define FAUDIO_DEBUG_ENGINE		0x0001
+#define FAUDIO_VOICE_NOPITCH		0x0002
+#define FAUDIO_VOICE_NOSRC		0x0004
+#define FAUDIO_VOICE_USEFILTER		0x0008
+#define FAUDIO_VOICE_MUSIC		0x0010
+#define FAUDIO_PLAY_TAILS		0x0020
+#define FAUDIO_END_OF_STREAM		0x0040
+#define FAUDIO_SEND_USEFILTER		0x0080
+#define FAUDIO_VOICE_NOSAMPLESPLAYED	0x0100
+#define FAUDIO_1024_QUANTUM		0x8000
+
+#define FAUDIO_DEFAULT_FILTER_TYPE	FAudioLowPassFilter
+#define FAUDIO_DEFAULT_FILTER_FREQUENCY	FAUDIO_MAX_FILTER_FREQUENCY
+#define FAUDIO_DEFAULT_FILTER_ONEOVERQ	1.0f
+
+#define FAUDIO_LOG_ERRORS		0x0001
+#define FAUDIO_LOG_WARNINGS		0x0002
+#define FAUDIO_LOG_INFO			0x0004
+#define FAUDIO_LOG_DETAIL		0x0008
+#define FAUDIO_LOG_API_CALLS		0x0010
+#define FAUDIO_LOG_FUNC_CALLS		0x0020
+#define FAUDIO_LOG_TIMING		0x0040
+#define FAUDIO_LOG_LOCKS		0x0080
+#define FAUDIO_LOG_MEMORY		0x0100
+#define FAUDIO_LOG_STREAMING		0x1000
+
+#ifndef _SPEAKER_POSITIONS_
+#define SPEAKER_FRONT_LEFT		0x00000001
+#define SPEAKER_FRONT_RIGHT		0x00000002
+#define SPEAKER_FRONT_CENTER		0x00000004
+#define SPEAKER_LOW_FREQUENCY		0x00000008
+#define SPEAKER_BACK_LEFT		0x00000010
+#define SPEAKER_BACK_RIGHT		0x00000020
+#define SPEAKER_FRONT_LEFT_OF_CENTER	0x00000040
+#define SPEAKER_FRONT_RIGHT_OF_CENTER	0x00000080
+#define SPEAKER_BACK_CENTER		0x00000100
+#define SPEAKER_SIDE_LEFT		0x00000200
+#define SPEAKER_SIDE_RIGHT		0x00000400
+#define SPEAKER_TOP_CENTER		0x00000800
+#define SPEAKER_TOP_FRONT_LEFT		0x00001000
+#define SPEAKER_TOP_FRONT_CENTER	0x00002000
+#define SPEAKER_TOP_FRONT_RIGHT		0x00004000
+#define SPEAKER_TOP_BACK_LEFT		0x00008000
+#define SPEAKER_TOP_BACK_CENTER		0x00010000
+#define SPEAKER_TOP_BACK_RIGHT		0x00020000
+#define _SPEAKER_POSITIONS_
+#endif
+
+#ifndef SPEAKER_MONO
+#define SPEAKER_MONO	SPEAKER_FRONT_CENTER
+#define SPEAKER_STEREO	(SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT)
+#define SPEAKER_2POINT1 \
+	(	SPEAKER_FRONT_LEFT	| \
+		SPEAKER_FRONT_RIGHT	| \
+		SPEAKER_LOW_FREQUENCY	)
+#define SPEAKER_SURROUND \
+	(	SPEAKER_FRONT_LEFT	| \
+		SPEAKER_FRONT_RIGHT	| \
+		SPEAKER_FRONT_CENTER	| \
+		SPEAKER_BACK_CENTER	)
+#define SPEAKER_QUAD \
+	(	SPEAKER_FRONT_LEFT	| \
+		SPEAKER_FRONT_RIGHT	| \
+		SPEAKER_BACK_LEFT	| \
+		SPEAKER_BACK_RIGHT	)
+#define SPEAKER_4POINT1 \
+	(	SPEAKER_FRONT_LEFT	| \
+		SPEAKER_FRONT_RIGHT	| \
+		SPEAKER_LOW_FREQUENCY	| \
+		SPEAKER_BACK_LEFT	| \
+		SPEAKER_BACK_RIGHT	)
+#define SPEAKER_5POINT1 \
+	(	SPEAKER_FRONT_LEFT	| \
+		SPEAKER_FRONT_RIGHT	| \
+		SPEAKER_FRONT_CENTER	| \
+		SPEAKER_LOW_FREQUENCY	| \
+		SPEAKER_BACK_LEFT	| \
+		SPEAKER_BACK_RIGHT	)
+#define SPEAKER_7POINT1 \
+	(	SPEAKER_FRONT_LEFT		| \
+		SPEAKER_FRONT_RIGHT		| \
+		SPEAKER_FRONT_CENTER		| \
+		SPEAKER_LOW_FREQUENCY		| \
+		SPEAKER_BACK_LEFT		| \
+		SPEAKER_BACK_RIGHT		| \
+		SPEAKER_FRONT_LEFT_OF_CENTER	| \
+		SPEAKER_FRONT_RIGHT_OF_CENTER	)
+#define SPEAKER_5POINT1_SURROUND \
+	(	SPEAKER_FRONT_LEFT	| \
+		SPEAKER_FRONT_RIGHT	| \
+		SPEAKER_FRONT_CENTER	| \
+		SPEAKER_LOW_FREQUENCY	| \
+		SPEAKER_SIDE_LEFT	| \
+		SPEAKER_SIDE_RIGHT	)
+#define SPEAKER_7POINT1_SURROUND \
+	(	SPEAKER_FRONT_LEFT	| \
+		SPEAKER_FRONT_RIGHT	| \
+		SPEAKER_FRONT_CENTER	| \
+		SPEAKER_LOW_FREQUENCY	| \
+		SPEAKER_BACK_LEFT	| \
+		SPEAKER_BACK_RIGHT	| \
+		SPEAKER_SIDE_LEFT	| \
+		SPEAKER_SIDE_RIGHT	)
+#define SPEAKER_XBOX SPEAKER_5POINT1
+#endif
+
+#define FAUDIO_FORMAT_PCM		1
+#define FAUDIO_FORMAT_MSADPCM		2
+#define FAUDIO_FORMAT_IEEE_FLOAT	3
+#define FAUDIO_FORMAT_WMAUDIO2		0x0161
+#define FAUDIO_FORMAT_WMAUDIO3		0x0162
+#define FAUDIO_FORMAT_WMAUDIO_LOSSLESS	0x0163
+#define FAUDIO_FORMAT_XMAUDIO2		0x0166
+#define FAUDIO_FORMAT_EXTENSIBLE	0xFFFE
+
+extern FAudioGUID DATAFORMAT_SUBTYPE_PCM;
+extern FAudioGUID DATAFORMAT_SUBTYPE_IEEE_FLOAT;
+
+/* FAudio Version API */
+
+#define FAUDIO_TARGET_VERSION 8 /* Targeting compatibility with XAudio 2.8 */
+
+#define FAUDIO_ABI_VERSION	 0
+#define FAUDIO_MAJOR_VERSION	21
+#define FAUDIO_MINOR_VERSION	10
+#define FAUDIO_PATCH_VERSION	 0
+
+#define FAUDIO_COMPILED_VERSION ( \
+	(FAUDIO_ABI_VERSION * 100 * 100 * 100) + \
+	(FAUDIO_MAJOR_VERSION * 100 * 100) + \
+	(FAUDIO_MINOR_VERSION * 100) + \
+	(FAUDIO_PATCH_VERSION) \
+)
+
+FAUDIOAPI uint32_t FAudioLinkedVersion(void);
+
+/* FAudio Interface */
+
+/* This should be your first FAudio call.
+ *
+ * ppFAudio:		Filled with the FAudio core context.
+ * Flags:		Can be 0 or FAUDIO_DEBUG_ENGINE.
+ * XAudio2Processor:	Set this to FAUDIO_DEFAULT_PROCESSOR.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioCreate(
+	FAudio **ppFAudio,
+	uint32_t Flags,
+	FAudioProcessor XAudio2Processor
+);
+
+/* See "extensions/COMConstructEXT.txt" for more details */
+FAUDIOAPI uint32_t FAudioCOMConstructEXT(FAudio **ppFAudio, uint8_t version);
+
+/* Increments a reference counter. When counter is 0, audio is freed.
+ * Returns the reference count after incrementing.
+ */
+FAUDIOAPI uint32_t FAudio_AddRef(FAudio *audio);
+
+/* Decrements a reference counter. When counter is 0, audio is freed.
+ * Returns the reference count after decrementing.
+ */
+FAUDIOAPI uint32_t FAudio_Release(FAudio *audio);
+
+/* Queries the number of sound devices available for use.
+ *
+ * pCount: Filled with the number of available sound devices.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudio_GetDeviceCount(FAudio *audio, uint32_t *pCount);
+
+/* Gets basic information about a sound device.
+ *
+ * Index:		Can be between 0 and the result of GetDeviceCount.
+ * pDeviceDetails:	Filled with the device information.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudio_GetDeviceDetails(
+	FAudio *audio,
+	uint32_t Index,
+	FAudioDeviceDetails *pDeviceDetails
+);
+
+/* You don't actually have to call this, unless you're using the COM APIs.
+ * See the FAudioCreate API for parameter information.
+ */
+FAUDIOAPI uint32_t FAudio_Initialize(
+	FAudio *audio,
+	uint32_t Flags,
+	FAudioProcessor XAudio2Processor
+);
+
+/* Register a new set of engine callbacks.
+ * There is no limit to the number of sets, but expect performance to degrade
+ * if you have a whole bunch of these. You most likely only need one.
+ *
+ * pCallback: The completely-initialized FAudioEngineCallback structure.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudio_RegisterForCallbacks(
+	FAudio *audio,
+	FAudioEngineCallback *pCallback
+);
+
+/* Remove an active set of engine callbacks.
+ * This checks the pointer value, NOT the callback values!
+ *
+ * pCallback: An FAudioEngineCallback structure previously sent to Register.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI void FAudio_UnregisterForCallbacks(
+	FAudio *audio,
+	FAudioEngineCallback *pCallback
+);
+
+/* Creates a "source" voice, used to play back wavedata.
+ *
+ * ppSourceVoice:	Filled with the source voice pointer.
+ * pSourceFormat:	The input wavedata format, see the documentation for
+ *			FAudioWaveFormatEx.
+ * Flags:		Can be 0 or a mix of the following FAUDIO_VOICE_* flags:
+ *			NOPITCH/NOSRC:	Resampling is disabled. If you set this,
+ *					the source format sample rate MUST match
+ *					the output voices' input sample rates.
+ *					Also, SetFrequencyRatio will fail.
+ *			USEFILTER:	Enables the use of SetFilterParameters.
+ *			MUSIC:		Unsupported.
+ * MaxFrequencyRatio:	AKA your max pitch. This allows us to optimize the size
+ *			of the decode/resample cache sizes. For example, if you
+ *			only expect to raise pitch by a single octave, you can
+ *			set this value to 2.0f. 2.0f is the default value.
+ *			Bounds: [FAUDIO_MIN_FREQ_RATIO, FAUDIO_MAX_FREQ_RATIO].
+ * pCallback:		Voice callbacks, see FAudioVoiceCallback documentation.
+ * pSendList:		List of output voices. If NULL, defaults to master.
+ *			All output voices must have the same sample rate!
+ * pEffectChain:	List of FAPO effects. This value can be NULL.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudio_CreateSourceVoice(
+	FAudio *audio,
+	FAudioSourceVoice **ppSourceVoice,
+	const FAudioWaveFormatEx *pSourceFormat,
+	uint32_t Flags,
+	float MaxFrequencyRatio,
+	FAudioVoiceCallback *pCallback,
+	const FAudioVoiceSends *pSendList,
+	const FAudioEffectChain *pEffectChain
+);
+
+/* Creates a "submix" voice, used to mix/process input voices.
+ * The typical use case for this is to perform CPU-intensive tasks on large
+ * groups of voices all at once. Examples include resampling and FAPO effects.
+ *
+ * ppSubmixVoice:	Filled with the submix voice pointer.
+ * InputChannels:	Input voices will convert to this channel count.
+ * InputSampleRate:	Input voices will convert to this sample rate.
+ * Flags:		Can be 0 or FAUDIO_VOICE_USEFILTER.
+ * ProcessingStage:	If you have multiple submixes that depend on a specific
+ *			order of processing, you can sort them by setting this
+ *			value to prioritize them. For example, submixes with
+ *			stage 0 will process first, then stage 1, 2, and so on.
+ * pSendList:		List of output voices. If NULL, defaults to master.
+ *			All output voices must have the same sample rate!
+ * pEffectChain:	List of FAPO effects. This value can be NULL.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudio_CreateSubmixVoice(
+	FAudio *audio,
+	FAudioSubmixVoice **ppSubmixVoice,
+	uint32_t InputChannels,
+	uint32_t InputSampleRate,
+	uint32_t Flags,
+	uint32_t ProcessingStage,
+	const FAudioVoiceSends *pSendList,
+	const FAudioEffectChain *pEffectChain
+);
+
+/* This should be your second FAudio call, unless you care about which device
+ * you want to use. In that case, see GetDeviceDetails.
+ *
+ * ppMasteringVoice:	Filled with the mastering voice pointer.
+ * InputChannels:	Device channel count. Can be FAUDIO_DEFAULT_CHANNELS.
+ * InputSampleRate:	Device sample rate. Can be FAUDIO_DEFAULT_SAMPLERATE.
+ * Flags:		This value must be 0.
+ * DeviceIndex:		0 for the default device. See GetDeviceCount.
+ * pEffectChain:	List of FAPO effects. This value can be NULL.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudio_CreateMasteringVoice(
+	FAudio *audio,
+	FAudioMasteringVoice **ppMasteringVoice,
+	uint32_t InputChannels,
+	uint32_t InputSampleRate,
+	uint32_t Flags,
+	uint32_t DeviceIndex,
+	const FAudioEffectChain *pEffectChain
+);
+
+/* This is the XAudio 2.8+ version of CreateMasteringVoice.
+ * Right now this doesn't do anything. Don't use this function.
+ */
+FAUDIOAPI uint32_t FAudio_CreateMasteringVoice8(
+	FAudio *audio,
+	FAudioMasteringVoice **ppMasteringVoice,
+	uint32_t InputChannels,
+	uint32_t InputSampleRate,
+	uint32_t Flags,
+	uint16_t *szDeviceId,
+	const FAudioEffectChain *pEffectChain,
+	FAudioStreamCategory StreamCategory
+);
+
+/* Starts the engine, begins processing the audio graph.
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudio_StartEngine(FAudio *audio);
+
+/* Stops the engine and halts all processing.
+ * The audio device will continue to run, but will produce silence.
+ * The graph will be frozen until you call StartEngine, where it will then
+ * resume all processing exactly as it would have had this never been called.
+ */
+FAUDIOAPI void FAudio_StopEngine(FAudio *audio);
+
+/* Flushes a batch of FAudio calls compiled with a given "OperationSet" tag.
+ * This function is based on IXAudio2::CommitChanges from the XAudio2 spec.
+ * This is useful for pushing calls that need to be done perfectly in sync. For
+ * example, if you want to play two separate sources at the exact same time, you
+ * can call FAudioSourceVoice_Start with an OperationSet value of your choice,
+ * then call CommitChanges with that same value to start the sources together.
+ *
+ * OperationSet: Either a value known by you or FAUDIO_COMMIT_ALL
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudio_CommitOperationSet(
+	FAudio *audio,
+	uint32_t OperationSet
+);
+
+/* DO NOT USE THIS FUNCTION OR I SWEAR TO GOD */
+FAUDIODEPRECATED("This function will break your program! Use FAudio_CommitOperationSet instead!")
+FAUDIOAPI uint32_t FAudio_CommitChanges(FAudio *audio);
+
+/* Requests various bits of performance information from the engine.
+ *
+ * pPerfData: Filled with the data. See FAudioPerformanceData for details.
+ */
+FAUDIOAPI void FAudio_GetPerformanceData(
+	FAudio *audio,
+	FAudioPerformanceData *pPerfData
+);
+
+/* When using a Debug binary, this lets you configure what information gets
+ * logged to output. Be careful, this can spit out a LOT of text.
+ *
+ * pDebugConfiguration:	See FAudioDebugConfiguration for details.
+ * pReserved:		Set this to NULL.
+ */
+FAUDIOAPI void FAudio_SetDebugConfiguration(
+	FAudio *audio,
+	FAudioDebugConfiguration *pDebugConfiguration,
+	void* pReserved
+);
+
+/* Requests the values that determine's the engine's update size.
+ * For example, a 48KHz engine with a 1024-sample update period would return
+ * 1024 for the numerator and 48000 for the denominator. With this information,
+ * you can determine the precise update size in milliseconds.
+ *
+ * quantumNumerator - The engine's update size, in sample frames.
+ * quantumDenominator - The engine's sample rate, in Hz
+ */
+FAUDIOAPI void FAudio_GetProcessingQuantum(
+	FAudio *audio,
+	uint32_t *quantumNumerator,
+	uint32_t *quantumDenominator
+);
+
+/* FAudioVoice Interface */
+
+/* Requests basic information about a voice.
+ *
+ * pVoiceDetails: See FAudioVoiceDetails for details.
+ */
+FAUDIOAPI void FAudioVoice_GetVoiceDetails(
+	FAudioVoice *voice,
+	FAudioVoiceDetails *pVoiceDetails
+);
+
+/* Change the output voices for this voice.
+ * This function is invalid for mastering voices.
+ *
+ * pSendList:	List of output voices. If NULL, defaults to master.
+ *		All output voices must have the same sample rate!
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioVoice_SetOutputVoices(
+	FAudioVoice *voice,
+	const FAudioVoiceSends *pSendList
+);
+
+/* Change/Remove the effect chain for this voice.
+ *
+ * pEffectChain:	List of FAPO effects. This value can be NULL.
+ *			Note that the final channel counts for this chain MUST
+ *			match the input/output channel count that was
+ *			determined at voice creation time!
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioVoice_SetEffectChain(
+	FAudioVoice *voice,
+	const FAudioEffectChain *pEffectChain
+);
+
+/* Enables an effect in the effect chain.
+ *
+ * EffectIndex:		The index of the effect (based on the chain order).
+ * OperationSet:	See CommitChanges. Default is FAUDIO_COMMIT_NOW.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioVoice_EnableEffect(
+	FAudioVoice *voice,
+	uint32_t EffectIndex,
+	uint32_t OperationSet
+);
+
+/* Disables an effect in the effect chain.
+ *
+ * EffectIndex:		The index of the effect (based on the chain order).
+ * OperationSet:	See CommitChanges. Default is FAUDIO_COMMIT_NOW.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioVoice_DisableEffect(
+	FAudioVoice *voice,
+	uint32_t EffectIndex,
+	uint32_t OperationSet
+);
+
+/* Queries the enabled/disabled state of an effect in the effect chain.
+ *
+ * EffectIndex:	The index of the effect (based on the chain order).
+ * pEnabled:	Filled with either 1 (Enabled) or 0 (Disabled).
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI void FAudioVoice_GetEffectState(
+	FAudioVoice *voice,
+	uint32_t EffectIndex,
+	int32_t *pEnabled
+);
+
+/* Submits a block of memory to be sent to FAPO::SetParameters.
+ *
+ * EffectIndex:		The index of the effect (based on the chain order).
+ * pParameters:		The values to be copied and submitted to the FAPO.
+ * ParametersByteSize:	This should match what the FAPO expects!
+ * OperationSet:	See CommitChanges. Default is FAUDIO_COMMIT_NOW.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioVoice_SetEffectParameters(
+	FAudioVoice *voice,
+	uint32_t EffectIndex,
+	const void *pParameters,
+	uint32_t ParametersByteSize,
+	uint32_t OperationSet
+);
+
+/* Requests the latest parameters from FAPO::GetParameters.
+ *
+ * EffectIndex:		The index of the effect (based on the chain order).
+ * pParameters:		Filled with the latest parameter values from the FAPO.
+ * ParametersByteSize:	This should match what the FAPO expects!
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioVoice_GetEffectParameters(
+	FAudioVoice *voice,
+	uint32_t EffectIndex,
+	void *pParameters,
+	uint32_t ParametersByteSize
+);
+
+/* Sets the filter variables for a voice.
+ * This is only valid on voices with the USEFILTER flag.
+ *
+ * pParameters:		See FAudioFilterParameters for details.
+ * OperationSet:	See CommitChanges. Default is FAUDIO_COMMIT_NOW.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioVoice_SetFilterParameters(
+	FAudioVoice *voice,
+	const FAudioFilterParameters *pParameters,
+	uint32_t OperationSet
+);
+
+/* Requests the filter variables for a voice.
+ * This is only valid on voices with the USEFILTER flag.
+ *
+ * pParameters: See FAudioFilterParameters for details.
+ */
+FAUDIOAPI void FAudioVoice_GetFilterParameters(
+	FAudioVoice *voice,
+	FAudioFilterParameters *pParameters
+);
+
+/* Sets the filter variables for a voice's output voice.
+ * This is only valid on sends with the USEFILTER flag.
+ *
+ * pDestinationVoice:	An output voice from the voice's send list.
+ * pParameters:		See FAudioFilterParameters for details.
+ * OperationSet:	See CommitChanges. Default is FAUDIO_COMMIT_NOW.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioVoice_SetOutputFilterParameters(
+	FAudioVoice *voice,
+	FAudioVoice *pDestinationVoice,
+	const FAudioFilterParameters *pParameters,
+	uint32_t OperationSet
+);
+
+/* Requests the filter variables for a voice's output voice.
+ * This is only valid on sends with the USEFILTER flag.
+ *
+ * pDestinationVoice:	An output voice from the voice's send list.
+ * pParameters:		See FAudioFilterParameters for details.
+ */
+FAUDIOAPI void FAudioVoice_GetOutputFilterParameters(
+	FAudioVoice *voice,
+	FAudioVoice *pDestinationVoice,
+	FAudioFilterParameters *pParameters
+);
+
+/* Sets the global volume of a voice.
+ *
+ * Volume:		Amplitude ratio. 1.0f is default, 0.0f is silence.
+ *			Note that you can actually set volume < 0.0f!
+ *			Bounds: [-FAUDIO_MAX_VOLUME_LEVEL, FAUDIO_MAX_VOLUME_LEVEL]
+ * OperationSet:	See CommitChanges. Default is FAUDIO_COMMIT_NOW.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioVoice_SetVolume(
+	FAudioVoice *voice,
+	float Volume,
+	uint32_t OperationSet
+);
+
+/* Requests the global volume of a voice.
+ *
+ * pVolume: Filled with the current voice amplitude ratio.
+ */
+FAUDIOAPI void FAudioVoice_GetVolume(
+	FAudioVoice *voice,
+	float *pVolume
+);
+
+/* Sets the per-channel volumes of a voice.
+ *
+ * Channels:		Must match the channel count of this voice!
+ * pVolumes:		Amplitude ratios for each channel. Same as SetVolume.
+ * OperationSet:	See CommitChanges. Default is FAUDIO_COMMIT_NOW.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioVoice_SetChannelVolumes(
+	FAudioVoice *voice,
+	uint32_t Channels,
+	const float *pVolumes,
+	uint32_t OperationSet
+);
+
+/* Requests the per-channel volumes of a voice.
+ *
+ * Channels:	Must match the channel count of this voice!
+ * pVolumes:	Filled with the current channel amplitude ratios.
+ */
+FAUDIOAPI void FAudioVoice_GetChannelVolumes(
+	FAudioVoice *voice,
+	uint32_t Channels,
+	float *pVolumes
+);
+
+/* Sets the volumes of a send's output channels. The matrix is based on the
+ * voice's input channels. For example, the default matrix for a 2-channel
+ * source and a 2-channel output voice is as follows:
+ * [0] = 1.0f; <- Left input, left output
+ * [1] = 0.0f; <- Right input, left output
+ * [2] = 0.0f; <- Left input, right output
+ * [3] = 1.0f; <- Right input, right output
+ * This is typically only used for panning or 3D sound (via F3DAudio).
+ *
+ * pDestinationVoice:	An output voice from the voice's send list.
+ * SourceChannels:	Must match the voice's input channel count!
+ * DestinationChannels:	Must match the destination's input channel count!
+ * pLevelMatrix:	A float[SourceChannels * DestinationChannels].
+ * OperationSet:	See CommitChanges. Default is FAUDIO_COMMIT_NOW.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioVoice_SetOutputMatrix(
+	FAudioVoice *voice,
+	FAudioVoice *pDestinationVoice,
+	uint32_t SourceChannels,
+	uint32_t DestinationChannels,
+	const float *pLevelMatrix,
+	uint32_t OperationSet
+);
+
+/* Gets the volumes of a send's output channels. See SetOutputMatrix.
+ *
+ * pDestinationVoice:	An output voice from the voice's send list.
+ * SourceChannels:	Must match the voice's input channel count!
+ * DestinationChannels:	Must match the voice's output channel count!
+ * pLevelMatrix:	A float[SourceChannels * DestinationChannels].
+ */
+FAUDIOAPI void FAudioVoice_GetOutputMatrix(
+	FAudioVoice *voice,
+	FAudioVoice *pDestinationVoice,
+	uint32_t SourceChannels,
+	uint32_t DestinationChannels,
+	float *pLevelMatrix
+);
+
+/* Removes this voice from the audio graph and frees memory. */
+FAUDIOAPI void FAudioVoice_DestroyVoice(FAudioVoice *voice);
+
+/* FAudioSourceVoice Interface */
+
+/* Starts processing for a source voice.
+ *
+ * Flags:		Must be 0.
+ * OperationSet:	See CommitChanges. Default is FAUDIO_COMMIT_NOW.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioSourceVoice_Start(
+	FAudioSourceVoice *voice,
+	uint32_t Flags,
+	uint32_t OperationSet
+);
+
+/* Pauses processing for a source voice. Yes, I said pausing.
+ * If you want to _actually_ stop, call FlushSourceBuffers next.
+ *
+ * Flags:		Can be 0 or FAUDIO_PLAY_TAILS, which allows effects to
+ *			keep emitting output even after processing has stopped.
+ * OperationSet:	See CommitChanges. Default is FAUDIO_COMMIT_NOW.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioSourceVoice_Stop(
+	FAudioSourceVoice *voice,
+	uint32_t Flags,
+	uint32_t OperationSet
+);
+
+/* Submits a block of wavedata for the source to process.
+ *
+ * pBuffer:	See FAudioBuffer for details.
+ * pBufferWMA:	See FAudioBufferWMA for details. (Also, don't use WMA.)
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioSourceVoice_SubmitSourceBuffer(
+	FAudioSourceVoice *voice,
+	const FAudioBuffer *pBuffer,
+	const FAudioBufferWMA *pBufferWMA
+);
+
+/* Removes all buffers from a source, with a minor exception.
+ * If the voice is still playing, the active buffer is left alone.
+ * All buffers that are removed will spawn an OnBufferEnd callback.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioSourceVoice_FlushSourceBuffers(
+	FAudioSourceVoice *voice
+);
+
+/* Takes the last buffer currently queued and sets the END_OF_STREAM flag.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioSourceVoice_Discontinuity(
+	FAudioSourceVoice *voice
+);
+
+/* Sets the loop count of the active buffer to 0.
+ *
+ * OperationSet: See CommitChanges. Default is FAUDIO_COMMIT_NOW.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioSourceVoice_ExitLoop(
+	FAudioSourceVoice *voice,
+	uint32_t OperationSet
+);
+
+/* Requests the state and some basic statistics for this source.
+ *
+ * pVoiceState:	See FAudioVoiceState for details.
+ * Flags:	Can be 0 or FAUDIO_VOICE_NOSAMPLESPLAYED.
+ */
+FAUDIOAPI void FAudioSourceVoice_GetState(
+	FAudioSourceVoice *voice,
+	FAudioVoiceState *pVoiceState,
+	uint32_t Flags
+);
+
+/* Sets the frequency ratio (fancy phrase for pitch) of this source.
+ *
+ * Ratio:		The frequency ratio, must be <= MaxFrequencyRatio.
+ * OperationSet:	See CommitChanges. Default is FAUDIO_COMMIT_NOW.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioSourceVoice_SetFrequencyRatio(
+	FAudioSourceVoice *voice,
+	float Ratio,
+	uint32_t OperationSet
+);
+
+/* Requests the frequency ratio (fancy phrase for pitch) of this source.
+ *
+ * pRatio: Filled with the frequency ratio.
+ */
+FAUDIOAPI void FAudioSourceVoice_GetFrequencyRatio(
+	FAudioSourceVoice *voice,
+	float *pRatio
+);
+
+/* Resets the core sample rate of this source.
+ * You probably don't want this, it's more likely you want SetFrequencyRatio.
+ * This is used to recycle voices without having to constantly reallocate them.
+ * For example, if you have wavedata that's all float32 mono, but the sample
+ * rates are different, you can take a source that was being used for a 48KHz
+ * wave and call this so it can be used for a 44.1KHz wave.
+ *
+ * NewSourceSampleRate: The new sample rate for this source.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioSourceVoice_SetSourceSampleRate(
+	FAudioSourceVoice *voice,
+	uint32_t NewSourceSampleRate
+);
+
+/* FAudioMasteringVoice Interface */
+
+/* Requests the channel mask for the mastering voice.
+ * This is typically used with F3DAudioInitialize, but you may find it
+ * interesting if you want to see the user's basic speaker layout.
+ *
+ * pChannelMask: Filled with the channel mask.
+ *
+ * Returns 0 on success.
+ */
+FAUDIOAPI uint32_t FAudioMasteringVoice_GetChannelMask(
+	FAudioMasteringVoice *voice,
+	uint32_t *pChannelMask
+);
+
+/* FAudioEngineCallback Interface */
+
+/* If something horrible happens, this will be called.
+ *
+ * Error: The error code that spawned this callback.
+ */
+typedef void (FAUDIOCALL * OnCriticalErrorFunc)(
+	FAudioEngineCallback *callback,
+	uint32_t Error
+);
+
+/* This is called at the end of a processing update. */
+typedef void (FAUDIOCALL * OnProcessingPassEndFunc)(
+	FAudioEngineCallback *callback
+);
+
+/* This is called at the beginning of a processing update. */
+typedef void (FAUDIOCALL * OnProcessingPassStartFunc)(
+	FAudioEngineCallback *callback
+);
+
+struct FAudioEngineCallback
+{
+	OnCriticalErrorFunc OnCriticalError;
+	OnProcessingPassEndFunc OnProcessingPassEnd;
+	OnProcessingPassStartFunc OnProcessingPassStart;
+};
+
+/* FAudioVoiceCallback Interface */
+
+/* When a buffer is no longer in use, this is called.
+ *
+ * pBufferContext: The pContext for the FAudioBuffer in question.
+ */
+typedef void (FAUDIOCALL * OnBufferEndFunc)(
+	FAudioVoiceCallback *callback,
+	void *pBufferContext
+);
+
+/* When a buffer is now being used, this is called.
+ *
+ * pBufferContext: The pContext for the FAudioBuffer in question.
+ */
+typedef void (FAUDIOCALL * OnBufferStartFunc)(
+	FAudioVoiceCallback *callback,
+	void *pBufferContext
+);
+
+/* When a buffer completes a loop, this is called.
+ *
+ * pBufferContext: The pContext for the FAudioBuffer in question.
+ */
+typedef void (FAUDIOCALL * OnLoopEndFunc)(
+	FAudioVoiceCallback *callback,
+	void *pBufferContext
+);
+
+/* When a buffer that has the END_OF_STREAM flag is finished, this is called. */
+typedef void (FAUDIOCALL * OnStreamEndFunc)(
+	FAudioVoiceCallback *callback
+);
+
+/* If something horrible happens to a voice, this is called.
+ *
+ * pBufferContext:	The pContext for the FAudioBuffer in question.
+ * Error:		The error code that spawned this callback.
+ */
+typedef void (FAUDIOCALL * OnVoiceErrorFunc)(
+	FAudioVoiceCallback *callback,
+	void *pBufferContext,
+	uint32_t Error
+);
+
+/* When this voice is done being processed, this is called. */
+typedef void (FAUDIOCALL * OnVoiceProcessingPassEndFunc)(
+	FAudioVoiceCallback *callback
+);
+
+/* When a voice is about to start being processed, this is called.
+ *
+ * BytesRequested:	The number of bytes needed from the application to
+ *			complete a full update. For example, if we need 512
+ *			frames for a whole update, and the voice is a float32
+ *			stereo source, BytesRequired will be 4096.
+ */
+typedef void (FAUDIOCALL * OnVoiceProcessingPassStartFunc)(
+	FAudioVoiceCallback *callback,
+	uint32_t BytesRequired
+);
+
+struct FAudioVoiceCallback
+{
+	OnBufferEndFunc OnBufferEnd;
+	OnBufferStartFunc OnBufferStart;
+	OnLoopEndFunc OnLoopEnd;
+	OnStreamEndFunc OnStreamEnd;
+	OnVoiceErrorFunc OnVoiceError;
+	OnVoiceProcessingPassEndFunc OnVoiceProcessingPassEnd;
+	OnVoiceProcessingPassStartFunc OnVoiceProcessingPassStart;
+};
+
+/* FAudio Custom Allocator API
+ * See "extensions/CustomAllocatorEXT.txt" for more information.
+ */
+
+typedef void* (FAUDIOCALL * FAudioMallocFunc)(size_t size);
+typedef void (FAUDIOCALL * FAudioFreeFunc)(void* ptr);
+typedef void* (FAUDIOCALL * FAudioReallocFunc)(void* ptr, size_t size);
+
+FAUDIOAPI uint32_t FAudioCreateWithCustomAllocatorEXT(
+	FAudio **ppFAudio,
+	uint32_t Flags,
+	FAudioProcessor XAudio2Processor,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc
+);
+FAUDIOAPI uint32_t FAudioCOMConstructWithCustomAllocatorEXT(
+	FAudio **ppFAudio,
+	uint8_t version,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc
+);
+
+/* FAudio Engine Procedure API
+ * See "extensions/EngineProcedureEXT.txt" for more information.
+ */
+typedef void (FAUDIOCALL *FAudioEngineCallEXT)(FAudio *audio, float *output);
+typedef void (FAUDIOCALL *FAudioEngineProcedureEXT)(FAudioEngineCallEXT defaultEngineProc, FAudio *audio, float *output, void *user);
+
+FAUDIOAPI void FAudio_SetEngineProcedureEXT(
+	FAudio *audio,
+	FAudioEngineProcedureEXT clientEngineProc,
+	void *user
+);
+
+
+/* FAudio I/O API */
+
+#define FAUDIO_SEEK_SET 0
+#define FAUDIO_SEEK_CUR 1
+#define FAUDIO_SEEK_END 2
+#define FAUDIO_EOF -1
+
+typedef size_t (FAUDIOCALL * FAudio_readfunc)(
+	void *data,
+	void *dst,
+	size_t size,
+	size_t count
+);
+typedef int64_t (FAUDIOCALL * FAudio_seekfunc)(
+	void *data,
+	int64_t offset,
+	int whence
+);
+typedef int (FAUDIOCALL * FAudio_closefunc)(
+	void *data
+);
+
+typedef struct FAudioIOStream
+{
+	void *data;
+	FAudio_readfunc read;
+	FAudio_seekfunc seek;
+	FAudio_closefunc close;
+	void *lock;
+} FAudioIOStream;
+
+FAUDIOAPI FAudioIOStream* FAudio_fopen(const char *path);
+FAUDIOAPI FAudioIOStream* FAudio_memopen(void *mem, int len);
+FAUDIOAPI uint8_t* FAudio_memptr(FAudioIOStream *io, size_t offset);
+FAUDIOAPI void FAudio_close(FAudioIOStream *io);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* FAUDIO_H */
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/include/FAudioFX.h b/libs/faudio/include/FAudioFX.h
new file mode 100644
index 00000000000..22052d33e87
--- /dev/null
+++ b/libs/faudio/include/FAudioFX.h
@@ -0,0 +1,308 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+/* This file has no documentation since the MSDN docs are still perfectly fine:
+ * https://docs.microsoft.com/en-us/windows/desktop/api/xaudio2fx/
+ *
+ * Note, however, that FAudio's Reverb implementation does NOT support the new
+ * parameters for XAudio 2.9's 7.1 Reverb effect!
+ */
+
+#ifndef FAUDIOFX_H
+#define FAUDIOFX_H
+
+#include "FAudio.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* GUIDs */
+
+extern const FAudioGUID FAudioFX_CLSID_AudioVolumeMeter;
+extern const FAudioGUID FAudioFX_CLSID_AudioReverb;
+
+/* Structures */
+
+#pragma pack(push, 1)
+
+typedef struct FAudioFXVolumeMeterLevels
+{
+	float *pPeakLevels;
+	float *pRMSLevels;
+	uint32_t ChannelCount;
+} FAudioFXVolumeMeterLevels;
+
+typedef struct FAudioFXReverbParameters
+{
+	float WetDryMix;
+	uint32_t ReflectionsDelay;
+	uint8_t ReverbDelay;
+	uint8_t RearDelay;
+	uint8_t PositionLeft;
+	uint8_t PositionRight;
+	uint8_t PositionMatrixLeft;
+	uint8_t PositionMatrixRight;
+	uint8_t EarlyDiffusion;
+	uint8_t LateDiffusion;
+	uint8_t LowEQGain;
+	uint8_t LowEQCutoff;
+	uint8_t HighEQGain;
+	uint8_t HighEQCutoff;
+	float RoomFilterFreq;
+	float RoomFilterMain;
+	float RoomFilterHF;
+	float ReflectionsGain;
+	float ReverbGain;
+	float DecayTime;
+	float Density;
+	float RoomSize;
+} FAudioFXReverbParameters;
+
+typedef struct FAudioFXReverbParameters9
+{
+	float WetDryMix;
+	uint32_t ReflectionsDelay;
+	uint8_t ReverbDelay;
+	uint8_t RearDelay;
+	uint8_t SideDelay;
+	uint8_t PositionLeft;
+	uint8_t PositionRight;
+	uint8_t PositionMatrixLeft;
+	uint8_t PositionMatrixRight;
+	uint8_t EarlyDiffusion;
+	uint8_t LateDiffusion;
+	uint8_t LowEQGain;
+	uint8_t LowEQCutoff;
+	uint8_t HighEQGain;
+	uint8_t HighEQCutoff;
+	float RoomFilterFreq;
+	float RoomFilterMain;
+	float RoomFilterHF;
+	float ReflectionsGain;
+	float ReverbGain;
+	float DecayTime;
+	float Density;
+	float RoomSize;
+} FAudioFXReverbParameters9;
+
+typedef struct FAudioFXReverbI3DL2Parameters
+{
+	float WetDryMix;
+	int32_t Room;
+	int32_t RoomHF;
+	float RoomRolloffFactor;
+	float DecayTime;
+	float DecayHFRatio;
+	int32_t Reflections;
+	float ReflectionsDelay;
+	int32_t Reverb;
+	float ReverbDelay;
+	float Diffusion;
+	float Density;
+	float HFReference;
+} FAudioFXReverbI3DL2Parameters;
+
+#pragma pack(pop)
+
+/* Constants */
+
+#define FAUDIOFX_DEBUG 1
+
+#define FAUDIOFX_REVERB_MIN_FRAMERATE 20000
+#define FAUDIOFX_REVERB_MAX_FRAMERATE 48000
+
+#define FAUDIOFX_REVERB_MIN_WET_DRY_MIX		0.0f
+#define FAUDIOFX_REVERB_MIN_REFLECTIONS_DELAY	0
+#define FAUDIOFX_REVERB_MIN_REVERB_DELAY	0
+#define FAUDIOFX_REVERB_MIN_REAR_DELAY		0
+#define FAUDIOFX_REVERB_MIN_7POINT1_SIDE_DELAY	0
+#define FAUDIOFX_REVERB_MIN_7POINT1_REAR_DELAY	0
+#define FAUDIOFX_REVERB_MIN_POSITION		0
+#define FAUDIOFX_REVERB_MIN_DIFFUSION		0
+#define FAUDIOFX_REVERB_MIN_LOW_EQ_GAIN		0
+#define FAUDIOFX_REVERB_MIN_LOW_EQ_CUTOFF	0
+#define FAUDIOFX_REVERB_MIN_HIGH_EQ_GAIN	0
+#define FAUDIOFX_REVERB_MIN_HIGH_EQ_CUTOFF	0
+#define FAUDIOFX_REVERB_MIN_ROOM_FILTER_FREQ	20.0f
+#define FAUDIOFX_REVERB_MIN_ROOM_FILTER_MAIN	-100.0f
+#define FAUDIOFX_REVERB_MIN_ROOM_FILTER_HF	-100.0f
+#define FAUDIOFX_REVERB_MIN_REFLECTIONS_GAIN	-100.0f
+#define FAUDIOFX_REVERB_MIN_REVERB_GAIN		-100.0f
+#define FAUDIOFX_REVERB_MIN_DECAY_TIME		0.1f
+#define FAUDIOFX_REVERB_MIN_DENSITY		0.0f
+#define FAUDIOFX_REVERB_MIN_ROOM_SIZE		0.0f
+
+#define FAUDIOFX_REVERB_MAX_WET_DRY_MIX		100.0f
+#define FAUDIOFX_REVERB_MAX_REFLECTIONS_DELAY	300
+#define FAUDIOFX_REVERB_MAX_REVERB_DELAY	85
+#define FAUDIOFX_REVERB_MAX_REAR_DELAY		5
+#define FAUDIOFX_REVERB_MAX_7POINT1_SIDE_DELAY	5
+#define FAUDIOFX_REVERB_MAX_7POINT1_REAR_DELAY	20
+#define FAUDIOFX_REVERB_MAX_POSITION		30
+#define FAUDIOFX_REVERB_MAX_DIFFUSION		15
+#define FAUDIOFX_REVERB_MAX_LOW_EQ_GAIN		12
+#define FAUDIOFX_REVERB_MAX_LOW_EQ_CUTOFF	9
+#define FAUDIOFX_REVERB_MAX_HIGH_EQ_GAIN	8
+#define FAUDIOFX_REVERB_MAX_HIGH_EQ_CUTOFF	14
+#define FAUDIOFX_REVERB_MAX_ROOM_FILTER_FREQ	20000.0f
+#define FAUDIOFX_REVERB_MAX_ROOM_FILTER_MAIN	0.0f
+#define FAUDIOFX_REVERB_MAX_ROOM_FILTER_HF	0.0f
+#define FAUDIOFX_REVERB_MAX_REFLECTIONS_GAIN	20.0f
+#define FAUDIOFX_REVERB_MAX_REVERB_GAIN		20.0f
+#define FAUDIOFX_REVERB_MAX_DENSITY		100.0f
+#define FAUDIOFX_REVERB_MAX_ROOM_SIZE		100.0f
+
+#define FAUDIOFX_REVERB_DEFAULT_WET_DRY_MIX		100.0f
+#define FAUDIOFX_REVERB_DEFAULT_REFLECTIONS_DELAY	5
+#define FAUDIOFX_REVERB_DEFAULT_REVERB_DELAY		5
+#define FAUDIOFX_REVERB_DEFAULT_REAR_DELAY		5
+#define FAUDIOFX_REVERB_DEFAULT_7POINT1_SIDE_DELAY	5
+#define FAUDIOFX_REVERB_DEFAULT_7POINT1_REAR_DELAY	20
+#define FAUDIOFX_REVERB_DEFAULT_POSITION		6
+#define FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX		27
+#define FAUDIOFX_REVERB_DEFAULT_EARLY_DIFFUSION		8
+#define FAUDIOFX_REVERB_DEFAULT_LATE_DIFFUSION		8
+#define FAUDIOFX_REVERB_DEFAULT_LOW_EQ_GAIN		8
+#define FAUDIOFX_REVERB_DEFAULT_LOW_EQ_CUTOFF		4
+#define FAUDIOFX_REVERB_DEFAULT_HIGH_EQ_GAIN		8
+#define FAUDIOFX_REVERB_DEFAULT_HIGH_EQ_CUTOFF		4
+#define FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_FREQ	5000.0f
+#define FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_MAIN	0.0f
+#define FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_HF		0.0f
+#define FAUDIOFX_REVERB_DEFAULT_REFLECTIONS_GAIN	0.0f
+#define FAUDIOFX_REVERB_DEFAULT_REVERB_GAIN		0.0f
+#define FAUDIOFX_REVERB_DEFAULT_DECAY_TIME		1.0f
+#define FAUDIOFX_REVERB_DEFAULT_DENSITY			100.0f
+#define FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE		100.0f
+
+#define FAUDIOFX_I3DL2_PRESET_DEFAULT \
+	{100,-10000,    0,0.0f, 1.00f,0.50f,-10000,0.020f,-10000,0.040f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_GENERIC \
+	{100, -1000, -100,0.0f, 1.49f,0.83f, -2602,0.007f,   200,0.011f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_PADDEDCELL \
+	{100, -1000,-6000,0.0f, 0.17f,0.10f, -1204,0.001f,   207,0.002f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_ROOM \
+	{100, -1000, -454,0.0f, 0.40f,0.83f, -1646,0.002f,    53,0.003f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_BATHROOM \
+	{100, -1000,-1200,0.0f, 1.49f,0.54f,  -370,0.007f,  1030,0.011f,100.0f, 60.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_LIVINGROOM \
+	{100, -1000,-6000,0.0f, 0.50f,0.10f, -1376,0.003f, -1104,0.004f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_STONEROOM \
+	{100, -1000, -300,0.0f, 2.31f,0.64f,  -711,0.012f,    83,0.017f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_AUDITORIUM \
+	{100, -1000, -476,0.0f, 4.32f,0.59f,  -789,0.020f,  -289,0.030f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_CONCERTHALL \
+	{100, -1000, -500,0.0f, 3.92f,0.70f, -1230,0.020f,    -2,0.029f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_CAVE \
+	{100, -1000,    0,0.0f, 2.91f,1.30f,  -602,0.015f,  -302,0.022f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_ARENA \
+	{100, -1000, -698,0.0f, 7.24f,0.33f, -1166,0.020f,    16,0.030f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_HANGAR \
+	{100, -1000,-1000,0.0f,10.05f,0.23f,  -602,0.020f,   198,0.030f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_CARPETEDHALLWAY \
+	{100, -1000,-4000,0.0f, 0.30f,0.10f, -1831,0.002f, -1630,0.030f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_HALLWAY \
+	{100, -1000, -300,0.0f, 1.49f,0.59f, -1219,0.007f,   441,0.011f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_STONECORRIDOR \
+	{100, -1000, -237,0.0f, 2.70f,0.79f, -1214,0.013f,   395,0.020f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_ALLEY \
+	{100, -1000, -270,0.0f, 1.49f,0.86f, -1204,0.007f,    -4,0.011f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_FOREST \
+	{100, -1000,-3300,0.0f, 1.49f,0.54f, -2560,0.162f,  -613,0.088f, 79.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_CITY \
+	{100, -1000, -800,0.0f, 1.49f,0.67f, -2273,0.007f, -2217,0.011f, 50.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_MOUNTAINS \
+	{100, -1000,-2500,0.0f, 1.49f,0.21f, -2780,0.300f, -2014,0.100f, 27.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_QUARRY \
+	{100, -1000,-1000,0.0f, 1.49f,0.83f,-10000,0.061f,   500,0.025f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_PLAIN \
+	{100, -1000,-2000,0.0f, 1.49f,0.50f, -2466,0.179f, -2514,0.100f, 21.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_PARKINGLOT \
+	{100, -1000,    0,0.0f, 1.65f,1.50f, -1363,0.008f, -1153,0.012f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_SEWERPIPE \
+	{100, -1000,-1000,0.0f, 2.81f,0.14f,   429,0.014f,   648,0.021f, 80.0f, 60.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_UNDERWATER \
+	{100, -1000,-4000,0.0f, 1.49f,0.10f,  -449,0.007f,  1700,0.011f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_SMALLROOM \
+	{100, -1000, -600,0.0f, 1.10f,0.83f,  -400,0.005f,   500,0.010f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_MEDIUMROOM \
+	{100, -1000, -600,0.0f, 1.30f,0.83f, -1000,0.010f,  -200,0.020f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_LARGEROOM \
+	{100, -1000, -600,0.0f, 1.50f,0.83f, -1600,0.020f, -1000,0.040f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_MEDIUMHALL \
+	{100, -1000, -600,0.0f, 1.80f,0.70f, -1300,0.015f,  -800,0.030f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_LARGEHALL \
+	{100, -1000, -600,0.0f, 1.80f,0.70f, -2000,0.030f, -1400,0.060f,100.0f,100.0f,5000.0f}
+#define FAUDIOFX_I3DL2_PRESET_PLATE \
+	{100, -1000, -200,0.0f, 1.30f,0.90f,     0,0.002f,     0,0.010f,100.0f, 75.0f,5000.0f}
+
+/* Functions */
+
+FAUDIOAPI uint32_t FAudioCreateVolumeMeter(FAPO** ppApo, uint32_t Flags);
+FAUDIOAPI uint32_t FAudioCreateReverb(FAPO** ppApo, uint32_t Flags);
+FAUDIOAPI uint32_t FAudioCreateReverb9(FAPO** ppApo, uint32_t Flags);
+
+/* See "extensions/CustomAllocatorEXT.txt" for more information. */
+FAUDIOAPI uint32_t FAudioCreateVolumeMeterWithCustomAllocatorEXT(
+	FAPO** ppApo,
+	uint32_t Flags,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc
+);
+FAUDIOAPI uint32_t FAudioCreateReverbWithCustomAllocatorEXT(
+	FAPO** ppApo,
+	uint32_t Flags,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc
+);
+FAUDIOAPI uint32_t FAudioCreateReverb9WithCustomAllocatorEXT(
+	FAPO** ppApo,
+	uint32_t Flags,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc
+);
+
+FAUDIOAPI void ReverbConvertI3DL2ToNative(
+	const FAudioFXReverbI3DL2Parameters *pI3DL2,
+	FAudioFXReverbParameters *pNative
+);
+FAUDIOAPI void ReverbConvertI3DL2ToNative9(
+	const FAudioFXReverbI3DL2Parameters *pI3DL2,
+	FAudioFXReverbParameters9 *pNative,
+	int32_t sevenDotOneReverb
+);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* FAUDIOFX_H */
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/F3DAudio.c b/libs/faudio/src/F3DAudio.c
new file mode 100644
index 00000000000..bc2b9edad21
--- /dev/null
+++ b/libs/faudio/src/F3DAudio.c
@@ -0,0 +1,1563 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "F3DAudio.h"
+#include "FAudio_internal.h"
+
+#include <math.h> /* ONLY USE THIS FOR isnan! */
+#include <float.h> /* ONLY USE THIS FOR FLT_MIN/FLT_MAX! */
+
+/* VS2010 doesn't define isnan (which is C99), so here it is. */
+#if defined(_MSC_VER) && !defined(isnan)
+#define isnan(x) _isnan(x)
+#endif
+
+/* UTILITY MACROS */
+
+#define PARAM_CHECK_OK 1
+#define PARAM_CHECK_FAIL (!PARAM_CHECK_OK)
+
+#define ARRAY_COUNT(x) (sizeof(x) / sizeof(x[0]))
+
+#define LERP(a, x, y) ((1.0f - a) * x + a * y)
+
+/* PARAMETER CHECK MACROS */
+
+#define PARAM_CHECK(cond, msg) FAudio_assert(cond && msg)
+
+#define POINTER_CHECK(p) \
+	PARAM_CHECK(p != NULL, "Pointer " #p " must be != NULL")
+
+#define FLOAT_BETWEEN_CHECK(f, a, b) \
+	PARAM_CHECK(f >= a, "Value" #f " is too low"); \
+	PARAM_CHECK(f <= b, "Value" #f " is too big")
+
+
+/* Quote X3DAUDIO docs:
+ * "To be considered orthonormal, a pair of vectors must have a magnitude of
+ * 1 +- 1x10-5 and a dot product of 0 +- 1x10-5."
+ * VECTOR_NORMAL_CHECK verifies that vectors are normal (i.e. have norm 1 +- 1x10-5)
+ * VECTOR_BASE_CHECK verifies that a pair of vectors are orthogonal (i.e. their dot
+ * product is 0 +- 1x10-5)
+ */
+
+/* TODO: Switch to square length (to save CPU) */
+#define VECTOR_NORMAL_CHECK(v) \
+	PARAM_CHECK( \
+		FAudio_fabsf(VectorLength(v) - 1.0f) <= 1e-5f, \
+		"Vector " #v " isn't normal" \
+	)
+
+#define VECTOR_BASE_CHECK(u, v) \
+	PARAM_CHECK( \
+		FAudio_fabsf(VectorDot(u, v)) <= 1e-5f, \
+		"Vector u and v have non-negligible dot product" \
+	)
+
+/*************************************
+ * F3DAudioInitialize Implementation *
+ *************************************/
+
+/* F3DAUDIO_HANDLE Structure */
+#define SPEAKERMASK(Instance)		*((uint32_t*)	&Instance[0])
+#define SPEAKERCOUNT(Instance)		*((uint32_t*)	&Instance[4])
+#define SPEAKER_LF_INDEX(Instance)	*((uint32_t*)	&Instance[8])
+#define SPEEDOFSOUND(Instance)		*((float*)	&Instance[12])
+#define SPEEDOFSOUNDEPSILON(Instance)	*((float*)	&Instance[16])
+
+/* Export for unit tests */
+F3DAUDIOAPI uint32_t F3DAudioCheckInitParams(
+	uint32_t SpeakerChannelMask,
+	float SpeedOfSound,
+	F3DAUDIO_HANDLE instance
+) {
+	const uint32_t kAllowedSpeakerMasks[] =
+	{
+		SPEAKER_MONO,
+		SPEAKER_STEREO,
+		SPEAKER_2POINT1,
+		SPEAKER_QUAD,
+		SPEAKER_SURROUND,
+		SPEAKER_4POINT1,
+		SPEAKER_5POINT1,
+		SPEAKER_5POINT1_SURROUND,
+		SPEAKER_7POINT1,
+		SPEAKER_7POINT1_SURROUND,
+	};
+	uint8_t speakerMaskIsValid = 0;
+	uint32_t i;
+
+	POINTER_CHECK(instance);
+
+	for (i = 0; i < ARRAY_COUNT(kAllowedSpeakerMasks); i += 1)
+	{
+		if (SpeakerChannelMask == kAllowedSpeakerMasks[i])
+		{
+			speakerMaskIsValid = 1;
+			break;
+		}
+	}
+
+	/* The docs don't clearly say it, but the debug dll does check that
+	 * we're exactly in one of the allowed speaker configurations.
+	 * -Adrien
+	 */
+	PARAM_CHECK(
+		speakerMaskIsValid == 1,
+		"SpeakerChannelMask is invalid. Needs to be one of"
+		" MONO, STEREO, QUAD, 2POINT1, 4POINT1, 5POINT1, 7POINT1,"
+		" SURROUND, 5POINT1_SURROUND, or 7POINT1_SURROUND."
+	);
+
+	PARAM_CHECK(SpeedOfSound >= FLT_MIN, "SpeedOfSound needs to be >= FLT_MIN");
+
+	return PARAM_CHECK_OK;
+}
+
+void F3DAudioInitialize(
+	uint32_t SpeakerChannelMask,
+	float SpeedOfSound,
+	F3DAUDIO_HANDLE Instance
+) {
+	F3DAudioInitialize8(SpeakerChannelMask, SpeedOfSound, Instance);
+}
+
+uint32_t F3DAudioInitialize8(
+	uint32_t SpeakerChannelMask,
+	float SpeedOfSound,
+	F3DAUDIO_HANDLE Instance
+) {
+	union
+	{
+		float f;
+		uint32_t i;
+	} epsilonHack;
+	uint32_t speakerCount = 0;
+
+	if (!F3DAudioCheckInitParams(SpeakerChannelMask, SpeedOfSound, Instance))
+	{
+		return FAUDIO_E_INVALID_CALL;
+	}
+
+	SPEAKERMASK(Instance) = SpeakerChannelMask;
+	SPEEDOFSOUND(Instance) = SpeedOfSound;
+
+	/* "Convert" raw float to int... */
+	epsilonHack.f = SpeedOfSound;
+	/* ... Subtract epsilon value... */
+	epsilonHack.i -= 1;
+	/* ... Convert back to float. */
+	SPEEDOFSOUNDEPSILON(Instance) = epsilonHack.f;
+
+	SPEAKER_LF_INDEX(Instance) = 0xFFFFFFFF;
+	if (SpeakerChannelMask & SPEAKER_LOW_FREQUENCY)
+	{
+		if (SpeakerChannelMask & SPEAKER_FRONT_CENTER)
+		{
+			SPEAKER_LF_INDEX(Instance) = 3;
+		}
+		else
+		{
+			SPEAKER_LF_INDEX(Instance) = 2;
+		}
+	}
+
+	while (SpeakerChannelMask)
+	{
+		speakerCount += 1;
+		SpeakerChannelMask &= SpeakerChannelMask - 1;
+	}
+	SPEAKERCOUNT(Instance) = speakerCount;
+
+	return 0;
+}
+
+
+/************************************
+ * F3DAudioCalculate Implementation *
+ ************************************/
+
+/* VECTOR UTILITIES */
+
+static inline F3DAUDIO_VECTOR Vec(float x, float y, float z)
+{
+	F3DAUDIO_VECTOR res;
+	res.x = x;
+	res.y = y;
+	res.z = z;
+	return res;
+}
+
+#define VectorAdd(u, v) Vec(u.x + v.x, u.y + v.y, u.z + v.z)
+
+#define VectorSub(u, v) Vec(u.x - v.x, u.y - v.y, u.z - v.z)
+
+#define VectorScale(u, s) Vec(u.x * s, u.y * s, u.z * s)
+
+#define VectorCross(u, v) Vec( \
+	(u.y * v.z) - (u.z * v.y), \
+	(u.z * v.x) - (u.x * v.z), \
+	(u.x * v.y) - (u.y * v.x) \
+)
+
+#define VectorLength(v) FAudio_sqrtf( \
+	(v.x * v.x) + (v.y * v.y) + (v.z * v.z) \
+)
+
+#define VectorDot(u, v) ((u.x * v.x) + (u.y * v.y) + (u.z * v.z))
+
+/* This structure represent a tuple of vectors that form a left-handed basis.
+ * That is, all vectors are normal, orthogonal to each other, and taken in the
+ * order front, right, top they follow the left-hand rule.
+ * (https://en.wikipedia.org/wiki/Right-hand_rule)
+ */
+typedef struct F3DAUDIO_BASIS
+{
+	F3DAUDIO_VECTOR front;
+	F3DAUDIO_VECTOR right;
+	F3DAUDIO_VECTOR top;
+} F3DAUDIO_BASIS;
+
+/* CHECK UTILITY FUNCTIONS */
+
+static inline uint8_t CheckCone(F3DAUDIO_CONE *pCone)
+{
+	if (!pCone)
+	{
+		return PARAM_CHECK_OK;
+	}
+
+	FLOAT_BETWEEN_CHECK(pCone->InnerAngle, 0.0f, F3DAUDIO_2PI);
+	FLOAT_BETWEEN_CHECK(pCone->OuterAngle, pCone->InnerAngle, F3DAUDIO_2PI);
+
+	FLOAT_BETWEEN_CHECK(pCone->InnerVolume, 0.0f, 2.0f);
+	FLOAT_BETWEEN_CHECK(pCone->OuterVolume, 0.0f, 2.0f);
+
+	FLOAT_BETWEEN_CHECK(pCone->InnerLPF, 0.0f, 1.0f);
+	FLOAT_BETWEEN_CHECK(pCone->OuterLPF, 0.0f, 1.0f);
+
+	FLOAT_BETWEEN_CHECK(pCone->InnerReverb, 0.0f, 2.0f);
+	FLOAT_BETWEEN_CHECK(pCone->OuterReverb, 0.0f, 2.0f);
+
+	return PARAM_CHECK_OK;
+}
+
+static inline uint8_t CheckCurve(F3DAUDIO_DISTANCE_CURVE *pCurve)
+{
+	F3DAUDIO_DISTANCE_CURVE_POINT *points;
+	uint32_t i;
+	if (!pCurve)
+	{
+		return PARAM_CHECK_OK;
+	}
+
+	points = pCurve->pPoints;
+	POINTER_CHECK(points);
+	PARAM_CHECK(pCurve->PointCount >= 2, "Invalid number of points for curve");
+
+	for (i = 0; i < pCurve->PointCount; i += 1)
+	{
+		FLOAT_BETWEEN_CHECK(points[i].Distance, 0.0f, 1.0f);
+	}
+
+	PARAM_CHECK(
+		points[0].Distance == 0.0f,
+		"First point in the curve must be at distance 0.0f"
+	);
+	PARAM_CHECK(
+		points[pCurve->PointCount - 1].Distance == 1.0f,
+		"Last point in the curve must be at distance 1.0f"
+	);
+
+	for (i = 0; i < (pCurve->PointCount - 1); i += 1)
+	{
+		PARAM_CHECK(
+			points[i].Distance < points[i + 1].Distance,
+			"Curve points must be in strict ascending order"
+		);
+	}
+
+	return PARAM_CHECK_OK;
+}
+
+/* Export for unit tests */
+F3DAUDIOAPI uint8_t F3DAudioCheckCalculateParams(
+	const F3DAUDIO_HANDLE Instance,
+	const F3DAUDIO_LISTENER *pListener,
+	const F3DAUDIO_EMITTER *pEmitter,
+	uint32_t Flags,
+	F3DAUDIO_DSP_SETTINGS *pDSPSettings
+) {
+	uint32_t i, ChannelCount;
+
+	POINTER_CHECK(Instance);
+	POINTER_CHECK(pListener);
+	POINTER_CHECK(pEmitter);
+	POINTER_CHECK(pDSPSettings);
+
+	if (Flags & F3DAUDIO_CALCULATE_MATRIX)
+	{
+		POINTER_CHECK(pDSPSettings->pMatrixCoefficients);
+	}
+	if (Flags & F3DAUDIO_CALCULATE_ZEROCENTER)
+	{
+		const uint32_t isCalculateMatrix = (Flags & F3DAUDIO_CALCULATE_MATRIX);
+		const uint32_t hasCenter = SPEAKERMASK(Instance) & SPEAKER_FRONT_CENTER;
+		PARAM_CHECK(
+			isCalculateMatrix && hasCenter,
+			"F3DAUDIO_CALCULATE_ZEROCENTER is only valid for matrix"
+			" calculations with an output format that has a center channel"
+		);
+	}
+
+	if (Flags & F3DAUDIO_CALCULATE_REDIRECT_TO_LFE)
+	{
+		const uint32_t isCalculateMatrix = (Flags & F3DAUDIO_CALCULATE_MATRIX);
+		const uint32_t hasLF = SPEAKERMASK(Instance) & SPEAKER_LOW_FREQUENCY;
+		PARAM_CHECK(
+			isCalculateMatrix && hasLF,
+			"F3DAUDIO_CALCULATE_REDIRECT_TO_LFE is only valid for matrix"
+			" calculations with an output format that has a low-frequency"
+			" channel"
+		);
+	}
+
+	ChannelCount = SPEAKERCOUNT(Instance);
+	PARAM_CHECK(
+		pDSPSettings->DstChannelCount == ChannelCount,
+		"Invalid channel count, DSP settings and speaker configuration must agree"
+	);
+	PARAM_CHECK(
+		pDSPSettings->SrcChannelCount == pEmitter->ChannelCount,
+		"Invalid channel count, DSP settings and emitter must agree"
+	);
+
+	if (pListener->pCone)
+	{
+		PARAM_CHECK(
+			CheckCone(pListener->pCone) == PARAM_CHECK_OK,
+			"Invalid listener cone"
+		);
+	}
+	VECTOR_NORMAL_CHECK(pListener->OrientFront);
+	VECTOR_NORMAL_CHECK(pListener->OrientTop);
+	VECTOR_BASE_CHECK(pListener->OrientFront, pListener->OrientTop);
+
+	if (pEmitter->pCone)
+	{
+		VECTOR_NORMAL_CHECK(pEmitter->OrientFront);
+		PARAM_CHECK(
+			CheckCone(pEmitter->pCone) == PARAM_CHECK_OK,
+			"Invalid emitter cone"
+		);
+	}
+	else if (Flags & F3DAUDIO_CALCULATE_EMITTER_ANGLE)
+	{
+		VECTOR_NORMAL_CHECK(pEmitter->OrientFront);
+	}
+	if (pEmitter->ChannelCount > 1)
+	{
+		/* Only used for multi-channel emitters */
+		VECTOR_NORMAL_CHECK(pEmitter->OrientFront);
+		VECTOR_NORMAL_CHECK(pEmitter->OrientTop);
+		VECTOR_BASE_CHECK(pEmitter->OrientFront, pEmitter->OrientTop);
+	}
+	FLOAT_BETWEEN_CHECK(pEmitter->InnerRadius, 0.0f, FLT_MAX);
+	FLOAT_BETWEEN_CHECK(pEmitter->InnerRadiusAngle, 0.0f, F3DAUDIO_2PI / 4.0f);
+	PARAM_CHECK(
+		pEmitter->ChannelCount > 0,
+		"Invalid channel count for emitter"
+	);
+	PARAM_CHECK(
+		pEmitter->ChannelRadius >= 0.0f,
+		"Invalid channel radius for emitter"
+	);
+	if (pEmitter->ChannelCount > 1)
+	{
+		PARAM_CHECK(
+			pEmitter->pChannelAzimuths != NULL,
+			"Invalid channel azimuths for multi-channel emitter"
+		);
+		if (pEmitter->pChannelAzimuths)
+		{
+			for (i = 0; i < pEmitter->ChannelCount; i += 1)
+			{
+				float currentAzimuth = pEmitter->pChannelAzimuths[i];
+				FLOAT_BETWEEN_CHECK(currentAzimuth, 0.0f, F3DAUDIO_2PI);
+				if (currentAzimuth == F3DAUDIO_2PI)
+				{
+					PARAM_CHECK(
+						!(Flags & F3DAUDIO_CALCULATE_REDIRECT_TO_LFE),
+						"F3DAUDIO_CALCULATE_REDIRECT_TO_LFE valid only for"
+						" matrix calculations with emitters that have no LFE"
+						" channel"
+					);
+				}
+			}
+		}
+	}
+	FLOAT_BETWEEN_CHECK(pEmitter->CurveDistanceScaler, FLT_MIN, FLT_MAX);
+	FLOAT_BETWEEN_CHECK(pEmitter->DopplerScaler, 0.0f, FLT_MAX);
+
+	PARAM_CHECK(
+		CheckCurve(pEmitter->pVolumeCurve) == PARAM_CHECK_OK,
+		"Invalid Volume curve"
+	);
+	PARAM_CHECK(
+		CheckCurve(pEmitter->pLFECurve) == PARAM_CHECK_OK,
+		"Invalid LFE curve"
+	);
+	PARAM_CHECK(
+		CheckCurve(pEmitter->pLPFDirectCurve) == PARAM_CHECK_OK,
+		"Invalid LPFDirect curve"
+	);
+	PARAM_CHECK(
+		CheckCurve(pEmitter->pLPFReverbCurve) == PARAM_CHECK_OK,
+		"Invalid LPFReverb curve"
+	);
+	PARAM_CHECK(
+		CheckCurve(pEmitter->pReverbCurve) == PARAM_CHECK_OK,
+		"Invalid Reverb curve"
+	);
+
+	return PARAM_CHECK_OK;
+}
+
+/*
+ * MATRIX CALCULATION
+ */
+
+/* This function computes the distance either according to a curve if pCurve
+ * isn't NULL, or according to the inverse distance law 1/d otherwise.
+ */
+static inline float ComputeDistanceAttenuation(
+	float normalizedDistance,
+	F3DAUDIO_DISTANCE_CURVE *pCurve
+) {
+	float res;
+	float alpha;
+	uint32_t n_points;
+	size_t i;
+	if (pCurve)
+	{
+		F3DAUDIO_DISTANCE_CURVE_POINT* points = pCurve->pPoints;
+		n_points = pCurve->PointCount;
+
+		/* By definition, the first point in the curve must be 0.0f
+		 * -Adrien
+		 */
+
+		/* We advance i up until our normalizedDistance lies between the distances of
+		 * the i_th and (i-1)_th points, or we reach the last point.
+		 */
+		for (i = 1; (i < n_points) && (normalizedDistance >= points[i].Distance); i += 1);
+		if (i == n_points)
+		{
+			/* We've reached the last point, so we use its value directly.
+			 * Quote X3DAUDIO docs:
+			 * "If an emitter moves beyond a distance of (CurveDistanceScaler × 1.0f),
+			 * the last point on the curve is used to compute the volume output level."
+			 */
+			res = points[n_points - 1].DSPSetting;
+		}
+		else
+		{
+			/* We're between two points: the distance attenuation is the linear interpolation of the DSPSetting
+			 * values defined by our points, according to the distance.
+			 */
+			alpha = (points[i].Distance - normalizedDistance) / (points[i].Distance - points[i - 1].Distance);
+			res = LERP(alpha, points[i].DSPSetting, points[i - 1].DSPSetting);
+		}
+	}
+	else
+	{
+		res = 1.0f;
+		if (normalizedDistance >= 1.0f)
+		{
+			res /= normalizedDistance;
+		}
+	}
+	return res;
+}
+
+static inline float ComputeConeParameter(
+	float distance,
+	float angle,
+	float innerAngle,
+	float outerAngle,
+	float innerParam,
+	float outerParam
+) {
+	/* When computing whether a point lies inside a cone, X3DAUDIO first determines
+	 * whether the point is close enough to the apex of the cone.
+	 * If it is, the innerParam is used.
+	 * The following constant is the one that is used for this distance check;
+	 * It is an approximation, found by manual binary search.
+	 * TODO: find the exact value of the constant via automated binary search. */
+	#define CONE_NULL_DISTANCE_TOLERANCE 1e-7
+
+	float halfInnerAngle, halfOuterAngle, alpha;
+
+	/* Quote X3DAudio.h:
+	 * "Set both cone angles to 0 or X3DAUDIO_2PI for omnidirectionality using
+	 * only the outer or inner values respectively."
+	 */
+	if (innerAngle == 0.0f && outerAngle == 0.0f)
+	{
+		return outerParam;
+	}
+	if (innerAngle == F3DAUDIO_2PI && outerAngle == F3DAUDIO_2PI)
+	{
+		return innerParam;
+	}
+
+	/* If we're within the inner angle, or close enough to the apex, we use
+	 * the innerParam. */
+	halfInnerAngle = innerAngle / 2.0f;
+	if (distance <= CONE_NULL_DISTANCE_TOLERANCE || angle <= halfInnerAngle)
+	{
+		return innerParam;
+	}
+
+	/* If we're between the inner angle and the outer angle, we must use
+	 * some interpolation of the innerParam and outerParam according to the
+	 * distance between our angle and the inner and outer angles.
+	 */
+	halfOuterAngle = outerAngle / 2.0f;
+	if (angle <= halfOuterAngle)
+	{
+		alpha = (angle - halfInnerAngle) / (halfOuterAngle - halfInnerAngle);
+
+		/* Sooo... This is awkward. MSDN doesn't say anything, but
+		 * X3DAudio.h says that this should be lerped. However in
+		 * practice the behaviour of X3DAudio isn't a lerp at all. It's
+		 * easy to see with big (InnerAngle / OuterAngle) values. If we
+		 * want accurate emulation, we'll need to either find what
+		 * formula they use, or use a more advanced interpolation, like
+		 * tricubic.
+		 *
+		 * TODO: HIGH_ACCURACY version.
+		 * -Adrien
+		 */
+		return LERP(alpha, innerParam, outerParam);
+	}
+
+	/* Otherwise, we're outside the outer angle, so we just return the outer param. */
+	return outerParam;
+}
+
+/* X3DAudio.h declares something like this, but the default (if emitter is NULL)
+ * volume curve is a *computed* inverse law, while on the other hand a curve
+ * leads to a piecewise linear function. So a "default curve" like this is
+ * pointless, not sure what X3DAudio does with it...
+ * -Adrien
+ */
+#if 0
+static F3DAUDIO_DISTANCE_CURVE_POINT DefaultVolumeCurvePoints[] =
+{
+	{ 0.0f, 1.0f },
+	{ 1.0f, 0.0f }
+};
+static F3DAUDIO_DISTANCE_CURVE DefaultVolumeCurve =
+{
+	DefaultVolumeCurvePoints,
+	ARRAY_COUNT(DefaultVolumeCurvePoints)
+};
+#endif
+
+/* Here we declare the azimuths of every speaker for every speaker
+ * configuration, ordered by increasing angle, as well as the index to which
+ * they map in the final matrix for their respective configuration. It had to be
+ * reverse engineered by looking at the data from various X3DAudioCalculate()
+ * matrix results for the various speaker configurations; *in particular*, the
+ * azimuths are different from the ones in F3DAudio.h (and X3DAudio.h) for
+ * SPEAKER_STEREO (which is declared has having front L and R speakers in the
+ * bit mask, but in fact has L and R *side* speakers). LF speakers are
+ * deliberately not included in the SpeakerInfo list, rather, we store the index
+ * into a separate field (with a -1 sentinel value if it has no LF speaker).
+ * -Adrien
+ */
+typedef struct
+{
+	float azimuth;
+	uint32_t matrixIdx;
+} SpeakerInfo;
+
+typedef struct
+{
+	uint32_t configMask;
+	const SpeakerInfo *speakers;
+
+	/* Not strictly necessary because it can be inferred from the
+	 * SpeakerCount field of the F3DAUDIO_HANDLE, but makes code much
+	 * cleaner and less error prone
+	 */
+	uint32_t numNonLFSpeakers;
+
+	int32_t LFSpeakerIdx;
+} ConfigInfo;
+
+/* It is absolutely necessary that these are stored in increasing, *positive*
+ * azimuth order (i.e. all angles between [0; 2PI]), as we'll do a linear
+ * interval search inside FindSpeakerAzimuths.
+ * -Adrien
+ */
+
+#define SPEAKER_AZIMUTH_CENTER			0.0f
+#define SPEAKER_AZIMUTH_FRONT_RIGHT_OF_CENTER	(F3DAUDIO_PI *  1.0f / 8.0f)
+#define SPEAKER_AZIMUTH_FRONT_RIGHT		(F3DAUDIO_PI *  1.0f / 4.0f)
+#define SPEAKER_AZIMUTH_SIDE_RIGHT		(F3DAUDIO_PI *  1.0f / 2.0f)
+#define SPEAKER_AZIMUTH_BACK_RIGHT		(F3DAUDIO_PI *  3.0f / 4.0f)
+#define SPEAKER_AZIMUTH_BACK_CENTER		F3DAUDIO_PI
+#define SPEAKER_AZIMUTH_BACK_LEFT		(F3DAUDIO_PI *  5.0f / 4.0f)
+#define SPEAKER_AZIMUTH_SIDE_LEFT		(F3DAUDIO_PI *  3.0f / 2.0f)
+#define SPEAKER_AZIMUTH_FRONT_LEFT		(F3DAUDIO_PI *  7.0f / 4.0f)
+#define SPEAKER_AZIMUTH_FRONT_LEFT_OF_CENTER	(F3DAUDIO_PI * 15.0f / 8.0f)
+
+const SpeakerInfo kMonoConfigSpeakers[] =
+{
+	{ SPEAKER_AZIMUTH_CENTER, 0 },
+};
+const SpeakerInfo kStereoConfigSpeakers[] =
+{
+	{ SPEAKER_AZIMUTH_SIDE_RIGHT,	1 },
+	{ SPEAKER_AZIMUTH_SIDE_LEFT,	0 },
+};
+const SpeakerInfo k2Point1ConfigSpeakers[] =
+{
+	{ SPEAKER_AZIMUTH_SIDE_RIGHT,	1 },
+	{ SPEAKER_AZIMUTH_SIDE_LEFT,	0 },
+};
+const SpeakerInfo kSurroundConfigSpeakers[] =
+{
+	{ SPEAKER_AZIMUTH_CENTER,	2 },
+	{ SPEAKER_AZIMUTH_FRONT_RIGHT,	1 },
+	{ SPEAKER_AZIMUTH_BACK_CENTER,	3 },
+	{ SPEAKER_AZIMUTH_FRONT_LEFT,	0 },
+};
+const SpeakerInfo kQuadConfigSpeakers[] =
+{
+	{ SPEAKER_AZIMUTH_FRONT_RIGHT, 1 },
+	{ SPEAKER_AZIMUTH_BACK_RIGHT,  3 },
+	{ SPEAKER_AZIMUTH_BACK_LEFT,   2 },
+	{ SPEAKER_AZIMUTH_FRONT_LEFT,  0 },
+};
+const SpeakerInfo k4Point1ConfigSpeakers[] =
+{
+	{ SPEAKER_AZIMUTH_FRONT_RIGHT,	1 },
+	{ SPEAKER_AZIMUTH_BACK_RIGHT,	4 },
+	{ SPEAKER_AZIMUTH_BACK_LEFT,	3 },
+	{ SPEAKER_AZIMUTH_FRONT_LEFT,	0 },
+};
+const SpeakerInfo k5Point1ConfigSpeakers[] =
+{
+	{ SPEAKER_AZIMUTH_CENTER,	2 },
+	{ SPEAKER_AZIMUTH_FRONT_RIGHT,	1 },
+	{ SPEAKER_AZIMUTH_BACK_RIGHT,	5 },
+	{ SPEAKER_AZIMUTH_BACK_LEFT,	4 },
+	{ SPEAKER_AZIMUTH_FRONT_LEFT,	0 },
+};
+const SpeakerInfo k7Point1ConfigSpeakers[] =
+{
+	{ SPEAKER_AZIMUTH_CENTER,			2 },
+	{ SPEAKER_AZIMUTH_FRONT_RIGHT_OF_CENTER,	7 },
+	{ SPEAKER_AZIMUTH_FRONT_RIGHT,			1 },
+	{ SPEAKER_AZIMUTH_BACK_RIGHT,			5 },
+	{ SPEAKER_AZIMUTH_BACK_LEFT,			4 },
+	{ SPEAKER_AZIMUTH_FRONT_LEFT,			0 },
+	{ SPEAKER_AZIMUTH_FRONT_LEFT_OF_CENTER,		6 },
+};
+const SpeakerInfo k5Point1SurroundConfigSpeakers[] =
+{
+	{ SPEAKER_AZIMUTH_CENTER,	2 },
+	{ SPEAKER_AZIMUTH_FRONT_RIGHT,	1 },
+	{ SPEAKER_AZIMUTH_SIDE_RIGHT,	5 },
+	{ SPEAKER_AZIMUTH_SIDE_LEFT,	4 },
+	{ SPEAKER_AZIMUTH_FRONT_LEFT,	0 },
+};
+const SpeakerInfo k7Point1SurroundConfigSpeakers[] =
+{
+	{ SPEAKER_AZIMUTH_CENTER,	2 },
+	{ SPEAKER_AZIMUTH_FRONT_RIGHT,	1 },
+	{ SPEAKER_AZIMUTH_SIDE_RIGHT,	7 },
+	{ SPEAKER_AZIMUTH_BACK_RIGHT,	5 },
+	{ SPEAKER_AZIMUTH_BACK_LEFT,	4 },
+	{ SPEAKER_AZIMUTH_SIDE_LEFT,	6 },
+	{ SPEAKER_AZIMUTH_FRONT_LEFT,	0 },
+};
+
+/* With that organization, the index of the LF speaker into the matrix array
+ * strangely looks *exactly* like the mystery field in the F3DAUDIO_HANDLE!!
+ * We're keeping a separate field within ConfigInfo because it makes the code
+ * much cleaner, though.
+ * -Adrien
+ */
+const ConfigInfo kSpeakersConfigInfo[] =
+{
+	{ SPEAKER_MONO,			kMonoConfigSpeakers,		ARRAY_COUNT(kMonoConfigSpeakers),		-1 },
+	{ SPEAKER_STEREO,		kStereoConfigSpeakers,		ARRAY_COUNT(kStereoConfigSpeakers),		-1 },
+	{ SPEAKER_2POINT1,		k2Point1ConfigSpeakers,		ARRAY_COUNT(k2Point1ConfigSpeakers),		 2 },
+	{ SPEAKER_SURROUND,		kSurroundConfigSpeakers,	ARRAY_COUNT(kSurroundConfigSpeakers),		-1 },
+	{ SPEAKER_QUAD,			kQuadConfigSpeakers,		ARRAY_COUNT(kQuadConfigSpeakers),		-1 },
+	{ SPEAKER_4POINT1,		k4Point1ConfigSpeakers,		ARRAY_COUNT(k4Point1ConfigSpeakers),		 2 },
+	{ SPEAKER_5POINT1,		k5Point1ConfigSpeakers,		ARRAY_COUNT(k5Point1ConfigSpeakers),		 3 },
+	{ SPEAKER_7POINT1,		k7Point1ConfigSpeakers,		ARRAY_COUNT(k7Point1ConfigSpeakers),		 3 },
+	{ SPEAKER_5POINT1_SURROUND,	k5Point1SurroundConfigSpeakers,	ARRAY_COUNT(k5Point1SurroundConfigSpeakers),	 3 },
+	{ SPEAKER_7POINT1_SURROUND,	k7Point1SurroundConfigSpeakers,	ARRAY_COUNT(k7Point1SurroundConfigSpeakers),	 3 },
+};
+
+/* A simple linear search is absolutely OK for 10 elements. */
+static const ConfigInfo* GetConfigInfo(uint32_t speakerConfigMask)
+{
+	uint32_t i;
+	for (i = 0; i < ARRAY_COUNT(kSpeakersConfigInfo); i += 1)
+	{
+		if (kSpeakersConfigInfo[i].configMask == speakerConfigMask)
+		{
+			return &kSpeakersConfigInfo[i];
+		}
+	}
+
+	FAudio_assert(0 && "Config info not found!");
+	return NULL;
+}
+
+/* Given a configuration, this function finds the azimuths of the two speakers
+ * between which the emitter lies. All the azimuths here are relative to the
+ * listener's base, since that's where the speakers are defined.
+ */
+static inline void FindSpeakerAzimuths(
+	const ConfigInfo* config,
+	float emitterAzimuth,
+	uint8_t skipCenter,
+	const SpeakerInfo **speakerInfo
+) {
+	uint32_t i, nexti = 0;
+	float a0 = 0.0f, a1 = 0.0f;
+
+	FAudio_assert(config != NULL);
+
+	/* We want to find, given an azimuth, which speakers are the closest
+	 * ones (in terms of angle) to that azimuth.
+	 * This is done by iterating through the list of speaker azimuths, as
+	 * given to us by the current ConfigInfo (which stores speaker azimuths
+	 * in increasing order of azimuth for each possible speaker configuration;
+	 * each speaker azimuth is defined to be between 0 and 2PI by construction).
+	 */
+	for (i = 0; i < config->numNonLFSpeakers; i += 1)
+	{
+		/* a0 and a1 are the azimuths of candidate speakers */
+		a0 = config->speakers[i].azimuth;
+		nexti = (i + 1) % config->numNonLFSpeakers;
+		a1 = config->speakers[nexti].azimuth;
+
+		if (a0 < a1)
+		{
+			if (emitterAzimuth >= a0 && emitterAzimuth < a1)
+			{
+				break;
+			}
+		}
+		/* It is possible for a speaker pair to enclose the singulary at 0 == 2PI:
+		 * consider for example the quad config, which has a front left speaker
+		 * at 7PI/4 and a front right speaker at PI/4. In that case a0 = 7PI/4 and
+		 * a1 = PI/4, and the way we know whether our current azimuth lies between
+		 * that pair is by checking whether the azimuth is greather than 7PI/4 or
+		 * whether it's less than PI/4. (By contract, currentAzimuth is always less
+		 * than 2PI.)
+		 */
+		else
+		{
+			if (emitterAzimuth >= a0 || emitterAzimuth < a1)
+			{
+				break;
+			}
+		}
+	}
+	FAudio_assert(emitterAzimuth >= a0 || emitterAzimuth < a1);
+
+	/* skipCenter means that we don't want to use the center speaker.
+	 * The easiest way to deal with this is to check whether either of our candidate
+	 * speakers are the center, which always has an azimuth of 0.0. If that is the case
+	 * we just replace it with either the previous one or the next one.
+	 */
+	if (skipCenter)
+	{
+		if (a0 == 0.0f)
+		{
+			if (i == 0)
+			{
+				i = config->numNonLFSpeakers - 1;
+			}
+			else
+			{
+				i -= 1;
+			}
+		}
+		else if (a1 == 0.0f)
+		{
+			nexti += 1;
+			if (nexti >= config->numNonLFSpeakers)
+			{
+				nexti -= config->numNonLFSpeakers;
+			}
+		}
+	}
+	speakerInfo[0] = &config->speakers[i];
+	speakerInfo[1] = &config->speakers[nexti];
+}
+
+/* Used to store diffusion factors */
+/* See below for explanation. */
+#define DIFFUSION_SPEAKERS_ALL		0
+#define DIFFUSION_SPEAKERS_MATCHING	1
+#define DIFFUSION_SPEAKERS_OPPOSITE	2
+typedef float DiffusionSpeakerFactors[3];
+
+/* ComputeInnerRadiusDiffusionFactors is a utility function that returns how
+ * energy dissipates to the speakers, given the radial distance between the
+ * emitter and the listener and the (optionally 0) InnerRadius distance. It
+ * returns 3 floats, via the diffusionFactors array, that say how much energy
+ * (after distance attenuation) will need to be distributed between each of the
+ * following cases:
+ *
+ * - SPEAKERS_ALL for all (non-LF) speakers, _INCLUDING_ the MATCHING and OPPOSITE.
+ * - SPEAKERS_OPPOSITE corresponds to the two speakers OPPOSITE the emitter.
+ * - SPEAKERS_MATCHING corresponds to the two speakers closest to the emitter.
+ *
+ * For a distance below a certain threshold (DISTANCE_EQUAL_ENERGY), all
+ * speakers receive equal energy.
+ *
+ * Above that, the amount that all speakers receive decreases linearly as radial
+ * distance increases, up until InnerRadius / 2. (If InnerRadius is null, we use
+ * MINIMUM_INNER_RADIUS.)
+ *
+ * At the same time, both opposite and matching speakers start to receive sound
+ * (in addition to the energy they receive from the aforementioned "all
+ * speakers" linear law) according to some unknown as of now law,
+ * that is currently emulated with a LERP. This is true up until InnerRadius.
+ *
+ * Above InnerRadius, only the two matching speakers receive sound.
+ *
+ * For more detail, see the "Inner Radius and Inner Radius Angle" in the
+ * MSDN docs for the X3DAUDIO_EMITTER structure.
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.x3daudio.x3daudio_emitter(v=vs.85).aspx
+ */
+static inline void ComputeInnerRadiusDiffusionFactors(
+	float radialDistance,
+	float InnerRadius,
+	DiffusionSpeakerFactors diffusionFactors
+) {
+
+	/* Determined experimentally; this is the midpoint value, i.e. the
+	 * value at 0.5 for the matching speakers, used for the standard
+	 * diffusion curve.
+	 *
+	 * Note: It is SUSPICIOUSLY close to 1/sqrt(2), but I haven't figured out why.
+	 * -Adrien
+	 */
+	#define DIFFUSION_LERP_MIDPOINT_VALUE 0.707107f
+
+	/* X3DAudio always uses an InnerRadius-like behaviour (i.e. diffusing sound to more than
+	 * a pair of speakers) even if InnerRadius is set to 0.0f.
+	 * This constant determines the distance at which this behaviour is produced in that case. */
+	/* This constant was determined by manual binary search. TODO: get a more accurate version
+	 * via an automated binary search. */
+	#define DIFFUSION_DISTANCE_MINIMUM_INNER_RADIUS 4e-7f
+	float actualInnerRadius = FAudio_max(InnerRadius, DIFFUSION_DISTANCE_MINIMUM_INNER_RADIUS);
+	float normalizedRadialDist;
+	float a, ms, os;
+
+	normalizedRadialDist = radialDistance / actualInnerRadius;
+
+	/* X3DAudio does another check for small radial distances before applying any InnerRadius-like
+	 * behaviour. This is the constant that determines the threshold: below this distance we simply
+	 * diffuse to all speakers equally. */
+	#define DIFFUSION_DISTANCE_EQUAL_ENERGY 1e-7f
+	if (radialDistance <= DIFFUSION_DISTANCE_EQUAL_ENERGY)
+	{
+		a = 1.0f;
+		ms = 0.0f;
+		os = 0.0f;
+	}
+	else if (normalizedRadialDist <= 0.5f)
+	{
+		/* Determined experimentally that this is indeed a linear law,
+		 * with 100% confidence.
+		 * -Adrien
+		 */
+		a = 1.0f - 2.0f * normalizedRadialDist;
+
+		/* Lerping here is an approximation.
+		 * TODO: High accuracy version. Having stared at the curves long
+		 * enough, I'm pretty sure this is a quadratic, but trying to
+		 * polyfit with numpy didn't give nice, round polynomial
+		 * coefficients...
+		 * -Adrien
+		 */
+		ms = LERP(2.0f * normalizedRadialDist, 0.0f, DIFFUSION_LERP_MIDPOINT_VALUE);
+		os = 1.0f - a - ms;
+	}
+	else if (normalizedRadialDist <= 1.0f)
+	{
+		a = 0.0f;
+
+		/* Similarly, this is a lerp based on the midpoint value; the
+		 * real, high-accuracy curve also looks like a quadratic.
+		 * -Adrien
+		 */
+		ms = LERP(2.0f * (normalizedRadialDist - 0.5f), DIFFUSION_LERP_MIDPOINT_VALUE, 1.0f);
+		os = 1.0f - a - ms;
+	}
+	else
+	{
+		a = 0.0f;
+		ms = 1.0f;
+		os = 0.0f;
+	}
+	diffusionFactors[DIFFUSION_SPEAKERS_ALL] = a;
+	diffusionFactors[DIFFUSION_SPEAKERS_MATCHING] = ms;
+	diffusionFactors[DIFFUSION_SPEAKERS_OPPOSITE] = os;
+}
+
+/* ComputeEmitterChannelCoefficients handles the coefficients calculation for 1
+ * column of the matrix. It uses ComputeInnerRadiusDiffusionFactors to separate
+ * into three discrete cases; and for each case does the right repartition of
+ * the energy after attenuation to the right speakers, in particular in the
+ * MATCHING and OPPOSITE cases, it gives each of the two speakers found a linear
+ * amount of the energy, according to the angular distance between the emitter
+ * and the speaker azimuth.
+ */
+static inline void ComputeEmitterChannelCoefficients(
+	const ConfigInfo *curConfig,
+	const F3DAUDIO_BASIS *listenerBasis,
+	float innerRadius,
+	F3DAUDIO_VECTOR channelPosition,
+	float attenuation,
+	float LFEattenuation,
+	uint32_t flags,
+	uint32_t currentChannel,
+	uint32_t numSrcChannels,
+	float *pMatrixCoefficients
+) {
+	float elevation, radialDistance;
+	F3DAUDIO_VECTOR projTopVec, projPlane;
+	uint8_t skipCenter = (flags & F3DAUDIO_CALCULATE_ZEROCENTER) ? 1 : 0;
+	DiffusionSpeakerFactors diffusionFactors = { 0.0f };
+
+	float x, y;
+	float emitterAzimuth;
+	float energyPerChannel;
+	float totalEnergy;
+	uint32_t nChannelsToDiffuseTo;
+	uint32_t iS, centerChannelIdx = -1;
+	const SpeakerInfo* infos[2];
+	float a0, a1, val;
+	uint32_t i0, i1;
+
+	/* We project against the listener basis' top vector to get the elevation of the
+	 * current emitter channel position.
+	 */
+	elevation = VectorDot(listenerBasis->top, channelPosition);
+
+	/* To obtain the projection in the front-right plane of the listener's basis of the
+	 * emitter channel position, we simply remove the projection against the top vector.
+	 * The radial distance is then the length of the projected vector.
+	 */
+	projTopVec = VectorScale(listenerBasis->top, elevation);
+	projPlane = VectorSub(channelPosition, projTopVec);
+	radialDistance = VectorLength(projPlane);
+
+	ComputeInnerRadiusDiffusionFactors(
+		radialDistance,
+		innerRadius,
+		diffusionFactors
+	);
+
+	/* See the ComputeInnerRadiusDiffusionFactors comment above for more context. */
+	/* DIFFUSION_SPEAKERS_ALL corresponds to diffusing part of the sound to all of the
+	 * speakers, equally. The amount of sound is determined by the float value
+	 * diffusionFactors[DIFFUSION_SPEAKERS_ALL]. */
+	if (diffusionFactors[DIFFUSION_SPEAKERS_ALL] > 0.0f)
+	{
+		nChannelsToDiffuseTo = curConfig->numNonLFSpeakers;
+		totalEnergy = diffusionFactors[DIFFUSION_SPEAKERS_ALL] * attenuation;
+
+		if (skipCenter)
+		{
+			nChannelsToDiffuseTo -= 1;
+			FAudio_assert(curConfig->speakers[0].azimuth == SPEAKER_AZIMUTH_CENTER);
+			centerChannelIdx = curConfig->speakers[0].matrixIdx;
+		}
+
+		energyPerChannel = totalEnergy / nChannelsToDiffuseTo;
+
+		for (iS = 0; iS < curConfig->numNonLFSpeakers; iS += 1)
+		{
+			const uint32_t curSpeakerIdx = curConfig->speakers[iS].matrixIdx;
+			if (skipCenter && curSpeakerIdx == centerChannelIdx)
+			{
+				continue;
+			}
+
+			pMatrixCoefficients[curSpeakerIdx * numSrcChannels + currentChannel] += energyPerChannel;
+		}
+	}
+
+	/* DIFFUSION_SPEAKERS_MATCHING corresponds to sending part of the sound to the speakers closest
+	 * (in terms of azimuth) to the current position of the emitter. The amount of sound we shoud send
+	 * corresponds here to diffusionFactors[DIFFUSION_SPEAKERS_MATCHING].
+	 * We use the FindSpeakerAzimuths function to find the speakers that match. */
+	if (diffusionFactors[DIFFUSION_SPEAKERS_MATCHING] > 0.0f)
+	{
+		const float totalEnergy = diffusionFactors[DIFFUSION_SPEAKERS_MATCHING] * attenuation;
+
+		x = VectorDot(listenerBasis->front, projPlane);
+		y = VectorDot(listenerBasis->right, projPlane);
+
+		/* Now, a critical point: We shouldn't be sending sound to
+		 * matching speakers when x and y are close to 0. That's the
+		 * contract we get from ComputeInnerRadiusDiffusionFactors,
+		 * which checks that we're not too close to the zero distance.
+		 * This allows the atan2 calculation to give good results.
+		 */
+
+		/* atan2 returns [-PI, PI], but we want [0, 2PI] */
+		emitterAzimuth = FAudio_atan2f(y, x);
+		if (emitterAzimuth < 0.0f)
+		{
+			emitterAzimuth += F3DAUDIO_2PI;
+		}
+
+		FindSpeakerAzimuths(curConfig, emitterAzimuth, skipCenter, infos);
+		a0 = infos[0]->azimuth;
+		a1 = infos[1]->azimuth;
+
+		/* The following code is necessary to handle the singularity in
+		 * (0 == 2PI). It'll give us a nice, well ordered interval.
+		 */
+		if (a0 > a1)
+		{
+			if (emitterAzimuth >= a0)
+			{
+				emitterAzimuth -= F3DAUDIO_2PI;
+			}
+			a0 -= F3DAUDIO_2PI;
+		}
+		FAudio_assert(emitterAzimuth >= a0 && emitterAzimuth <= a1);
+
+		val = (emitterAzimuth - a0) / (a1 - a0);
+
+		i0 = infos[0]->matrixIdx;
+		i1 = infos[1]->matrixIdx;
+
+		pMatrixCoefficients[i0 * numSrcChannels + currentChannel] += (1.0f - val) * totalEnergy;
+		pMatrixCoefficients[i1 * numSrcChannels + currentChannel] += (       val) * totalEnergy;
+	}
+
+	/* DIFFUSION_SPEAKERS_OPPOSITE corresponds to sending part of the sound to the speakers
+	 * _opposite_ the ones that are the closest to the current emitter position.
+	 * To find these, we simply find the ones that are closest to the current emitter's azimuth + PI
+	 * using the FindSpeakerAzimuth function. */
+	if (diffusionFactors[DIFFUSION_SPEAKERS_OPPOSITE] > 0.0f)
+	{
+		/* This code is similar to the matching speakers code above. */
+		const float totalEnergy = diffusionFactors[DIFFUSION_SPEAKERS_OPPOSITE] * attenuation;
+
+		x = VectorDot(listenerBasis->front, projPlane);
+		y = VectorDot(listenerBasis->right, projPlane);
+
+		/* Similarly, we expect atan2 to be well behaved here. */
+		emitterAzimuth = FAudio_atan2f(y, x);
+
+		/* Opposite speakers lie at azimuth + PI */
+		emitterAzimuth += F3DAUDIO_PI;
+
+		/* Normalize to [0; 2PI) range. */
+		if (emitterAzimuth < 0.0f)
+		{
+			emitterAzimuth += F3DAUDIO_2PI;
+		}
+		else if (emitterAzimuth > F3DAUDIO_2PI)
+		{
+			emitterAzimuth -= F3DAUDIO_2PI;
+		}
+
+		FindSpeakerAzimuths(curConfig, emitterAzimuth, skipCenter, infos);
+		a0 = infos[0]->azimuth;
+		a1 = infos[1]->azimuth;
+
+		/* The following code is necessary to handle the singularity in
+		 * (0 == 2PI). It'll give us a nice, well ordered interval.
+		 */
+		if (a0 > a1)
+		{
+			if (emitterAzimuth >= a0)
+			{
+				emitterAzimuth -= F3DAUDIO_2PI;
+			}
+			a0 -= F3DAUDIO_2PI;
+		}
+		FAudio_assert(emitterAzimuth >= a0 && emitterAzimuth <= a1);
+
+		val = (emitterAzimuth - a0) / (a1 - a0);
+
+		i0 = infos[0]->matrixIdx;
+		i1 = infos[1]->matrixIdx;
+
+		pMatrixCoefficients[i0 * numSrcChannels + currentChannel] += (1.0f - val) * totalEnergy;
+		pMatrixCoefficients[i1 * numSrcChannels + currentChannel] += (       val) * totalEnergy;
+	}
+
+	if (flags & F3DAUDIO_CALCULATE_REDIRECT_TO_LFE)
+	{
+		FAudio_assert(curConfig->LFSpeakerIdx != -1);
+		pMatrixCoefficients[curConfig->LFSpeakerIdx * numSrcChannels + currentChannel] += LFEattenuation / numSrcChannels;
+	}
+}
+
+/* Calculations consist of several orthogonal steps that compose multiplicatively:
+ *
+ * First, we compute the attenuations (volume and LFE) due to distance, which
+ * may involve an optional volume and/or LFE volume curve.
+ *
+ * Then, we compute those due to optional cones.
+ *
+ * We then compute how much energy is diffuse w.r.t InnerRadius. If InnerRadius
+ * is 0.0f, this step is computed as if it was InnerRadius was
+ * NON_NULL_DISTANCE_DISK_RADIUS. The way this works is, we look at the radial
+ * distance of the current emitter channel to the listener, with regard to the
+ * listener's top orientation (i.e. this distance is independant of the
+ * emitter's elevation!). If this distance is less than NULL_DISTANCE_RADIUS,
+ * energy is diffused equally between all channels. If it's greater than
+ * InnerRadius (or NON_NULL_DISTANCE_RADIUS, if InnerRadius is 0.0f, as
+ * mentioned above), the two closest speakers, by azimuth, receive all the
+ * energy. Between InnerRadius/2.0f and InnerRadius, the energy starts bleeding
+ * into the opposite speakers. Once we go below InnerRadius/2.0f, the energy
+ * also starts to bleed into the other (non-opposite) channels, if there are
+ * any. This computation is handled by the ComputeInnerRadiusDiffusionFactors
+ * function. (TODO: High-accuracy version of this.)
+ *
+ * Finally, if we're not in the equal diffusion case, we find out the azimuths
+ * of the two closest speakers (with azimuth being defined with respect to the
+ * listener's front orientation, in the plane normal to the listener's top
+ * vector), as well as the azimuths of the two opposite speakers, if necessary,
+ * and linearly interpolate with respect to the angular distance. In the equal
+ * diffusion case, each channel receives the same value.
+ *
+ * Note: in the case of multi-channel emitters, the distance attenuation is only
+ * compted once, but all the azimuths and InnerRadius calculations are done per
+ * emitter channel.
+ *
+ * TODO: Handle InnerRadiusAngle. But honestly the X3DAudio default behaviour is
+ * so wacky that I wonder if anybody has ever used it.
+ * -Adrien
+ */
+static inline void CalculateMatrix(
+	uint32_t ChannelMask,
+	uint32_t Flags,
+	const F3DAUDIO_LISTENER *pListener,
+	const F3DAUDIO_EMITTER *pEmitter,
+	uint32_t SrcChannelCount,
+	uint32_t DstChannelCount,
+	F3DAUDIO_VECTOR emitterToListener,
+	float eToLDistance,
+	float normalizedDistance,
+	float* MatrixCoefficients
+) {
+	uint32_t iEC;
+	float curEmAzimuth;
+	const ConfigInfo* curConfig = GetConfigInfo(ChannelMask);
+	float attenuation = ComputeDistanceAttenuation(
+		normalizedDistance,
+		pEmitter->pVolumeCurve
+	);
+	/* TODO: this could be skipped if the destination has no LFE */
+	float LFEattenuation = ComputeDistanceAttenuation(
+		normalizedDistance,
+		pEmitter->pLFECurve
+	);
+
+	F3DAUDIO_VECTOR listenerToEmitter;
+	F3DAUDIO_VECTOR listenerToEmChannel;
+	F3DAUDIO_BASIS listenerBasis;
+
+	/* Note: For both cone calculations, the angle might be NaN or infinite
+	 * if distance == 0... ComputeConeParameter *does* check for this
+	 * special case. It is necessary that we still go through the
+	 * ComputeConeParameter function, because omnidirectional cones might
+	 * give either InnerVolume or OuterVolume.
+	 * -Adrien
+	 */
+	if (pListener->pCone)
+	{
+		/* Negate the dot product because we need listenerToEmitter in
+		 * this case
+		 * -Adrien
+		 */
+		const float angle = -FAudio_acosf(
+			VectorDot(pListener->OrientFront, emitterToListener) /
+			eToLDistance
+		);
+
+		const float listenerConeParam = ComputeConeParameter(
+			eToLDistance,
+			angle,
+			pListener->pCone->InnerAngle,
+			pListener->pCone->OuterAngle,
+			pListener->pCone->InnerVolume,
+			pListener->pCone->OuterVolume
+		);
+		attenuation *= listenerConeParam;
+		LFEattenuation *= listenerConeParam;
+	}
+
+	/* See note above. */
+	if (pEmitter->pCone && pEmitter->ChannelCount == 1)
+	{
+		const float angle = FAudio_acosf(
+			VectorDot(pEmitter->OrientFront, emitterToListener) /
+			eToLDistance
+		);
+
+		const float emitterConeParam = ComputeConeParameter(
+			eToLDistance,
+			angle,
+			pEmitter->pCone->InnerAngle,
+			pEmitter->pCone->OuterAngle,
+			pEmitter->pCone->InnerVolume,
+			pEmitter->pCone->OuterVolume
+		);
+		attenuation *= emitterConeParam;
+	}
+
+	FAudio_zero(MatrixCoefficients, sizeof(float) * SrcChannelCount * DstChannelCount);
+
+	/* In the SPEAKER_MONO case, we can skip all energy diffusion calculation. */
+	if (DstChannelCount == 1)
+	{
+		for (iEC = 0; iEC < pEmitter->ChannelCount; iEC += 1)
+		{
+			curEmAzimuth = 0.0f;
+			if (pEmitter->pChannelAzimuths)
+			{
+				curEmAzimuth = pEmitter->pChannelAzimuths[iEC];
+			}
+
+			/* The MONO setup doesn't have an LFE speaker. */
+			if (curEmAzimuth != F3DAUDIO_2PI)
+			{
+				MatrixCoefficients[iEC] = attenuation;
+			}
+		}
+	}
+	else
+	{
+		listenerToEmitter = VectorScale(emitterToListener, -1.0f);
+
+		/* Remember here that the coordinate system is Left-Handed. */
+		listenerBasis.front = pListener->OrientFront;
+		listenerBasis.right = VectorCross(pListener->OrientTop, pListener->OrientFront);
+		listenerBasis.top = pListener->OrientTop;
+
+
+		/* Handling the mono-channel emitter case separately is easier
+		 * than having it as a separate case of a for-loop; indeed, in
+		 * this case, we need to ignore the non-relevant values from the
+		 * emitter, _even if they're set_.
+		 */
+		if (pEmitter->ChannelCount == 1)
+		{
+			listenerToEmChannel = listenerToEmitter;
+
+			ComputeEmitterChannelCoefficients(
+				curConfig,
+				&listenerBasis,
+				pEmitter->InnerRadius,
+				listenerToEmChannel,
+				attenuation,
+				LFEattenuation,
+				Flags,
+				0 /* currentChannel */,
+				1 /* numSrcChannels */,
+				MatrixCoefficients
+			);
+		}
+		else /* Multi-channel emitter case. */
+		{
+			const F3DAUDIO_VECTOR emitterRight = VectorCross(pEmitter->OrientTop, pEmitter->OrientFront);
+
+			for (iEC = 0; iEC < pEmitter->ChannelCount; iEC += 1)
+			{
+				const float emChAzimuth = pEmitter->pChannelAzimuths[iEC];
+
+				/* LFEs are easy enough to deal with; we can
+				 * just do them separately.
+				 */
+				if (emChAzimuth == F3DAUDIO_2PI)
+				{
+					MatrixCoefficients[curConfig->LFSpeakerIdx * pEmitter->ChannelCount + iEC] = LFEattenuation;
+				}
+				else
+				{
+					/* First compute the emitter channel
+					 * vector relative to the emitter base...
+					 */
+					const F3DAUDIO_VECTOR emitterBaseToChannel = VectorAdd(
+						VectorScale(pEmitter->OrientFront, pEmitter->ChannelRadius * FAudio_cosf(emChAzimuth)),
+						VectorScale(emitterRight, pEmitter->ChannelRadius * FAudio_sinf(emChAzimuth))
+					);
+					/* ... then translate. */
+					listenerToEmChannel = VectorAdd(
+						listenerToEmitter,
+						emitterBaseToChannel
+					);
+
+					ComputeEmitterChannelCoefficients(
+						curConfig,
+						&listenerBasis,
+						pEmitter->InnerRadius,
+						listenerToEmChannel,
+						attenuation,
+						LFEattenuation,
+						Flags,
+						iEC,
+						pEmitter->ChannelCount,
+						MatrixCoefficients
+					);
+				}
+			}
+		}
+
+
+	}
+
+	/* TODO: add post check to validate values
+	 * (sum < 1, all values > 0, no Inf / NaN..
+	 * Sum can be >1 when cone or curve is set to a gain!
+	 * Perhaps under a paranoid check disabled by default.
+	 */
+}
+
+/*
+ * OTHER CALCULATIONS
+ */
+
+/* DopplerPitchScalar
+ * Adapted from algorithm published as a part of the webaudio specification:
+ * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#Spatialization-doppler-shift
+ * -Chad
+ */
+static inline void CalculateDoppler(
+	float SpeedOfSound,
+	const F3DAUDIO_LISTENER* pListener,
+	const F3DAUDIO_EMITTER* pEmitter,
+	F3DAUDIO_VECTOR emitterToListener,
+	float eToLDistance,
+	float* listenerVelocityComponent,
+	float* emitterVelocityComponent,
+	float* DopplerFactor
+) {
+	float scaledSpeedOfSound;
+	*DopplerFactor = 1.0f;
+
+	/* Project... */
+	if (eToLDistance != 0.0f)
+	{
+		*listenerVelocityComponent =
+			VectorDot(emitterToListener, pListener->Velocity) / eToLDistance;
+		*emitterVelocityComponent =
+			VectorDot(emitterToListener, pEmitter->Velocity) / eToLDistance;
+	}
+	else
+	{
+		*listenerVelocityComponent = 0.0f;
+		*emitterVelocityComponent = 0.0f;
+	}
+
+	if (pEmitter->DopplerScaler > 0.0f)
+	{
+		scaledSpeedOfSound = SpeedOfSound / pEmitter->DopplerScaler;
+
+		/* Clamp... */
+		*listenerVelocityComponent = FAudio_min(
+			*listenerVelocityComponent,
+			scaledSpeedOfSound
+		);
+		*emitterVelocityComponent = FAudio_min(
+			*emitterVelocityComponent,
+			scaledSpeedOfSound
+		);
+
+		/* ... then Multiply. */
+		*DopplerFactor = (
+			SpeedOfSound - pEmitter->DopplerScaler * *listenerVelocityComponent
+		) / (
+			SpeedOfSound - pEmitter->DopplerScaler * *emitterVelocityComponent
+		);
+		if (isnan(*DopplerFactor)) /* If emitter/listener are at the same pos... */
+		{
+			*DopplerFactor = 1.0f;
+		}
+
+		/* Limit the pitch shifting to 2 octaves up and 1 octave down */
+		*DopplerFactor = FAudio_clamp(
+			*DopplerFactor,
+			0.5f,
+			4.0f
+		);
+	}
+}
+
+void F3DAudioCalculate(
+	const F3DAUDIO_HANDLE Instance,
+	const F3DAUDIO_LISTENER *pListener,
+	const F3DAUDIO_EMITTER *pEmitter,
+	uint32_t Flags,
+	F3DAUDIO_DSP_SETTINGS *pDSPSettings
+) {
+	uint32_t i;
+	F3DAUDIO_VECTOR emitterToListener;
+	float eToLDistance, normalizedDistance, dp;
+
+	#define DEFAULT_POINTS(name, x1, y1, x2, y2) \
+		static F3DAUDIO_DISTANCE_CURVE_POINT name##Points[2] = \
+		{ \
+			{ x1, y1 }, \
+			{ x2, y2 } \
+		}; \
+		static F3DAUDIO_DISTANCE_CURVE name##Default = \
+		{ \
+			(F3DAUDIO_DISTANCE_CURVE_POINT*) &name##Points[0], 2 \
+		};
+	DEFAULT_POINTS(lpfDirect, 0.0f, 1.0f, 1.0f, 0.75f)
+	DEFAULT_POINTS(lpfReverb, 0.0f, 0.75f, 1.0f, 0.75f)
+	DEFAULT_POINTS(reverb, 0.0f, 1.0f, 1.0f, 0.0f)
+	#undef DEFAULT_POINTS
+
+	/* For XACT, this calculates "Distance" */
+	emitterToListener = VectorSub(pListener->Position, pEmitter->Position);
+	eToLDistance = VectorLength(emitterToListener);
+	pDSPSettings->EmitterToListenerDistance = eToLDistance;
+
+	F3DAudioCheckCalculateParams(Instance, pListener, pEmitter, Flags, pDSPSettings);
+
+	/* This is used by MATRIX, LPF, and REVERB */
+	normalizedDistance = eToLDistance / pEmitter->CurveDistanceScaler;
+
+	if (Flags & F3DAUDIO_CALCULATE_MATRIX)
+	{
+		CalculateMatrix(
+			SPEAKERMASK(Instance),
+			Flags,
+			pListener,
+			pEmitter,
+			pDSPSettings->SrcChannelCount,
+			pDSPSettings->DstChannelCount,
+			emitterToListener,
+			eToLDistance,
+			normalizedDistance,
+			pDSPSettings->pMatrixCoefficients
+		);
+	}
+
+	if (Flags & F3DAUDIO_CALCULATE_LPF_DIRECT)
+	{
+		pDSPSettings->LPFDirectCoefficient = ComputeDistanceAttenuation(
+			normalizedDistance,
+			(pEmitter->pLPFDirectCurve != NULL) ?
+				pEmitter->pLPFDirectCurve :
+				&lpfDirectDefault
+		);
+	}
+
+	if (Flags & F3DAUDIO_CALCULATE_LPF_REVERB)
+	{
+		pDSPSettings->LPFReverbCoefficient = ComputeDistanceAttenuation(
+			normalizedDistance,
+			(pEmitter->pLPFReverbCurve != NULL) ?
+				pEmitter->pLPFReverbCurve :
+				&lpfReverbDefault
+		);
+	}
+
+	if (Flags & F3DAUDIO_CALCULATE_REVERB)
+	{
+		pDSPSettings->ReverbLevel = ComputeDistanceAttenuation(
+			normalizedDistance,
+			(pEmitter->pReverbCurve != NULL) ?
+				pEmitter->pReverbCurve :
+				&reverbDefault
+		);
+	}
+
+	/* For XACT, this calculates "DopplerPitchScalar" */
+	if (Flags & F3DAUDIO_CALCULATE_DOPPLER)
+	{
+		CalculateDoppler(
+			SPEEDOFSOUND(Instance),
+			pListener,
+			pEmitter,
+			emitterToListener,
+			eToLDistance,
+			&pDSPSettings->ListenerVelocityComponent,
+			&pDSPSettings->EmitterVelocityComponent,
+			&pDSPSettings->DopplerFactor
+		);
+	}
+
+	/* For XACT, this calculates "OrientationAngle" */
+	if (Flags & F3DAUDIO_CALCULATE_EMITTER_ANGLE)
+	{
+		/* Determined roughly.
+		 * Below that distance, the emitter angle is considered to be PI/2.
+		 */
+		#define EMITTER_ANGLE_NULL_DISTANCE 1.2e-7
+		if (eToLDistance < EMITTER_ANGLE_NULL_DISTANCE)
+		{
+			pDSPSettings->EmitterToListenerAngle = F3DAUDIO_PI / 2.0f;
+		}
+		else
+		{
+			/* Note: pEmitter->OrientFront is normalized. */
+			dp = VectorDot(emitterToListener, pEmitter->OrientFront) / eToLDistance;
+			pDSPSettings->EmitterToListenerAngle = FAudio_acosf(dp);
+		}
+	}
+
+	/* Unimplemented Flags */
+	if (	(Flags & F3DAUDIO_CALCULATE_DELAY) &&
+		SPEAKERMASK(Instance) == SPEAKER_STEREO	)
+	{
+		for (i = 0; i < pDSPSettings->DstChannelCount; i += 1)
+		{
+			pDSPSettings->pDelayTimes[i] = 0.0f;
+		}
+		FAudio_assert(0 && "DELAY not implemented!");
+	}
+}
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FACT.c b/libs/faudio/src/FACT.c
new file mode 100644
index 00000000000..5eca83b389f
--- /dev/null
+++ b/libs/faudio/src/FACT.c
@@ -0,0 +1,3021 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "FAudioFX.h"
+#include "FACT_internal.h"
+
+/* AudioEngine implementation */
+
+uint32_t FACTCreateEngine(
+	uint32_t dwCreationFlags,
+	FACTAudioEngine **ppEngine
+) {
+	return FACTCreateEngineWithCustomAllocatorEXT(
+		dwCreationFlags,
+		ppEngine,
+		FAudio_malloc,
+		FAudio_free,
+		FAudio_realloc
+	);
+}
+
+uint32_t FACTCreateEngineWithCustomAllocatorEXT(
+	uint32_t dwCreationFlags,
+	FACTAudioEngine **ppEngine,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc
+) {
+	/* TODO: Anything fun with dwCreationFlags? */
+	FAudio_PlatformAddRef();
+	*ppEngine = (FACTAudioEngine*) customMalloc(sizeof(FACTAudioEngine));
+	if (*ppEngine == NULL)
+	{
+		return -1; /* TODO: E_OUTOFMEMORY */
+	}
+	FAudio_zero(*ppEngine, sizeof(FACTAudioEngine));
+	(*ppEngine)->sbLock = FAudio_PlatformCreateMutex();
+	(*ppEngine)->wbLock = FAudio_PlatformCreateMutex();
+	(*ppEngine)->apiLock = FAudio_PlatformCreateMutex();
+	(*ppEngine)->pMalloc = customMalloc;
+	(*ppEngine)->pFree = customFree;
+	(*ppEngine)->pRealloc = customRealloc;
+	(*ppEngine)->refcount = 1;
+	return 0;
+}
+
+uint32_t FACTAudioEngine_AddRef(FACTAudioEngine *pEngine)
+{
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+	pEngine->refcount += 1;
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return pEngine->refcount;
+}
+
+uint32_t FACTAudioEngine_Release(FACTAudioEngine *pEngine)
+{
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+	pEngine->refcount -= 1;
+	if (pEngine->refcount > 0)
+	{
+		FAudio_PlatformUnlockMutex(pEngine->apiLock);
+		return pEngine->refcount;
+	}
+	FACTAudioEngine_ShutDown(pEngine);
+	FAudio_PlatformDestroyMutex(pEngine->sbLock);
+	FAudio_PlatformDestroyMutex(pEngine->wbLock);
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	FAudio_PlatformDestroyMutex(pEngine->apiLock);
+	if (pEngine->settings != NULL)
+	{
+		pEngine->pFree(pEngine->settings);
+	}
+	pEngine->pFree(pEngine);
+	FAudio_PlatformRelease();
+	return 0;
+}
+
+uint32_t FACTAudioEngine_GetRendererCount(
+	FACTAudioEngine *pEngine,
+	uint16_t *pnRendererCount
+) {
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+	*pnRendererCount = (uint16_t) FAudio_PlatformGetDeviceCount();
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTAudioEngine_GetRendererDetails(
+	FACTAudioEngine *pEngine,
+	uint16_t nRendererIndex,
+	FACTRendererDetails *pRendererDetails
+) {
+	FAudioDeviceDetails deviceDetails;
+
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+
+	FAudio_PlatformGetDeviceDetails(
+		nRendererIndex,
+		&deviceDetails
+	);
+	FAudio_memcpy(
+		pRendererDetails->rendererID,
+		deviceDetails.DeviceID,
+		sizeof(int16_t) * 0xFF
+	);
+	FAudio_memcpy(
+		pRendererDetails->displayName,
+		deviceDetails.DisplayName,
+		sizeof(int16_t) * 0xFF
+	);
+	/* FIXME: Which defaults does it care about...? */
+	pRendererDetails->defaultDevice = (deviceDetails.Role & (
+		FAudioGlobalDefaultDevice |
+		FAudioDefaultGameDevice
+	)) != 0;
+
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTAudioEngine_GetFinalMixFormat(
+	FACTAudioEngine *pEngine,
+	FAudioWaveFormatExtensible *pFinalMixFormat
+) {
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+	FAudio_memcpy(
+		pFinalMixFormat,
+		&pEngine->audio->mixFormat,
+		sizeof(FAudioWaveFormatExtensible)
+	);
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTAudioEngine_Initialize(
+	FACTAudioEngine *pEngine,
+	const FACTRuntimeParameters *pParams
+) {
+	uint32_t parseRet;
+	uint32_t deviceIndex;
+	FAudioVoiceDetails masterDetails;
+	FAudioEffectDescriptor reverbDesc;
+	FAudioEffectChain reverbChain;
+
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+
+	if (!pParams->pGlobalSettingsBuffer || pParams->globalSettingsBufferSize == 0)
+	{
+		/* No file? Just go with a safe default. (Also why are you using XACT) */
+		pEngine->categoryCount = 3;
+		pEngine->variableCount = 0;
+		pEngine->rpcCount = 0;
+		pEngine->dspPresetCount = 0;
+		pEngine->dspParameterCount = 0;
+
+		pEngine->categories = (FACTAudioCategory*) pEngine->pMalloc(
+			sizeof(FACTAudioCategory) * pEngine->categoryCount
+		);
+		pEngine->categoryNames = (char**) pEngine->pMalloc(
+			sizeof(char*) * pEngine->categoryCount
+		);
+
+		pEngine->categoryNames[0] = pEngine->pMalloc(7);
+		FAudio_strlcpy(pEngine->categoryNames[0], "Global", 7);
+		pEngine->categories[0].instanceLimit = 255;
+		pEngine->categories[0].fadeInMS = 0;
+		pEngine->categories[0].fadeOutMS = 0;
+		pEngine->categories[0].maxInstanceBehavior = 0;
+		pEngine->categories[0].parentCategory = -1;
+		pEngine->categories[0].volume = 1.0f;
+		pEngine->categories[0].visibility = 1;
+		pEngine->categories[0].instanceCount = 0;
+		pEngine->categories[0].currentVolume = 1.0f;
+
+		pEngine->categoryNames[1] = pEngine->pMalloc(8);
+		FAudio_strlcpy(pEngine->categoryNames[1], "Default", 8);
+		pEngine->categories[1].instanceLimit = 255;
+		pEngine->categories[1].fadeInMS = 0;
+		pEngine->categories[1].fadeOutMS = 0;
+		pEngine->categories[1].maxInstanceBehavior = 0;
+		pEngine->categories[1].parentCategory = 0;
+		pEngine->categories[1].volume = 1.0f;
+		pEngine->categories[1].visibility = 1;
+		pEngine->categories[1].instanceCount = 0;
+		pEngine->categories[1].currentVolume = 1.0f;
+
+		pEngine->categoryNames[2] = pEngine->pMalloc(6);
+		FAudio_strlcpy(pEngine->categoryNames[2], "Music", 6);
+		pEngine->categories[2].instanceLimit = 255;
+		pEngine->categories[2].fadeInMS = 0;
+		pEngine->categories[2].fadeOutMS = 0;
+		pEngine->categories[2].maxInstanceBehavior = 0;
+		pEngine->categories[2].parentCategory = 0;
+		pEngine->categories[2].volume = 1.0f;
+		pEngine->categories[2].visibility = 1;
+		pEngine->categories[2].instanceCount = 0;
+		pEngine->categories[2].currentVolume = 1.0f;
+
+		pEngine->variables = NULL;
+		pEngine->variableNames = NULL;
+		pEngine->globalVariableValues = NULL;
+		pEngine->rpcs = NULL;
+		pEngine->dspPresets = NULL;
+	}
+	else
+	{
+		/* Parse the file */
+		parseRet = FACT_INTERNAL_ParseAudioEngine(pEngine, pParams);
+		if (parseRet != 0)
+		{
+			FAudio_PlatformUnlockMutex(pEngine->apiLock);
+			return parseRet;
+		}
+	}
+
+	/* Peristent Notifications */
+	pEngine->notifications = 0;
+	pEngine->cue_context = NULL;
+	pEngine->sb_context = NULL;
+	pEngine->wb_context = NULL;
+	pEngine->wave_context = NULL;
+
+	/* Assign the callbacks */
+	pEngine->notificationCallback = pParams->fnNotificationCallback;
+	pEngine->pReadFile = pParams->fileIOCallbacks.readFileCallback;
+	pEngine->pGetOverlappedResult = pParams->fileIOCallbacks.getOverlappedResultCallback;
+	if (pEngine->pReadFile == NULL)
+	{
+		pEngine->pReadFile = FACT_INTERNAL_DefaultReadFile;
+	}
+	if (pEngine->pGetOverlappedResult == NULL)
+	{
+		pEngine->pGetOverlappedResult = FACT_INTERNAL_DefaultGetOverlappedResult;
+	}
+
+	/* Init the FAudio subsystem */
+	pEngine->audio = pParams->pXAudio2;
+	if (pEngine->audio == NULL)
+	{
+		FAudio_assert(pParams->pMasteringVoice == NULL);
+		FAudioCreate(&pEngine->audio, 0, FAUDIO_DEFAULT_PROCESSOR);
+	}
+
+	/* Create the audio device */
+	pEngine->master = pParams->pMasteringVoice;
+	if (pEngine->master == NULL)
+	{
+		if (pParams->pRendererID == NULL || pParams->pRendererID[0] == 0)
+		{
+			deviceIndex = 0;
+		}
+		else
+		{
+			deviceIndex = pParams->pRendererID[0] - L'0';
+			if (deviceIndex > FAudio_PlatformGetDeviceCount())
+			{
+				deviceIndex = 0;
+			}
+		}
+		if (FAudio_CreateMasteringVoice(
+			pEngine->audio,
+			&pEngine->master,
+			FAUDIO_DEFAULT_CHANNELS,
+			FAUDIO_DEFAULT_SAMPLERATE,
+			0,
+			deviceIndex,
+			NULL
+		) != 0) {
+			FAudio_Release(pEngine->audio);
+			return FAUDIO_E_INVALID_CALL;
+		}
+	}
+
+	/* Create the reverb effect, if applicable */
+	if (pEngine->dspPresetCount > 0) /* Never more than 1...? */
+	{
+		FAudioVoice_GetVoiceDetails(pEngine->master, &masterDetails);
+
+		/* Reverb effect chain... */
+		FAudioCreateReverb(&reverbDesc.pEffect, 0);
+		reverbDesc.InitialState = 1;
+		reverbDesc.OutputChannels = (masterDetails.InputChannels == 6) ? 6 : 1;
+		reverbChain.EffectCount = 1;
+		reverbChain.pEffectDescriptors = &reverbDesc;
+
+		/* Reverb submix voice... */
+		FAudio_CreateSubmixVoice(
+			pEngine->audio,
+			&pEngine->reverbVoice,
+			1, /* Reverb will be omnidirectional */
+			masterDetails.InputSampleRate,
+			0,
+			0,
+			NULL,
+			&reverbChain
+		);
+
+		/* We can release now, the submix owns this! */
+		FAPOBase_Release((FAPOBase*) reverbDesc.pEffect);
+	}
+
+	pEngine->initialized = 1;
+	pEngine->apiThread = FAudio_PlatformCreateThread(
+		FACT_INTERNAL_APIThread,
+		"FACT Thread",
+		pEngine
+	);
+
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTAudioEngine_ShutDown(FACTAudioEngine *pEngine)
+{
+	uint32_t i, refcount;
+	FAudioMutex mutex;
+	FAudioMallocFunc pMalloc;
+	FAudioFreeFunc pFree;
+	FAudioReallocFunc pRealloc;
+
+	/* Close thread, then lock ASAP */
+	pEngine->initialized = 0;
+	FAudio_PlatformWaitThread(pEngine->apiThread, NULL);
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+
+	/* Stop the platform stream before freeing stuff! */
+	if (pEngine->audio != NULL)
+	{
+		FAudio_StopEngine(pEngine->audio);
+	}
+
+	/* This method destroys all existing cues, sound banks, and wave banks.
+	 * It blocks until all cues are destroyed.
+	 */
+	while (pEngine->wbList != NULL)
+	{
+		FACTWaveBank_Destroy((FACTWaveBank*) pEngine->wbList->entry);
+	}
+	while (pEngine->sbList != NULL)
+	{
+		FACTSoundBank_Destroy((FACTSoundBank*) pEngine->sbList->entry);
+	}
+
+	/* Category data */
+	for (i = 0; i < pEngine->categoryCount; i += 1)
+	{
+		pEngine->pFree(pEngine->categoryNames[i]);
+	}
+	pEngine->pFree(pEngine->categoryNames);
+	pEngine->pFree(pEngine->categories);
+
+	/* Variable data */
+	for (i = 0; i < pEngine->variableCount; i += 1)
+	{
+		pEngine->pFree(pEngine->variableNames[i]);
+	}
+	pEngine->pFree(pEngine->variableNames);
+	pEngine->pFree(pEngine->variables);
+	pEngine->pFree(pEngine->globalVariableValues);
+
+	/* RPC data */
+	for (i = 0; i < pEngine->rpcCount; i += 1)
+	{
+		pEngine->pFree(pEngine->rpcs[i].points);
+	}
+	pEngine->pFree(pEngine->rpcs);
+	pEngine->pFree(pEngine->rpcCodes);
+
+	/* DSP data */
+	for (i = 0; i < pEngine->dspPresetCount; i += 1)
+	{
+		pEngine->pFree(pEngine->dspPresets[i].parameters);
+	}
+	pEngine->pFree(pEngine->dspPresets);
+	pEngine->pFree(pEngine->dspPresetCodes);
+
+	/* Audio resources */
+	if (pEngine->reverbVoice != NULL)
+	{
+		FAudioVoice_DestroyVoice(pEngine->reverbVoice);
+	}
+	if (pEngine->master != NULL)
+	{
+		FAudioVoice_DestroyVoice(pEngine->master);
+	}
+	if (pEngine->audio != NULL)
+	{
+		FAudio_Release(pEngine->audio);
+	}
+
+	/* Finally. */
+	refcount = pEngine->refcount;
+	mutex = pEngine->apiLock;
+	pMalloc = pEngine->pMalloc;
+	pFree = pEngine->pFree;
+	pRealloc = pEngine->pRealloc;
+	FAudio_zero(pEngine, sizeof(FACTAudioEngine));
+	pEngine->pMalloc = pMalloc;
+	pEngine->pFree = pFree;
+	pEngine->pRealloc = pRealloc;
+	pEngine->refcount = refcount;
+	pEngine->apiLock = mutex;
+
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTAudioEngine_DoWork(FACTAudioEngine *pEngine)
+{
+	uint8_t i;
+	FACTCue *cue;
+	LinkedList *list;
+
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+
+	list = pEngine->sbList;
+	while (list != NULL)
+	{
+		cue = ((FACTSoundBank*) list->entry)->cueList;
+		while (cue != NULL)
+		{
+			if (cue->playingSound != NULL)
+			for (i = 0; i < cue->playingSound->sound->trackCount; i += 1)
+			{
+				if (	cue->playingSound->tracks[i].upcomingWave.wave == NULL &&
+					cue->playingSound->tracks[i].waveEvtInst->loopCount > 0	)
+				{
+					FACT_INTERNAL_GetNextWave(
+						cue,
+						cue->playingSound->sound,
+						&cue->playingSound->sound->tracks[i],
+						&cue->playingSound->tracks[i],
+						cue->playingSound->tracks[i].waveEvt,
+						cue->playingSound->tracks[i].waveEvtInst
+					);
+				}
+			}
+			cue = cue->next;
+		}
+		list = list->next;
+	}
+
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTAudioEngine_CreateSoundBank(
+	FACTAudioEngine *pEngine,
+	const void *pvBuffer,
+	uint32_t dwSize,
+	uint32_t dwFlags,
+	uint32_t dwAllocAttributes,
+	FACTSoundBank **ppSoundBank
+) {
+	uint32_t retval;
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+	retval = FACT_INTERNAL_ParseSoundBank(
+		pEngine,
+		pvBuffer,
+		dwSize,
+		ppSoundBank
+	);
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return retval;
+}
+
+uint32_t FACTAudioEngine_CreateInMemoryWaveBank(
+	FACTAudioEngine *pEngine,
+	const void *pvBuffer,
+	uint32_t dwSize,
+	uint32_t dwFlags,
+	uint32_t dwAllocAttributes,
+	FACTWaveBank **ppWaveBank
+) {
+	uint32_t retval;
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+	retval = FACT_INTERNAL_ParseWaveBank(
+		pEngine,
+		FAudio_memopen((void*) pvBuffer, dwSize),
+		0,
+		0,
+		FACT_INTERNAL_DefaultReadFile,
+		FACT_INTERNAL_DefaultGetOverlappedResult,
+		0,
+		ppWaveBank
+	);
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return retval;
+}
+
+uint32_t FACTAudioEngine_CreateStreamingWaveBank(
+	FACTAudioEngine *pEngine,
+	const FACTStreamingParameters *pParms,
+	FACTWaveBank **ppWaveBank
+) {
+	uint32_t retval, packetSize;
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+	if (	pEngine->pReadFile == FACT_INTERNAL_DefaultReadFile &&
+		pEngine->pGetOverlappedResult == FACT_INTERNAL_DefaultGetOverlappedResult	)
+	{
+		/* Our I/O doesn't care about packets, set to 0 as an optimization */
+		packetSize = 0;
+	}
+	else
+	{
+		packetSize = pParms->packetSize * 2048;
+	}
+	retval = FACT_INTERNAL_ParseWaveBank(
+		pEngine,
+		pParms->file,
+		pParms->offset,
+		packetSize,
+		pEngine->pReadFile,
+		pEngine->pGetOverlappedResult,
+		1,
+		ppWaveBank
+	);
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return retval;
+}
+
+uint32_t FACTAudioEngine_PrepareWave(
+	FACTAudioEngine *pEngine,
+	uint32_t dwFlags,
+	const char *szWavePath,
+	uint32_t wStreamingPacketSize,
+	uint32_t dwAlignment,
+	uint32_t dwPlayOffset,
+	uint8_t nLoopCount,
+	FACTWave **ppWave
+) {
+	/* TODO: FACTWave */
+	return 0;
+}
+
+uint32_t FACTAudioEngine_PrepareInMemoryWave(
+	FACTAudioEngine *pEngine,
+	uint32_t dwFlags,
+	FACTWaveBankEntry entry,
+	uint32_t *pdwSeekTable, /* Optional! */
+	uint8_t *pbWaveData,
+	uint32_t dwPlayOffset,
+	uint8_t nLoopCount,
+	FACTWave **ppWave
+) {
+	/* TODO: FACTWave */
+	return 0;
+}
+
+uint32_t FACTAudioEngine_PrepareStreamingWave(
+	FACTAudioEngine *pEngine,
+	uint32_t dwFlags,
+	FACTWaveBankEntry entry,
+	FACTStreamingParameters streamingParams,
+	uint32_t dwAlignment,
+	uint32_t *pdwSeekTable, /* Optional! */
+	uint8_t *pbWaveData, /* ABI bug, do not use! */
+	uint32_t dwPlayOffset,
+	uint8_t nLoopCount,
+	FACTWave **ppWave
+) {
+	/* TODO: FACTWave */
+	return 0;
+}
+
+uint32_t FACTAudioEngine_RegisterNotification(
+	FACTAudioEngine *pEngine,
+	const FACTNotificationDescription *pNotificationDescription
+) {
+	FAudio_assert(pEngine != NULL);
+	FAudio_assert(pNotificationDescription != NULL);
+	FAudio_assert(pEngine->notificationCallback != NULL);
+
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+
+	#define HANDLE_PERSIST(nt) \
+		if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_##nt) \
+		{ \
+			if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST) \
+			{ \
+				pEngine->notifications |= NOTIFY_##nt; \
+				PERSIST_ACTION \
+			} \
+			else \
+			{ \
+				FAudio_assert(0 && "TODO: "#nt" notification!"); \
+			} \
+		}
+
+	/* Cues */
+	#define PERSIST_ACTION pEngine->cue_context = pNotificationDescription->pvContext;
+	HANDLE_PERSIST(CUEPREPARED)
+	else HANDLE_PERSIST(CUEPLAY)
+	else HANDLE_PERSIST(CUESTOP)
+	else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_CUEDESTROYED)
+	{
+		if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
+		{
+			pEngine->notifications |= NOTIFY_CUEDESTROY;
+			pEngine->cue_context = pNotificationDescription->pvContext;
+		}
+		else
+		{
+			pNotificationDescription->pCue->notifyOnDestroy = 1;
+			pNotificationDescription->pCue->usercontext = pNotificationDescription->pvContext;
+		}
+	}
+	#undef PERSIST_ACTION
+
+	/* Markers */
+	#define PERSIST_ACTION
+	else HANDLE_PERSIST(MARKER)
+	#undef PERSIST_ACTION
+
+	/* SoundBank/WaveBank Destruction */
+	else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED)
+	{
+		if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
+		{
+			pEngine->notifications |= NOTIFY_SOUNDBANKDESTROY;
+			pEngine->sb_context = pNotificationDescription->pvContext;
+		}
+		else
+		{
+			pNotificationDescription->pSoundBank->notifyOnDestroy = 1;
+			pNotificationDescription->pSoundBank->usercontext = pNotificationDescription->pvContext;
+		}
+	}
+	else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED)
+	{
+		if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
+		{
+			pEngine->notifications |= NOTIFY_WAVEBANKDESTROY;
+			pEngine->wb_context = pNotificationDescription->pvContext;
+		}
+		else
+		{
+			pNotificationDescription->pWaveBank->notifyOnDestroy = 1;
+			pNotificationDescription->pWaveBank->usercontext = pNotificationDescription->pvContext;
+		}
+	}
+
+	/* Variables, Auditioning Tool */
+	#define PERSIST_ACTION
+	else HANDLE_PERSIST(LOCALVARIABLECHANGED)
+	else HANDLE_PERSIST(GLOBALVARIABLECHANGED)
+	else HANDLE_PERSIST(GUICONNECTED)
+	else HANDLE_PERSIST(GUIDISCONNECTED)
+	#undef PERSIST_ACTION
+
+	/* Waves */
+	#define PERSIST_ACTION pEngine->wave_context = pNotificationDescription->pvContext;
+	else HANDLE_PERSIST(WAVEPREPARED)
+	else HANDLE_PERSIST(WAVEPLAY)
+	else HANDLE_PERSIST(WAVESTOP)
+	else HANDLE_PERSIST(WAVELOOPED)
+	else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_WAVEDESTROYED)
+	{
+		if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
+		{
+			pEngine->notifications |= NOTIFY_WAVEDESTROY;
+			pEngine->wave_context = pNotificationDescription->pvContext;
+		}
+		else
+		{
+			pNotificationDescription->pWave->notifyOnDestroy = 1;
+			pNotificationDescription->pWave->usercontext = pNotificationDescription->pvContext;
+		}
+	}
+	#undef PERSIST_ACTION
+
+	/* WaveBanks */
+	#define PERSIST_ACTION pEngine->wb_context = pNotificationDescription->pvContext;
+	else HANDLE_PERSIST(WAVEBANKPREPARED)
+	#undef PERSIST_ACTION
+
+	/* Anything else? */
+	else
+	{
+		FAudio_assert(0 && "TODO: Unimplemented notification!");
+	}
+
+	#undef HANDLE_PERSIST
+
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTAudioEngine_UnRegisterNotification(
+	FACTAudioEngine *pEngine,
+	const FACTNotificationDescription *pNotificationDescription
+) {
+	FAudio_assert(pEngine != NULL);
+	FAudio_assert(pNotificationDescription != NULL);
+	FAudio_assert(pEngine->notificationCallback != NULL);
+
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+
+	#define HANDLE_PERSIST(nt) \
+		if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_##nt) \
+		{ \
+			if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST) \
+			{ \
+				pEngine->notifications &= ~NOTIFY_##nt; \
+				PERSIST_ACTION \
+			} \
+			else \
+			{ \
+				FAudio_assert(0 && "TODO: "#nt" notification!"); \
+			} \
+		}
+
+	/* Cues */
+	#define PERSIST_ACTION pEngine->cue_context = pNotificationDescription->pvContext;
+	HANDLE_PERSIST(CUEPREPARED)
+	else HANDLE_PERSIST(CUEPLAY)
+	else HANDLE_PERSIST(CUESTOP)
+	else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_CUEDESTROYED)
+	{
+		if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
+		{
+			pEngine->notifications &= ~NOTIFY_CUEDESTROY;
+			pEngine->cue_context = pNotificationDescription->pvContext;
+		}
+		else
+		{
+			pNotificationDescription->pCue->notifyOnDestroy = 0;
+			pNotificationDescription->pCue->usercontext = pNotificationDescription->pvContext;
+		}
+	}
+	#undef PERSIST_ACTION
+
+	/* Markers */
+	#define PERSIST_ACTION
+	else HANDLE_PERSIST(MARKER)
+	#undef PERSIST_ACTION
+
+	/* SoundBank/WaveBank Destruction */
+	else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED)
+	{
+		if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
+		{
+			pEngine->notifications &= ~NOTIFY_SOUNDBANKDESTROY;
+			pEngine->sb_context = pNotificationDescription->pvContext;
+		}
+		else
+		{
+			pNotificationDescription->pSoundBank->notifyOnDestroy = 0;
+			pNotificationDescription->pSoundBank->usercontext = pNotificationDescription->pvContext;
+		}
+	}
+	else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED)
+	{
+		if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
+		{
+			pEngine->notifications &= ~NOTIFY_WAVEBANKDESTROY;
+			pEngine->wb_context = pNotificationDescription->pvContext;
+		}
+		else
+		{
+			pNotificationDescription->pWaveBank->notifyOnDestroy = 0;
+			pNotificationDescription->pWaveBank->usercontext = pNotificationDescription->pvContext;
+		}
+	}
+
+	/* Variables, Auditioning Tool */
+	#define PERSIST_ACTION
+	else HANDLE_PERSIST(LOCALVARIABLECHANGED)
+	else HANDLE_PERSIST(GLOBALVARIABLECHANGED)
+	else HANDLE_PERSIST(GUICONNECTED)
+	else HANDLE_PERSIST(GUIDISCONNECTED)
+	#undef PERSIST_ACTION
+
+	/* Waves */
+	#define PERSIST_ACTION pEngine->wave_context = pNotificationDescription->pvContext;
+	else HANDLE_PERSIST(WAVEPREPARED)
+	else HANDLE_PERSIST(WAVEPLAY)
+	else HANDLE_PERSIST(WAVESTOP)
+	else HANDLE_PERSIST(WAVELOOPED)
+	else if (pNotificationDescription->type == FACTNOTIFICATIONTYPE_WAVEDESTROYED)
+	{
+		if (pNotificationDescription->flags & FACT_FLAG_NOTIFICATION_PERSIST)
+		{
+			pEngine->notifications &= ~NOTIFY_WAVEDESTROY;
+			pEngine->wave_context = pNotificationDescription->pvContext;
+		}
+		else
+		{
+			pNotificationDescription->pWave->notifyOnDestroy = 0;
+			pNotificationDescription->pWave->usercontext = pNotificationDescription->pvContext;
+		}
+	}
+	#undef PERSIST_ACTION
+
+	/* WaveBanks */
+	#define PERSIST_ACTION pEngine->wb_context = pNotificationDescription->pvContext;
+	else HANDLE_PERSIST(WAVEBANKPREPARED)
+	#undef PERSIST_ACTION
+
+	/* Anything else? */
+	else
+	{
+		FAudio_assert(0 && "TODO: Unimplemented notification!");
+	}
+
+	#undef HANDLE_PERSIST
+
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return 0;
+}
+
+uint16_t FACTAudioEngine_GetCategory(
+	FACTAudioEngine *pEngine,
+	const char *szFriendlyName
+) {
+	uint16_t i;
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+	for (i = 0; i < pEngine->categoryCount; i += 1)
+	{
+		if (FAudio_strcmp(szFriendlyName, pEngine->categoryNames[i]) == 0)
+		{
+			FAudio_PlatformUnlockMutex(pEngine->apiLock);
+			return i;
+		}
+	}
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return FACTCATEGORY_INVALID;
+}
+
+uint8_t FACT_INTERNAL_IsInCategory(
+	FACTAudioEngine *engine,
+	uint16_t target,
+	uint16_t category
+) {
+	FACTAudioCategory *cat;
+
+	/* Same category, no need to go on a crazy hunt */
+	if (category == target)
+	{
+		return 1;
+	}
+
+	/* Right, on with the crazy hunt */
+	cat = &engine->categories[category];
+	while (cat->parentCategory != -1)
+	{
+		if (cat->parentCategory == target)
+		{
+			return 1;
+		}
+		cat = &engine->categories[cat->parentCategory];
+	}
+	return 0;
+}
+
+uint32_t FACTAudioEngine_Stop(
+	FACTAudioEngine *pEngine,
+	uint16_t nCategory,
+	uint32_t dwFlags
+) {
+	FACTCue *cue, *backup;
+	LinkedList *list;
+
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+	list = pEngine->sbList;
+	while (list != NULL)
+	{
+		cue = ((FACTSoundBank*) list->entry)->cueList;
+		while (cue != NULL)
+		{
+			if (	cue->playingSound != NULL &&
+				FACT_INTERNAL_IsInCategory(
+					pEngine,
+					nCategory,
+					cue->playingSound->sound->category
+				)	)
+			{
+				if (	dwFlags == FACT_FLAG_STOP_IMMEDIATE &&
+					cue->managed	)
+				{
+					/* Just blow this up now */
+					backup = cue->next;
+					FACTCue_Destroy(cue);
+					cue = backup;
+				}
+				else
+				{
+					/* If managed, the mixer will destroy for us */
+					FACTCue_Stop(cue, dwFlags);
+					cue = cue->next;
+				}
+			}
+			else
+			{
+				cue = cue->next;
+			}
+		}
+		list = list->next;
+	}
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTAudioEngine_SetVolume(
+	FACTAudioEngine *pEngine,
+	uint16_t nCategory,
+	float volume
+) {
+	uint16_t i;
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+	pEngine->categories[nCategory].currentVolume = (
+		pEngine->categories[nCategory].volume *
+		volume
+	);
+	for (i = 0; i < pEngine->categoryCount; i += 1)
+	{
+		if (pEngine->categories[i].parentCategory == nCategory)
+		{
+			FACTAudioEngine_SetVolume(
+				pEngine,
+				i,
+				pEngine->categories[i].currentVolume
+			);
+		}
+	}
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTAudioEngine_Pause(
+	FACTAudioEngine *pEngine,
+	uint16_t nCategory,
+	int32_t fPause
+) {
+	FACTCue *cue;
+	LinkedList *list;
+
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+	list = pEngine->sbList;
+	while (list != NULL)
+	{
+		cue = ((FACTSoundBank*) list->entry)->cueList;
+		while (cue != NULL)
+		{
+			if (	cue->playingSound != NULL &&
+				FACT_INTERNAL_IsInCategory(
+					pEngine,
+					nCategory,
+					cue->playingSound->sound->category
+				)	)
+			{
+				FACTCue_Pause(cue, fPause);
+			}
+			cue = cue->next;
+		}
+		list = list->next;
+	}
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return 0;
+}
+
+uint16_t FACTAudioEngine_GetGlobalVariableIndex(
+	FACTAudioEngine *pEngine,
+	const char *szFriendlyName
+) {
+	uint16_t i;
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+	for (i = 0; i < pEngine->variableCount; i += 1)
+	{
+		if (	FAudio_strcmp(szFriendlyName, pEngine->variableNames[i]) == 0 &&
+			!(pEngine->variables[i].accessibility & 0x04)	)
+		{
+			FAudio_PlatformUnlockMutex(pEngine->apiLock);
+			return i;
+		}
+	}
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return FACTVARIABLEINDEX_INVALID;
+}
+
+uint32_t FACTAudioEngine_SetGlobalVariable(
+	FACTAudioEngine *pEngine,
+	uint16_t nIndex,
+	float nValue
+) {
+	FACTVariable *var;
+
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+
+	var = &pEngine->variables[nIndex];
+	FAudio_assert(var->accessibility & 0x01);
+	FAudio_assert(!(var->accessibility & 0x02));
+	FAudio_assert(!(var->accessibility & 0x04));
+	pEngine->globalVariableValues[nIndex] = FAudio_clamp(
+		nValue,
+		var->minValue,
+		var->maxValue
+	);
+
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTAudioEngine_GetGlobalVariable(
+	FACTAudioEngine *pEngine,
+	uint16_t nIndex,
+	float *pnValue
+) {
+	FACTVariable *var;
+
+	FAudio_PlatformLockMutex(pEngine->apiLock);
+
+	var = &pEngine->variables[nIndex];
+	FAudio_assert(var->accessibility & 0x01);
+	FAudio_assert(!(var->accessibility & 0x04));
+	*pnValue = pEngine->globalVariableValues[nIndex];
+
+	FAudio_PlatformUnlockMutex(pEngine->apiLock);
+	return 0;
+}
+
+/* SoundBank implementation */
+
+uint16_t FACTSoundBank_GetCueIndex(
+	FACTSoundBank *pSoundBank,
+	const char *szFriendlyName
+) {
+	uint16_t i;
+	if (pSoundBank == NULL)
+	{
+		return FACTINDEX_INVALID;
+	}
+
+	FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
+	if (pSoundBank->cueNames != NULL)
+	for (i = 0; i < pSoundBank->cueCount; i += 1)
+	{
+		if (FAudio_strcmp(szFriendlyName, pSoundBank->cueNames[i]) == 0)
+		{
+			FAudio_PlatformUnlockMutex(
+				pSoundBank->parentEngine->apiLock
+			);
+			return i;
+		}
+	}
+	FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
+	return FACTINDEX_INVALID;
+}
+
+uint32_t FACTSoundBank_GetNumCues(
+	FACTSoundBank *pSoundBank,
+	uint16_t *pnNumCues
+) {
+	if (pSoundBank == NULL)
+	{
+		*pnNumCues = 0;
+		return 0;
+	}
+
+	FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
+	*pnNumCues = pSoundBank->cueCount;
+	FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTSoundBank_GetCueProperties(
+	FACTSoundBank *pSoundBank,
+	uint16_t nCueIndex,
+	FACTCueProperties *pProperties
+) {
+	uint16_t i;
+	if (pSoundBank == NULL)
+	{
+		return 1;
+	}
+
+	FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
+
+	if (pSoundBank->cueNames == NULL)
+	{
+		FAudio_zero(pProperties->friendlyName, 0xFF);
+	}
+	else
+	{
+		FAudio_strlcpy(
+			pProperties->friendlyName,
+			pSoundBank->cueNames[nCueIndex],
+			0xFF
+		);
+	}
+	if (!(pSoundBank->cues[nCueIndex].flags & 0x04))
+	{
+		for (i = 0; i < pSoundBank->variationCount; i += 1)
+		{
+			if (pSoundBank->variationCodes[i] == pSoundBank->cues[nCueIndex].sbCode)
+			{
+				break;
+			}
+		}
+
+		FAudio_assert(i < pSoundBank->variationCount && "Variation table not found!");
+
+		if (pSoundBank->variations[i].flags == 3)
+		{
+			pProperties->interactive = 1;
+			pProperties->iaVariableIndex = pSoundBank->variations[i].variable;
+		}
+		else
+		{
+			pProperties->interactive = 0;
+			pProperties->iaVariableIndex = 0;
+		}
+		pProperties->numVariations = pSoundBank->variations[i].entryCount;
+	}
+	else
+	{
+		pProperties->interactive = 0;
+		pProperties->iaVariableIndex = 0;
+		pProperties->numVariations = 0;
+	}
+	pProperties->maxInstances = pSoundBank->cues[nCueIndex].instanceLimit;
+	pProperties->currentInstances = pSoundBank->cues[nCueIndex].instanceCount;
+
+	FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTSoundBank_Prepare(
+	FACTSoundBank *pSoundBank,
+	uint16_t nCueIndex,
+	uint32_t dwFlags,
+	int32_t timeOffset,
+	FACTCue** ppCue
+) {
+	uint16_t i;
+	FACTCue *latest;
+
+	if (pSoundBank == NULL)
+	{
+		*ppCue = NULL;
+		return 1;
+	}
+
+	*ppCue = (FACTCue*) pSoundBank->parentEngine->pMalloc(sizeof(FACTCue));
+	FAudio_zero(*ppCue, sizeof(FACTCue));
+
+	FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
+
+	/* Engine references */
+	(*ppCue)->parentBank = pSoundBank;
+	(*ppCue)->next = NULL;
+	(*ppCue)->managed = 0;
+	(*ppCue)->index = nCueIndex;
+	(*ppCue)->notifyOnDestroy = 0;
+	(*ppCue)->usercontext = NULL;
+
+	/* Sound data */
+	(*ppCue)->data = &pSoundBank->cues[nCueIndex];
+	if ((*ppCue)->data->flags & 0x04)
+	{
+		for (i = 0; i < pSoundBank->soundCount; i += 1)
+		{
+			if ((*ppCue)->data->sbCode == pSoundBank->soundCodes[i])
+			{
+				(*ppCue)->sound = &pSoundBank->sounds[i];
+				break;
+			}
+		}
+	}
+	else
+	{
+		for (i = 0; i < pSoundBank->variationCount; i += 1)
+		{
+			if ((*ppCue)->data->sbCode == pSoundBank->variationCodes[i])
+			{
+				(*ppCue)->variation = &pSoundBank->variations[i];
+				break;
+			}
+		}
+		if ((*ppCue)->variation->flags == 3)
+		{
+			(*ppCue)->interactive = pSoundBank->parentEngine->variables[
+				(*ppCue)->variation->variable
+			].initialValue;
+		}
+	}
+
+	/* Instance data */
+	(*ppCue)->variableValues = (float*) pSoundBank->parentEngine->pMalloc(
+		sizeof(float) * pSoundBank->parentEngine->variableCount
+	);
+	for (i = 0; i < pSoundBank->parentEngine->variableCount; i += 1)
+	{
+		(*ppCue)->variableValues[i] =
+			pSoundBank->parentEngine->variables[i].initialValue;
+	}
+
+	/* Playback */
+	(*ppCue)->state = FACT_STATE_PREPARED;
+
+	/* Add to the SoundBank Cue list */
+	if (pSoundBank->cueList == NULL)
+	{
+		pSoundBank->cueList = *ppCue;
+	}
+	else
+	{
+		latest = pSoundBank->cueList;
+		while (latest->next != NULL)
+		{
+			latest = latest->next;
+		}
+		latest->next = *ppCue;
+	}
+
+	FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTSoundBank_Play(
+	FACTSoundBank *pSoundBank,
+	uint16_t nCueIndex,
+	uint32_t dwFlags,
+	int32_t timeOffset,
+	FACTCue** ppCue /* Optional! */
+) {
+	FACTCue *result;
+	if (pSoundBank == NULL)
+	{
+		if (ppCue != NULL)
+		{
+			*ppCue = NULL;
+		}
+		return 1;
+	}
+
+	FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
+
+	FACTSoundBank_Prepare(
+		pSoundBank,
+		nCueIndex,
+		dwFlags,
+		timeOffset,
+		&result
+	);
+	if (ppCue != NULL)
+	{
+		*ppCue = result;
+	}
+	else
+	{
+		/* AKA we get to Destroy() this ourselves */
+		result->managed = 1;
+	}
+	FACTCue_Play(result);
+
+	FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTSoundBank_Play3D(
+	FACTSoundBank *pSoundBank,
+	uint16_t nCueIndex,
+	uint32_t dwFlags,
+	int32_t timeOffset,
+	F3DAUDIO_DSP_SETTINGS *pDSPSettings,
+	FACTCue** ppCue /* Optional! */
+) {
+	FACTCue *result;
+	if (pSoundBank == NULL)
+	{
+		if (ppCue != NULL)
+		{
+			*ppCue = NULL;
+		}
+		return 1;
+	}
+
+	FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
+
+	FACTSoundBank_Prepare(
+		pSoundBank,
+		nCueIndex,
+		dwFlags,
+		timeOffset,
+		&result
+	);
+	if (ppCue != NULL)
+	{
+		*ppCue = result;
+	}
+	else
+	{
+		/* AKA we get to Destroy() this ourselves */
+		result->managed = 1;
+	}
+	FACT3DApply(pDSPSettings, result);
+	FACTCue_Play(result);
+
+	FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTSoundBank_Stop(
+	FACTSoundBank *pSoundBank,
+	uint16_t nCueIndex,
+	uint32_t dwFlags
+) {
+	FACTCue *backup, *cue;
+	if (pSoundBank == NULL)
+	{
+		return 1;
+	}
+
+	FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
+	cue = pSoundBank->cueList;
+	while (cue != NULL)
+	{
+		if (cue->index == nCueIndex)
+		{
+			if (	dwFlags == FACT_FLAG_STOP_IMMEDIATE &&
+				cue->managed	)
+			{
+				/* Just blow this up now */
+				backup = cue->next;
+				FACTCue_Destroy(cue);
+				cue = backup;
+			}
+			else
+			{
+				/* If managed, the mixer will destroy for us */
+				FACTCue_Stop(cue, dwFlags);
+				cue = cue->next;
+			}
+		}
+		else
+		{
+			cue = cue->next;
+		}
+	}
+	FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTSoundBank_Destroy(FACTSoundBank *pSoundBank)
+{
+	uint16_t i, j, k;
+	FAudioMutex mutex;
+	FACTNotification note;
+	if (pSoundBank == NULL)
+	{
+		return 1;
+	}
+
+	FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
+
+	/* Synchronously destroys all cues that are associated */
+	while (pSoundBank->cueList != NULL)
+	{
+		FACTCue_Destroy(pSoundBank->cueList);
+	}
+
+	if (pSoundBank->parentEngine != NULL)
+	{
+		/* Remove this SoundBank from the Engine list */
+		LinkedList_RemoveEntry(
+			&pSoundBank->parentEngine->sbList,
+			pSoundBank,
+			pSoundBank->parentEngine->sbLock,
+			pSoundBank->parentEngine->pFree
+		);
+	}
+
+	/* SoundBank Name */
+	pSoundBank->parentEngine->pFree(pSoundBank->name);
+
+	/* Cue data */
+	pSoundBank->parentEngine->pFree(pSoundBank->cues);
+
+	/* WaveBank Name data */
+	for (i = 0; i < pSoundBank->wavebankCount; i += 1)
+	{
+		pSoundBank->parentEngine->pFree(pSoundBank->wavebankNames[i]);
+	}
+	pSoundBank->parentEngine->pFree(pSoundBank->wavebankNames);
+
+	/* Sound data */
+	for (i = 0; i < pSoundBank->soundCount; i += 1)
+	{
+		for (j = 0; j < pSoundBank->sounds[i].trackCount; j += 1)
+		{
+			for (k = 0; k < pSoundBank->sounds[i].tracks[j].eventCount; k += 1)
+			{
+				#define MATCH(t) \
+					pSoundBank->sounds[i].tracks[j].events[k].type == t
+				if (	MATCH(FACTEVENT_PLAYWAVE) ||
+					MATCH(FACTEVENT_PLAYWAVETRACKVARIATION) ||
+					MATCH(FACTEVENT_PLAYWAVEEFFECTVARIATION) ||
+					MATCH(FACTEVENT_PLAYWAVETRACKEFFECTVARIATION)	)
+				{
+					if (pSoundBank->sounds[i].tracks[j].events[k].wave.isComplex)
+					{
+						pSoundBank->parentEngine->pFree(
+							pSoundBank->sounds[i].tracks[j].events[k].wave.complex.tracks
+						);
+						pSoundBank->parentEngine->pFree(
+							pSoundBank->sounds[i].tracks[j].events[k].wave.complex.wavebanks
+						);
+						pSoundBank->parentEngine->pFree(
+							pSoundBank->sounds[i].tracks[j].events[k].wave.complex.weights
+						);
+					}
+				}
+				#undef MATCH
+			}
+			pSoundBank->parentEngine->pFree(
+				pSoundBank->sounds[i].tracks[j].events
+			);
+		}
+		pSoundBank->parentEngine->pFree(pSoundBank->sounds[i].tracks);
+		pSoundBank->parentEngine->pFree(pSoundBank->sounds[i].rpcCodes);
+		pSoundBank->parentEngine->pFree(pSoundBank->sounds[i].dspCodes);
+	}
+	pSoundBank->parentEngine->pFree(pSoundBank->sounds);
+	pSoundBank->parentEngine->pFree(pSoundBank->soundCodes);
+
+	/* Variation data */
+	for (i = 0; i < pSoundBank->variationCount; i += 1)
+	{
+		pSoundBank->parentEngine->pFree(
+			pSoundBank->variations[i].entries
+		);
+	}
+	pSoundBank->parentEngine->pFree(pSoundBank->variations);
+	pSoundBank->parentEngine->pFree(pSoundBank->variationCodes);
+
+	/* Transition data */
+	for (i = 0; i < pSoundBank->transitionCount; i += 1)
+	{
+		pSoundBank->parentEngine->pFree(
+			pSoundBank->transitions[i].entries
+		);
+	}
+	pSoundBank->parentEngine->pFree(pSoundBank->transitions);
+	pSoundBank->parentEngine->pFree(pSoundBank->transitionCodes);
+
+	/* Cue Name data */
+	if (pSoundBank->cueNames != NULL)
+	{
+		for (i = 0; i < pSoundBank->cueCount; i += 1)
+		{
+			pSoundBank->parentEngine->pFree(pSoundBank->cueNames[i]);
+		}
+		pSoundBank->parentEngine->pFree(pSoundBank->cueNames);
+	}
+
+	/* Finally. */
+	if (pSoundBank->notifyOnDestroy || pSoundBank->parentEngine->notifications & NOTIFY_SOUNDBANKDESTROY)
+	{
+		note.type = FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED;
+		note.soundBank.pSoundBank = pSoundBank;
+		if (pSoundBank->parentEngine->notifications & NOTIFY_SOUNDBANKDESTROY)
+		{
+			note.pvContext = pSoundBank->parentEngine->sb_context;
+		}
+		else
+		{
+			note.pvContext = pSoundBank->usercontext;
+		}
+		pSoundBank->parentEngine->notificationCallback(&note);
+	}
+
+	mutex = pSoundBank->parentEngine->apiLock;
+	pSoundBank->parentEngine->pFree(pSoundBank);
+	FAudio_PlatformUnlockMutex(mutex);
+	return 0;
+}
+
+uint32_t FACTSoundBank_GetState(
+	FACTSoundBank *pSoundBank,
+	uint32_t *pdwState
+) {
+	uint16_t i;
+	if (pSoundBank == NULL)
+	{
+		*pdwState = 0;
+		return 1;
+	}
+
+	FAudio_PlatformLockMutex(pSoundBank->parentEngine->apiLock);
+
+	*pdwState = FACT_STATE_PREPARED;
+	for (i = 0; i < pSoundBank->cueCount; i += 1)
+	{
+		if (pSoundBank->cues[i].instanceCount > 0)
+		{
+			*pdwState |= FACT_STATE_INUSE;
+			FAudio_PlatformUnlockMutex(
+				pSoundBank->parentEngine->apiLock
+			);
+			return 0;
+		}
+	}
+
+	FAudio_PlatformUnlockMutex(pSoundBank->parentEngine->apiLock);
+	return 0;
+}
+
+/* WaveBank implementation */
+
+uint32_t FACTWaveBank_Destroy(FACTWaveBank *pWaveBank)
+{
+	uint32_t i;
+	FACTWave *wave;
+	FAudioMutex mutex;
+	FACTNotification note;
+	if (pWaveBank == NULL)
+	{
+		return 1;
+	}
+
+	FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
+
+	/* Synchronously destroys any cues that are using the wavebank */
+	while (pWaveBank->waveList != NULL)
+	{
+		wave = (FACTWave*) pWaveBank->waveList->entry;
+		if (wave->parentCue != NULL)
+		{
+			/* Destroying this Cue destroys the Wave */
+			FACTCue_Destroy(wave->parentCue);
+		}
+		else
+		{
+			FACTWave_Destroy(wave);
+		}
+	}
+
+	if (pWaveBank->parentEngine != NULL)
+	{
+		/* Remove this WaveBank from the Engine list */
+		LinkedList_RemoveEntry(
+			&pWaveBank->parentEngine->wbList,
+			pWaveBank,
+			pWaveBank->parentEngine->wbLock,
+			pWaveBank->parentEngine->pFree
+		);
+	}
+
+	/* Free everything, finally. */
+	pWaveBank->parentEngine->pFree(pWaveBank->name);
+	pWaveBank->parentEngine->pFree(pWaveBank->entries);
+	pWaveBank->parentEngine->pFree(pWaveBank->entryRefs);
+	if (pWaveBank->seekTables != NULL)
+	{
+		for (i = 0; i < pWaveBank->entryCount; i += 1)
+		{
+			if (pWaveBank->seekTables[i].entries != NULL)
+			{
+				pWaveBank->parentEngine->pFree(
+					pWaveBank->seekTables[i].entries
+				);
+			}
+		}
+		pWaveBank->parentEngine->pFree(pWaveBank->seekTables);
+	}
+
+	if (!pWaveBank->streaming)
+	{
+		FAudio_close(pWaveBank->io);
+	}
+
+	if (pWaveBank->packetBuffer != NULL)
+	{
+		pWaveBank->parentEngine->pFree(pWaveBank->packetBuffer);
+	}
+	if (pWaveBank->notifyOnDestroy || pWaveBank->parentEngine->notifications & NOTIFY_WAVEBANKDESTROY)
+	{
+		note.type = FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED;
+		note.waveBank.pWaveBank = pWaveBank;
+		if (pWaveBank->parentEngine->notifications & NOTIFY_WAVEBANKDESTROY)
+		{
+			note.pvContext = pWaveBank->parentEngine->wb_context;
+		}
+		else
+		{
+			note.pvContext = pWaveBank->usercontext;
+		}
+		pWaveBank->parentEngine->notificationCallback(&note);
+	}
+	FAudio_PlatformDestroyMutex(pWaveBank->waveLock);
+
+	if (pWaveBank->waveBankNames != NULL)
+	{
+		pWaveBank->parentEngine->pFree(pWaveBank->waveBankNames);
+	}
+
+	mutex = pWaveBank->parentEngine->apiLock;
+	pWaveBank->parentEngine->pFree(pWaveBank);
+	FAudio_PlatformUnlockMutex(mutex);
+	return 0;
+}
+
+uint32_t FACTWaveBank_GetState(
+	FACTWaveBank *pWaveBank,
+	uint32_t *pdwState
+) {
+	uint32_t i;
+	if (pWaveBank == NULL)
+	{
+		*pdwState = 0;
+		return 1;
+	}
+
+	FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
+
+	*pdwState = FACT_STATE_PREPARED;
+	for (i = 0; i < pWaveBank->entryCount; i += 1)
+	{
+		if (pWaveBank->entryRefs[i] > 0)
+		{
+			*pdwState |= FACT_STATE_INUSE;
+			FAudio_PlatformUnlockMutex(
+				pWaveBank->parentEngine->apiLock
+			);
+			return 0;
+		}
+	}
+
+	FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTWaveBank_GetNumWaves(
+	FACTWaveBank *pWaveBank,
+	uint16_t *pnNumWaves
+) {
+	if (pWaveBank == NULL)
+	{
+		*pnNumWaves = 0;
+		return 1;
+	}
+	FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
+	*pnNumWaves = pWaveBank->entryCount;
+	FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint16_t FACTWaveBank_GetWaveIndex(
+	FACTWaveBank *pWaveBank,
+	const char *szFriendlyName
+) {
+	uint16_t i;
+	char *curName;
+	if (pWaveBank == NULL || pWaveBank->waveBankNames == NULL)
+	{
+		return FACTINDEX_INVALID;
+	}
+
+	FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
+	curName = pWaveBank->waveBankNames;
+	for (i = 0; i < pWaveBank->entryCount; i += 1, curName += 64)
+	{
+		if (FAudio_strncmp(szFriendlyName, curName, 64) == 0)
+		{
+			FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
+			return i;
+		}
+	}
+	FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
+
+	return FACTINDEX_INVALID;
+}
+
+uint32_t FACTWaveBank_GetWaveProperties(
+	FACTWaveBank *pWaveBank,
+	uint16_t nWaveIndex,
+	FACTWaveProperties *pWaveProperties
+) {
+	FACTWaveBankEntry *entry;
+	if (pWaveBank == NULL)
+	{
+		return 1;
+	}
+
+	FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
+
+	entry = &pWaveBank->entries[nWaveIndex];
+
+	if (pWaveBank->waveBankNames)
+	{
+		FAudio_memcpy(
+			pWaveProperties->friendlyName,
+			&pWaveBank->waveBankNames[nWaveIndex * 64],
+			sizeof(pWaveProperties->friendlyName)
+		);
+	}
+	else
+	{
+		FAudio_zero(
+			pWaveProperties->friendlyName,
+			sizeof(pWaveProperties->friendlyName)
+		);
+	}
+
+	pWaveProperties->format = entry->Format;
+	pWaveProperties->durationInSamples = entry->PlayRegion.dwLength;
+	if (entry->Format.wFormatTag == 0)
+	{
+		pWaveProperties->durationInSamples /= (8 << entry->Format.wBitsPerSample) / 8;
+		pWaveProperties->durationInSamples /= entry->Format.nChannels;
+	}
+	else if (entry->Format.wFormatTag == FAUDIO_FORMAT_MSADPCM)
+	{
+		pWaveProperties->durationInSamples = (
+			pWaveProperties->durationInSamples /
+			((entry->Format.wBlockAlign + 22) * entry->Format.nChannels) *
+			((entry->Format.wBlockAlign + 16) * 2)
+		);
+	}
+	else
+	{
+		FAudio_assert(0 && "Unrecognized wFormatTag!");
+	}
+
+	pWaveProperties->loopRegion = entry->LoopRegion;
+	pWaveProperties->streaming = pWaveBank->streaming;
+
+	FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTWaveBank_Prepare(
+	FACTWaveBank *pWaveBank,
+	uint16_t nWaveIndex,
+	uint32_t dwFlags,
+	uint32_t dwPlayOffset,
+	uint8_t nLoopCount,
+	FACTWave **ppWave
+) {
+	FAudioBuffer buffer;
+	FAudioBufferWMA bufferWMA;
+	FAudioVoiceSends sends;
+	FAudioSendDescriptor send;
+	union
+	{
+		FAudioWaveFormatEx pcm;
+		FAudioADPCMWaveFormat adpcm;
+		FAudioXMA2WaveFormat xma2;
+	} format;
+	FACTWaveBankEntry *entry;
+	FACTSeekTable *seek;
+	if (pWaveBank == NULL)
+	{
+		*ppWave = NULL;
+		return 1;
+	}
+
+	*ppWave = (FACTWave*) pWaveBank->parentEngine->pMalloc(sizeof(FACTWave));
+
+	FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
+
+	entry = &pWaveBank->entries[nWaveIndex];
+
+	/* Engine references */
+	(*ppWave)->parentBank = pWaveBank;
+	(*ppWave)->parentCue = NULL;
+	(*ppWave)->index = nWaveIndex;
+	(*ppWave)->notifyOnDestroy = 0;
+	(*ppWave)->usercontext = NULL;
+
+	/* Playback */
+	(*ppWave)->state = FACT_STATE_PREPARED;
+	(*ppWave)->volume = 1.0f;
+	(*ppWave)->pitch = 0;
+	(*ppWave)->loopCount = nLoopCount;
+
+	/* TODO: Convert dwPlayOffset to a byte offset */
+	FAudio_assert(dwPlayOffset == 0);
+#if 0
+	if (dwFlags & FACT_FLAG_UNITS_MS)
+	{
+		dwPlayOffset = (uint32_t) (
+			( /* Samples per millisecond... */
+				(float) entry->Format.nSamplesPerSec /
+				1000.0f
+			) * (float) dwPlayOffset
+		);
+	}
+#endif
+
+	/* Create the voice */
+	send.Flags = 0;
+	send.pOutputVoice = pWaveBank->parentEngine->master;
+	sends.SendCount = 1;
+	sends.pSends = &send;
+	format.pcm.nChannels = entry->Format.nChannels;
+	format.pcm.nSamplesPerSec = entry->Format.nSamplesPerSec;
+	if (entry->Format.wFormatTag == 0x0)
+	{
+		format.pcm.wFormatTag = FAUDIO_FORMAT_PCM;
+		format.pcm.wBitsPerSample = 8 << entry->Format.wBitsPerSample;
+		format.pcm.nBlockAlign = format.pcm.nChannels * format.pcm.wBitsPerSample / 8;
+		format.pcm.nAvgBytesPerSec = format.pcm.nBlockAlign * format.pcm.nSamplesPerSec;
+		format.pcm.cbSize = 0;
+	}
+	else if (entry->Format.wFormatTag == 0x1)
+	{
+		/* XMA2 is quite similar to WMA Pro... is what everyone thought.
+		 * What a great way to start this comment.
+		 *
+		 * Let's reconstruct the extra data because who knows what decoder we're dealing with in <present year>.
+		 * It's also a good exercise in understanding XMA2 metadata and feeding blocks into the decoder properly.
+		 * At the time of writing this patch, it's FFmpeg via gstreamer which doesn't even respect most of this.
+		 * ... which means: good luck to whoever ends up finding inaccuracies here in the future!
+		 *
+		 * dwLoopLength seems to match dwPlayLength in everything I've seen that had bLoopCount == 0.
+		 * dwLoopBegin can be > 0 even with bLoopCount == 0 because why not. Let's ignore that.
+		 *
+		 * dwSamplesEncoded is usually close to dwPlayLength but not always (if ever?) equal. Let's assume equality.
+		 * The XMA2 seek table uses sample indices as opposed to WMA's byte index seek table.
+		 *
+		 * nBlockAlign uses aWMABlockAlign given the entire WMA Pro thing BUT it's expected to be the block size for decoding.
+		 * The XMA2 block size MUST be a multiple of 2048 BUT entry->PlayRegion.dwLength / seek->entryCount doesn't respect that.
+		 * And even when correctly guesstimating the block size, we sometimes end up with block sizes >= 64k BYTES. nBlockAlign IS 16-BIT!
+		 * Scrap nBlockAlign. I've given up and made all FAudio gstreamer functions use dwBytesPerBlock if available.
+		 * Still though, if we don't want FAudio_INTERNAL_DecodeGSTREAMER to hang, the total data length must match (see SoundEffect.cs in FNA).
+		 * As such, we round up when guessing the block size, feed GStreamer with zeroes^Wundersized blocks and hope for the best.
+		 *
+		 * This is FUN.
+		 * -ade
+		 */
+		FAudio_assert(entry->Format.wBitsPerSample != 0);
+
+		seek = &pWaveBank->seekTables[nWaveIndex];
+		format.pcm.wFormatTag = FAUDIO_FORMAT_XMAUDIO2;
+		format.pcm.wBitsPerSample = 16;
+		format.pcm.nAvgBytesPerSec = aWMAAvgBytesPerSec[entry->Format.wBlockAlign >> 5];
+		format.pcm.nBlockAlign = aWMABlockAlign[entry->Format.wBlockAlign & 0x1F];
+		format.pcm.cbSize = (
+			sizeof(FAudioXMA2WaveFormat) -
+			sizeof(FAudioWaveFormatEx)
+		);
+		format.xma2.wNumStreams = (format.pcm.nChannels + 1) / 2;
+		format.xma2.dwChannelMask = format.pcm.nChannels > 1 ? 0xFFFFFFFF >> (32 - format.pcm.nChannels) : 0;
+		format.xma2.dwSamplesEncoded = seek->entries[seek->entryCount - 1];
+		format.xma2.dwBytesPerBlock = (uint16_t) FAudio_ceil(
+			(double) entry->PlayRegion.dwLength /
+			(double) seek->entryCount /
+			2048.0
+		) * 2048;
+		format.xma2.dwPlayBegin = format.xma2.dwLoopBegin = 0;
+		format.xma2.dwPlayLength = format.xma2.dwLoopLength = format.xma2.dwSamplesEncoded;
+		format.xma2.bLoopCount = 0;
+		format.xma2.bEncoderVersion = 4;
+		format.xma2.wBlockCount = seek->entryCount;
+	}
+	else if (entry->Format.wFormatTag == 0x2)
+	{
+		format.pcm.wFormatTag = FAUDIO_FORMAT_MSADPCM;
+		format.pcm.nBlockAlign = (entry->Format.wBlockAlign + 22) * format.pcm.nChannels;
+		format.pcm.wBitsPerSample = 16;
+		format.pcm.cbSize = (
+			sizeof(FAudioADPCMWaveFormat) -
+			sizeof(FAudioWaveFormatEx)
+		);
+		format.adpcm.wSamplesPerBlock = (
+			((format.pcm.nBlockAlign / format.pcm.nChannels) - 6) * 2
+		);
+	}
+	else if (entry->Format.wFormatTag == 0x3)
+	{
+		/* Apparently this is used to detect WMA Pro...? */
+		FAudio_assert(entry->Format.wBitsPerSample == 0);
+
+		format.pcm.wFormatTag = FAUDIO_FORMAT_WMAUDIO2;
+		format.pcm.nAvgBytesPerSec = aWMAAvgBytesPerSec[entry->Format.wBlockAlign >> 5];
+		format.pcm.nBlockAlign = aWMABlockAlign[entry->Format.wBlockAlign & 0x1F];
+		format.pcm.wBitsPerSample = 16;
+		format.pcm.cbSize = 0;
+	}
+	else
+	{
+		FAudio_assert(0 && "Rebuild your WaveBanks with ADPCM!");
+	}
+	(*ppWave)->callback.callback.OnBufferEnd = pWaveBank->streaming ?
+		FACT_INTERNAL_OnBufferEnd :
+		NULL;
+	(*ppWave)->callback.callback.OnBufferStart = NULL;
+	(*ppWave)->callback.callback.OnLoopEnd = NULL;
+	(*ppWave)->callback.callback.OnStreamEnd = FACT_INTERNAL_OnStreamEnd;
+	(*ppWave)->callback.callback.OnVoiceError = NULL;
+	(*ppWave)->callback.callback.OnVoiceProcessingPassEnd = NULL;
+	(*ppWave)->callback.callback.OnVoiceProcessingPassStart = NULL;
+	(*ppWave)->callback.wave = *ppWave;
+	(*ppWave)->srcChannels = format.pcm.nChannels;
+	FAudio_CreateSourceVoice(
+		pWaveBank->parentEngine->audio,
+		&(*ppWave)->voice,
+		&format.pcm,
+		FAUDIO_VOICE_USEFILTER, /* FIXME: Can this be optional? */
+		4.0f,
+		(FAudioVoiceCallback*) &(*ppWave)->callback,
+		&sends,
+		NULL
+	);
+	if (pWaveBank->streaming)
+	{
+		/* Init stream cache info */
+		if (format.pcm.wFormatTag == FAUDIO_FORMAT_PCM)
+		{
+			(*ppWave)->streamSize = (
+				format.pcm.nSamplesPerSec *
+				format.pcm.nBlockAlign
+			);
+		}
+		else if (format.pcm.wFormatTag == FAUDIO_FORMAT_MSADPCM)
+		{
+			(*ppWave)->streamSize = (
+				format.pcm.nSamplesPerSec /
+				format.adpcm.wSamplesPerBlock *
+				format.pcm.nBlockAlign
+			);
+		}
+		else
+		{
+			/* Screw it, load the whole thing */
+			(*ppWave)->streamSize = entry->PlayRegion.dwLength;
+
+			/* XACT does NOT support loop subregions for these formats */
+			FAudio_assert(entry->LoopRegion.dwStartSample == 0);
+			FAudio_assert(entry->LoopRegion.dwTotalSamples == 0 || entry->LoopRegion.dwTotalSamples == entry->Duration);
+		}
+		(*ppWave)->streamCache = (uint8_t*) pWaveBank->parentEngine->pMalloc(
+			(*ppWave)->streamSize
+		);
+		(*ppWave)->streamOffset = entry->PlayRegion.dwOffset;
+
+		/* Read and submit first buffer from the WaveBank */
+		FACT_INTERNAL_OnBufferEnd(&(*ppWave)->callback.callback, NULL);
+	}
+	else
+	{
+		(*ppWave)->streamCache = NULL;
+
+		buffer.Flags = FAUDIO_END_OF_STREAM;
+		buffer.AudioBytes = entry->PlayRegion.dwLength;
+		buffer.pAudioData = FAudio_memptr(
+			pWaveBank->io,
+			entry->PlayRegion.dwOffset
+		);
+		buffer.PlayBegin = 0;
+		buffer.PlayLength = entry->Duration;
+		if (nLoopCount == 0)
+		{
+			buffer.LoopBegin = 0;
+			buffer.LoopLength = 0;
+			buffer.LoopCount = 0;
+		}
+		else
+		{
+			buffer.LoopBegin = entry->LoopRegion.dwStartSample;
+			buffer.LoopLength = entry->LoopRegion.dwTotalSamples;
+			buffer.LoopCount = nLoopCount;
+		}
+		buffer.pContext = NULL;
+		if (format.pcm.wFormatTag == FAUDIO_FORMAT_WMAUDIO2)
+		{
+			bufferWMA.pDecodedPacketCumulativeBytes =
+				pWaveBank->seekTables[nWaveIndex].entries;
+			bufferWMA.PacketCount =
+				pWaveBank->seekTables[nWaveIndex].entryCount;
+			FAudioSourceVoice_SubmitSourceBuffer(
+				(*ppWave)->voice,
+				&buffer,
+				&bufferWMA
+			);
+		}
+		else
+		{
+			FAudioSourceVoice_SubmitSourceBuffer(
+				(*ppWave)->voice,
+				&buffer,
+				NULL
+			);
+		}
+	}
+
+	/* Add to the WaveBank Wave list */
+	LinkedList_AddEntry(
+		&pWaveBank->waveList,
+		*ppWave,
+		pWaveBank->waveLock,
+		pWaveBank->parentEngine->pMalloc
+	);
+
+	FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTWaveBank_Play(
+	FACTWaveBank *pWaveBank,
+	uint16_t nWaveIndex,
+	uint32_t dwFlags,
+	uint32_t dwPlayOffset,
+	uint8_t nLoopCount,
+	FACTWave **ppWave
+) {
+	if (pWaveBank == NULL)
+	{
+		*ppWave = NULL;
+		return 1;
+	}
+	FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
+	FACTWaveBank_Prepare(
+		pWaveBank,
+		nWaveIndex,
+		dwFlags,
+		dwPlayOffset,
+		nLoopCount,
+		ppWave
+	);
+	FACTWave_Play(*ppWave);
+	FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTWaveBank_Stop(
+	FACTWaveBank *pWaveBank,
+	uint16_t nWaveIndex,
+	uint32_t dwFlags
+) {
+	FACTWave *wave;
+	LinkedList *list;
+	if (pWaveBank == NULL)
+	{
+		return 1;
+	}
+	FAudio_PlatformLockMutex(pWaveBank->parentEngine->apiLock);
+	list = pWaveBank->waveList;
+	while (list != NULL)
+	{
+		wave = (FACTWave*) list->entry;
+		if (wave->index == nWaveIndex)
+		{
+			FACTWave_Stop(wave, dwFlags);
+		}
+		list = list->next;
+	}
+	FAudio_PlatformUnlockMutex(pWaveBank->parentEngine->apiLock);
+	return 0;
+}
+
+/* Wave implementation */
+
+uint32_t FACTWave_Destroy(FACTWave *pWave)
+{
+	FAudioMutex mutex;
+	FACTNotification note;
+	if (pWave == NULL)
+	{
+		return 1;
+	}
+
+	FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
+
+	/* Stop before we start deleting everything */
+	FACTWave_Stop(pWave, FACT_FLAG_STOP_IMMEDIATE);
+
+	LinkedList_RemoveEntry(
+		&pWave->parentBank->waveList,
+		pWave,
+		pWave->parentBank->waveLock,
+		pWave->parentBank->parentEngine->pFree
+	);
+
+	FAudioVoice_DestroyVoice(pWave->voice);
+	if (pWave->streamCache != NULL)
+	{
+		pWave->parentBank->parentEngine->pFree(pWave->streamCache);
+	}
+	if (pWave->notifyOnDestroy || pWave->parentBank->parentEngine->notifications & NOTIFY_WAVEDESTROY)
+	{
+		note.type = FACTNOTIFICATIONTYPE_WAVEDESTROYED;
+		note.wave.pWave = pWave;
+		if (pWave->parentBank->parentEngine->notifications & NOTIFY_WAVEDESTROY)
+		{
+			note.pvContext = pWave->parentBank->parentEngine->wave_context;
+		}
+		else
+		{
+			note.pvContext = pWave->usercontext;
+		}
+		pWave->parentBank->parentEngine->notificationCallback(&note);
+	}
+
+	mutex = pWave->parentBank->parentEngine->apiLock;
+	pWave->parentBank->parentEngine->pFree(pWave);
+	FAudio_PlatformUnlockMutex(mutex);
+	return 0;
+}
+
+uint32_t FACTWave_Play(FACTWave *pWave)
+{
+	if (pWave == NULL)
+	{
+		return 1;
+	}
+	FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
+	FAudio_assert(!(pWave->state & (FACT_STATE_PLAYING | FACT_STATE_STOPPING)));
+	pWave->state |= FACT_STATE_PLAYING;
+	pWave->state &= ~(
+		FACT_STATE_PAUSED |
+		FACT_STATE_STOPPED
+	);
+	FAudioSourceVoice_Start(pWave->voice, 0, 0);
+	FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTWave_Stop(FACTWave *pWave, uint32_t dwFlags)
+{
+	if (pWave == NULL)
+	{
+		return 1;
+	}
+	FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
+
+	/* There are two ways that a Wave might be stopped immediately:
+	 * 1. The program explicitly asks for it
+	 * 2. The Wave is paused and therefore we can't do fade/release effects
+	 */
+	if (	dwFlags & FACT_FLAG_STOP_IMMEDIATE ||
+		pWave->state & FACT_STATE_PAUSED	)
+	{
+		pWave->state |= FACT_STATE_STOPPED;
+		pWave->state &= ~(
+			FACT_STATE_PLAYING |
+			FACT_STATE_STOPPING |
+			FACT_STATE_PAUSED
+		);
+		FAudioSourceVoice_Stop(pWave->voice, 0, 0);
+		FAudioSourceVoice_FlushSourceBuffers(pWave->voice);
+	}
+	else
+	{
+		pWave->state |= FACT_STATE_STOPPING;
+		FAudioSourceVoice_ExitLoop(pWave->voice, 0);
+	}
+
+	if (pWave->parentBank->parentEngine->notifications & NOTIFY_WAVESTOP)
+	{
+		FACTNotification note;
+		note.type = FACTNOTIFICATIONTYPE_WAVESTOP;
+		note.wave.pWave = pWave;
+		if (pWave->parentBank->parentEngine->notifications & NOTIFY_WAVESTOP)
+		{
+			note.pvContext = pWave->parentBank->parentEngine->wave_context;
+		}
+		pWave->parentBank->parentEngine->notificationCallback(&note);
+	}
+
+	FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTWave_Pause(FACTWave *pWave, int32_t fPause)
+{
+	if (pWave == NULL)
+	{
+		return 1;
+	}
+	FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
+
+	/* FIXME: Does the Cue STOPPING/STOPPED rule apply here too? */
+	if (pWave->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))
+	{
+		FAudio_PlatformUnlockMutex(
+			pWave->parentBank->parentEngine->apiLock
+		);
+		return 0;
+	}
+
+	/* All we do is set the flag, the mixer handles the rest */
+	if (fPause)
+	{
+		pWave->state |= FACT_STATE_PAUSED;
+		FAudioSourceVoice_Stop(pWave->voice, 0, 0);
+	}
+	else
+	{
+		pWave->state &= ~FACT_STATE_PAUSED;
+		FAudioSourceVoice_Start(pWave->voice, 0, 0);
+	}
+
+	FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTWave_GetState(FACTWave *pWave, uint32_t *pdwState)
+{
+	if (pWave == NULL)
+	{
+		*pdwState = 0;
+		return 1;
+	}
+	FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
+	*pdwState = pWave->state;
+	FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTWave_SetPitch(FACTWave *pWave, int16_t pitch)
+{
+	if (pWave == NULL)
+	{
+		return 1;
+	}
+	FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
+	pWave->pitch = FAudio_clamp(
+		pitch,
+		FACTPITCH_MIN_TOTAL,
+		FACTPITCH_MAX_TOTAL
+	);
+	FAudioSourceVoice_SetFrequencyRatio(
+		pWave->voice,
+		(float) FAudio_pow(2.0, pWave->pitch / 1200.0),
+		0
+	);
+	FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTWave_SetVolume(FACTWave *pWave, float volume)
+{
+	if (pWave == NULL)
+	{
+		return 1;
+	}
+	FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
+	pWave->volume = FAudio_clamp(
+		volume,
+		FACTVOLUME_MIN,
+		FACTVOLUME_MAX
+	);
+	FAudioVoice_SetVolume(
+		pWave->voice,
+		pWave->volume,
+		0
+	);
+	FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTWave_SetMatrixCoefficients(
+	FACTWave *pWave,
+	uint32_t uSrcChannelCount,
+	uint32_t uDstChannelCount,
+	float *pMatrixCoefficients
+) {
+	uint32_t i;
+	float *mtxDst, *mtxSrc, *mtxTmp = NULL;
+	if (pWave == NULL)
+	{
+		return 1;
+	}
+
+	/* There seems to be this weird feature in XACT where the channel count
+	 * can be completely wrong and it'll go to the right place.
+	 * I guess these XACT functions do some extra work to merge coefficients
+	 * but I have no idea where it really happens and XAudio2 definitely
+	 * does NOT like it when this is wrong, so here it goes...
+	 * -flibit
+	 */
+	if (uSrcChannelCount == 1 && pWave->srcChannels == 2)
+	{
+		mtxTmp = (float*) FAudio_alloca(
+			sizeof(float) *
+			pWave->srcChannels *
+			uDstChannelCount
+		);
+		mtxDst = mtxTmp;
+		mtxSrc = pMatrixCoefficients;
+		for (i = 0; i < uDstChannelCount; i += 1)
+		{
+			mtxDst[0] = *mtxSrc;
+			mtxDst[1] = *mtxSrc;
+			mtxDst += 2;
+			mtxSrc += 1;
+		}
+		uSrcChannelCount = 2;
+		pMatrixCoefficients = mtxTmp;
+	}
+	else if (uSrcChannelCount == 2 && pWave->srcChannels == 1)
+	{
+		mtxTmp = (float*) FAudio_alloca(
+			sizeof(float) *
+			pWave->srcChannels *
+			uDstChannelCount
+		);
+		mtxDst = mtxTmp;
+		mtxSrc = pMatrixCoefficients;
+		for (i = 0; i < uDstChannelCount; i += 1)
+		{
+			*mtxDst = (mtxSrc[0] + mtxSrc[1]) / 2.0f;
+			mtxDst += 1;
+			mtxSrc += 2;
+		}
+		uSrcChannelCount = 1;
+		pMatrixCoefficients = mtxTmp;
+	}
+
+	FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
+
+	FAudioVoice_SetOutputMatrix(
+		pWave->voice,
+		pWave->voice->sends.pSends->pOutputVoice,
+		uSrcChannelCount,
+		uDstChannelCount,
+		pMatrixCoefficients,
+		0
+	);
+
+	FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
+	if (mtxTmp != NULL)
+	{
+		FAudio_dealloca(mtxTmp);
+	}
+	return 0;
+}
+
+uint32_t FACTWave_GetProperties(
+	FACTWave *pWave,
+	FACTWaveInstanceProperties *pProperties
+) {
+	if (pWave == NULL)
+	{
+		return 1;
+	}
+	FAudio_PlatformLockMutex(pWave->parentBank->parentEngine->apiLock);
+
+	FACTWaveBank_GetWaveProperties(
+		pWave->parentBank,
+		pWave->index,
+		&pProperties->properties
+	);
+
+	/* FIXME: This is unsupported on PC, do we care about this? */
+	pProperties->backgroundMusic = 0;
+
+	FAudio_PlatformUnlockMutex(pWave->parentBank->parentEngine->apiLock);
+	return 0;
+}
+
+/* Cue implementation */
+
+uint32_t FACTCue_Destroy(FACTCue *pCue)
+{
+	FACTCue *cue, *prev;
+	FAudioMutex mutex;
+	FACTNotification note;
+	if (pCue == NULL)
+	{
+		return 1;
+	}
+
+	FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
+
+	/* Stop before we start deleting everything */
+	FACTCue_Stop(pCue, FACT_FLAG_STOP_IMMEDIATE);
+
+	/* Remove this Cue from the SoundBank list */
+	cue = pCue->parentBank->cueList;
+	prev = cue;
+	while (cue != NULL)
+	{
+		if (cue == pCue)
+		{
+			if (cue == prev) /* First in list */
+			{
+				pCue->parentBank->cueList = cue->next;
+			}
+			else
+			{
+				prev->next = cue->next;
+			}
+			break;
+		}
+		prev = cue;
+		cue = cue->next;
+	}
+	FAudio_assert(cue != NULL && "Could not find Cue reference!");
+
+	pCue->parentBank->parentEngine->pFree(pCue->variableValues);
+	if (pCue->notifyOnDestroy || pCue->parentBank->parentEngine->notifications & NOTIFY_CUEDESTROY)
+	{
+		note.type = FACTNOTIFICATIONTYPE_CUEDESTROYED;
+		note.cue.pCue = pCue;
+		if (pCue->parentBank->parentEngine->notifications & NOTIFY_CUEDESTROY)
+		{
+			note.pvContext = pCue->parentBank->parentEngine->cue_context;
+		}
+		else
+		{
+			note.pvContext = pCue->usercontext;
+		}
+		pCue->parentBank->parentEngine->notificationCallback(&note);
+	}
+
+	mutex = pCue->parentBank->parentEngine->apiLock;
+	pCue->parentBank->parentEngine->pFree(pCue);
+	FAudio_PlatformUnlockMutex(mutex);
+	return 0;
+}
+
+uint32_t FACTCue_Play(FACTCue *pCue)
+{
+	union
+	{
+		float maxf;
+		uint8_t maxi;
+	} limitmax;
+	FACTCue *tmp, *wnr;
+	uint16_t fadeInMS = 0;
+	FACTCueData *data;
+	if (pCue == NULL)
+	{
+		return 1;
+	}
+
+	FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
+
+	FAudio_assert(!(pCue->state & (FACT_STATE_PLAYING | FACT_STATE_STOPPING)));
+
+	data = &pCue->parentBank->cues[pCue->index];
+
+	/* Cue Instance Limits */
+	if (data->instanceCount >= data->instanceLimit)
+	{
+		wnr = NULL;
+		tmp = pCue->parentBank->cueList;
+		if (data->maxInstanceBehavior == 0) /* Fail */
+		{
+			pCue->state |= FACT_STATE_STOPPED;
+			pCue->state &= ~(
+				FACT_STATE_PLAYING |
+				FACT_STATE_STOPPING |
+				FACT_STATE_PAUSED
+			);
+
+			FACT_INTERNAL_SendCueNotification(pCue, NOTIFY_CUESTOP, FACTNOTIFICATIONTYPE_CUESTOP);
+
+			FAudio_PlatformUnlockMutex(
+				pCue->parentBank->parentEngine->apiLock
+			);
+			return 1;
+		}
+		else if (data->maxInstanceBehavior == 1) /* Queue */
+		{
+			/* FIXME: How is this different from Replace Oldest? */
+			while (tmp != NULL)
+			{
+				if (	tmp != pCue &&
+					tmp->index == pCue->index &&
+					!(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))	)
+				{
+					wnr = tmp;
+					break;
+				}
+				tmp = tmp->next;
+			}
+		}
+		else if (data->maxInstanceBehavior == 2) /* Replace Oldest */
+		{
+			while (tmp != NULL)
+			{
+				if (	tmp != pCue &&
+					tmp->index == pCue->index &&
+					!(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))	)
+				{
+					wnr = tmp;
+					break;
+				}
+				tmp = tmp->next;
+			}
+		}
+		else if (data->maxInstanceBehavior == 3) /* Replace Quietest */
+		{
+			limitmax.maxf = FACTVOLUME_MAX;
+			while (tmp != NULL)
+			{
+				if (	tmp != pCue &&
+					tmp->index == pCue->index &&
+					tmp->playingSound != NULL &&
+					/*FIXME: tmp->playingSound->volume < limitmax.maxf &&*/
+					!(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))	)
+				{
+					wnr = tmp;
+					/* limitmax.maxf = tmp->playingSound->volume; */
+				}
+				tmp = tmp->next;
+			}
+		}
+		else if (data->maxInstanceBehavior == 4) /* Replace Lowest Priority */
+		{
+			limitmax.maxi = 0xFF;
+			while (tmp != NULL)
+			{
+				if (	tmp != pCue &&
+					tmp->index == pCue->index &&
+					tmp->playingSound != NULL &&
+					tmp->playingSound->sound->priority < limitmax.maxi &&
+					!(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))	)
+				{
+					wnr = tmp;
+					limitmax.maxi = tmp->playingSound->sound->priority;
+				}
+				tmp = tmp->next;
+			}
+		}
+		if (wnr != NULL)
+		{
+			fadeInMS = data->fadeInMS;
+			if (wnr->playingSound != NULL)
+			{
+				FACT_INTERNAL_BeginFadeOut(wnr->playingSound, data->fadeOutMS);
+			}
+			else
+			{
+				FACTCue_Stop(wnr, 0);
+			}
+		}
+	}
+
+	/* Need an initial sound to play */
+	if (!FACT_INTERNAL_CreateSound(pCue, fadeInMS))
+	{
+		FAudio_PlatformUnlockMutex(
+			pCue->parentBank->parentEngine->apiLock
+		);
+		return 1;
+	}
+	data->instanceCount += 1;
+
+	pCue->state |= FACT_STATE_PLAYING;
+	pCue->state &= ~(
+		FACT_STATE_PAUSED |
+		FACT_STATE_STOPPED |
+		FACT_STATE_PREPARED
+	);
+
+	FACT_INTERNAL_SendCueNotification(pCue, NOTIFY_CUEPLAY, FACTNOTIFICATIONTYPE_CUEPLAY);
+
+	pCue->start = FAudio_timems();
+
+	/* If it's a simple wave, just play it! */
+	if (pCue->simpleWave != NULL)
+	{
+		if (pCue->active3D)
+		{
+			FACTWave_SetMatrixCoefficients(
+				pCue->simpleWave,
+				pCue->srcChannels,
+				pCue->dstChannels,
+				pCue->matrixCoefficients
+			);
+		}
+		FACTWave_Play(pCue->simpleWave);
+	}
+
+	FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTCue_Stop(FACTCue *pCue, uint32_t dwFlags)
+{
+	if (pCue == NULL)
+	{
+		return 1;
+	}
+	FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
+
+	/* If we're already stopped, there's nothing to do... */
+	if (pCue->state & FACT_STATE_STOPPED)
+	{
+		FAudio_PlatformUnlockMutex(
+			pCue->parentBank->parentEngine->apiLock
+		);
+		return 0;
+	}
+
+	/* If we're stopping and we haven't asked for IMMEDIATE, we're already
+	 * doing what the application is asking us to do...
+	 */
+	if (	(pCue->state & FACT_STATE_STOPPING) &&
+		!(dwFlags & FACT_FLAG_STOP_IMMEDIATE)	)
+	{
+		FAudio_PlatformUnlockMutex(
+			pCue->parentBank->parentEngine->apiLock
+		);
+		return 0;
+	}
+
+	/* There are three ways that a Cue might be stopped immediately:
+	 * 1. The program explicitly asks for it
+	 * 2. The Cue is paused and therefore we can't do fade/release effects
+	 * 3. The Cue is stopped "as authored" and has no fade effects
+	 */
+	if (	dwFlags & FACT_FLAG_STOP_IMMEDIATE ||
+		pCue->state & FACT_STATE_PAUSED	||
+		pCue->playingSound == NULL ||
+		(	pCue->parentBank->cues[pCue->index].fadeOutMS == 0 &&
+			pCue->maxRpcReleaseTime == 0	)	)
+	{
+		pCue->start = 0;
+		pCue->elapsed = 0;
+		pCue->state |= FACT_STATE_STOPPED;
+		pCue->state &= ~(
+			FACT_STATE_PLAYING |
+			FACT_STATE_STOPPING |
+			FACT_STATE_PAUSED
+		);
+
+		if (pCue->simpleWave != NULL)
+		{
+			FACTWave_Destroy(pCue->simpleWave);
+			pCue->simpleWave = NULL;
+
+			pCue->data->instanceCount -= 1;
+		}
+		else if (pCue->playingSound != NULL)
+		{
+			FACT_INTERNAL_DestroySound(pCue->playingSound);
+		}
+	}
+	else
+	{
+		if (pCue->parentBank->cues[pCue->index].fadeOutMS > 0)
+		{
+			FACT_INTERNAL_BeginFadeOut(
+				pCue->playingSound,
+				pCue->parentBank->cues[pCue->index].fadeOutMS
+			);
+		}
+		else if (pCue->maxRpcReleaseTime > 0)
+		{
+			FACT_INTERNAL_BeginReleaseRPC(
+				pCue->playingSound,
+				pCue->maxRpcReleaseTime
+			);
+		}
+		else
+		{
+			/* Pretty sure this doesn't happen, but just in case? */
+			pCue->state |= FACT_STATE_STOPPING;
+		}
+	}
+
+	FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTCue_GetState(FACTCue *pCue, uint32_t *pdwState)
+{
+	if (pCue == NULL)
+	{
+		*pdwState = 0;
+		return 1;
+	}
+	FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
+	*pdwState = pCue->state;
+	FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTCue_SetMatrixCoefficients(
+	FACTCue *pCue,
+	uint32_t uSrcChannelCount,
+	uint32_t uDstChannelCount,
+	float *pMatrixCoefficients
+) {
+	uint8_t i;
+
+	FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
+
+	/* See FACTCue.matrixCoefficients declaration */
+	FAudio_assert(uSrcChannelCount > 0 && uSrcChannelCount < 3);
+	FAudio_assert(uDstChannelCount > 0 && uDstChannelCount < 9);
+
+	/* Local storage */
+	pCue->srcChannels = uSrcChannelCount;
+	pCue->dstChannels = uDstChannelCount;
+	FAudio_memcpy(
+		pCue->matrixCoefficients,
+		pMatrixCoefficients,
+		sizeof(float) * uSrcChannelCount * uDstChannelCount
+	);
+	pCue->active3D = 1;
+
+	/* Apply to Waves if they exist */
+	if (pCue->simpleWave != NULL)
+	{
+		FACTWave_SetMatrixCoefficients(
+			pCue->simpleWave,
+			uSrcChannelCount,
+			uDstChannelCount,
+			pMatrixCoefficients
+		);
+	}
+	else if (pCue->playingSound != NULL)
+	{
+		for (i = 0; i < pCue->playingSound->sound->trackCount; i += 1)
+		{
+			if (pCue->playingSound->tracks[i].activeWave.wave != NULL)
+			{
+				FACTWave_SetMatrixCoefficients(
+					pCue->playingSound->tracks[i].activeWave.wave,
+					uSrcChannelCount,
+					uDstChannelCount,
+					pMatrixCoefficients
+				);
+			}
+		}
+	}
+
+	FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint16_t FACTCue_GetVariableIndex(
+	FACTCue *pCue,
+	const char *szFriendlyName
+) {
+	uint16_t i;
+	if (pCue == NULL)
+	{
+		return FACTVARIABLEINDEX_INVALID;
+	}
+	FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
+	for (i = 0; i < pCue->parentBank->parentEngine->variableCount; i += 1)
+	{
+		if (	FAudio_strcmp(szFriendlyName, pCue->parentBank->parentEngine->variableNames[i]) == 0 &&
+			pCue->parentBank->parentEngine->variables[i].accessibility & 0x04	)
+		{
+			FAudio_PlatformUnlockMutex(
+				pCue->parentBank->parentEngine->apiLock
+			);
+			return i;
+		}
+	}
+	FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
+	return FACTVARIABLEINDEX_INVALID;
+}
+
+uint32_t FACTCue_SetVariable(
+	FACTCue *pCue,
+	uint16_t nIndex,
+	float nValue
+) {
+	FACTVariable *var;
+	if (pCue == NULL)
+	{
+		return 1;
+	}
+
+	if (nIndex == FACTINDEX_INVALID)
+	{
+		return 1;
+	}
+
+	FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
+
+	var = &pCue->parentBank->parentEngine->variables[nIndex];
+	FAudio_assert(var->accessibility & 0x01);
+	FAudio_assert(!(var->accessibility & 0x02));
+	FAudio_assert(var->accessibility & 0x04);
+	pCue->variableValues[nIndex] = FAudio_clamp(
+		nValue,
+		var->minValue,
+		var->maxValue
+	);
+
+	FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTCue_GetVariable(
+	FACTCue *pCue,
+	uint16_t nIndex,
+	float *nValue
+) {
+	FACTVariable *var;
+	if (pCue == NULL)
+	{
+		*nValue = 0.0f;
+		return 1;
+	}
+
+	if (nIndex == FACTINDEX_INVALID)
+	{
+		return 1;
+	}
+
+	FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
+
+	var = &pCue->parentBank->parentEngine->variables[nIndex];
+	FAudio_assert(var->accessibility & 0x01);
+	FAudio_assert(var->accessibility & 0x04);
+
+	if (nIndex == 0) /* NumCueInstances */
+	{
+		*nValue = pCue->parentBank->cues[pCue->index].instanceCount;
+	}
+	else
+	{
+		*nValue = pCue->variableValues[nIndex];
+	}
+
+	FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTCue_Pause(FACTCue *pCue, int32_t fPause)
+{
+	uint8_t i;
+	if (pCue == NULL)
+	{
+		return 1;
+	}
+
+	FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
+
+	/* "A stopping or stopped cue cannot be paused." */
+	if (pCue->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))
+	{
+		FAudio_PlatformUnlockMutex(
+			pCue->parentBank->parentEngine->apiLock
+		);
+		return 0;
+	}
+
+	/* Store elapsed time */
+	pCue->elapsed += FAudio_timems() - pCue->start;
+
+	/* All we do is set the flag, not much to see here */
+	if (fPause)
+	{
+		pCue->state |= FACT_STATE_PAUSED;
+	}
+	else
+	{
+		pCue->state &= ~FACT_STATE_PAUSED;
+	}
+
+	/* Pause the Waves */
+	if (pCue->simpleWave != NULL)
+	{
+		FACTWave_Pause(pCue->simpleWave, fPause);
+	}
+	else if (pCue->playingSound != NULL)
+	{
+		for (i = 0; i < pCue->playingSound->sound->trackCount; i += 1)
+		{
+			if (pCue->playingSound->tracks[i].activeWave.wave != NULL)
+			{
+				FACTWave_Pause(
+					pCue->playingSound->tracks[i].activeWave.wave,
+					fPause
+				);
+			}
+		}
+	}
+
+	FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
+	return 0;
+}
+
+uint32_t FACTCue_GetProperties(
+	FACTCue *pCue,
+	FACTCueInstanceProperties **ppProperties
+) {
+	uint32_t i;
+	size_t allocSize;
+	FACTCueInstanceProperties *cueProps;
+	FACTVariationProperties *varProps;
+	FACTSoundProperties *sndProps;
+	FACTWaveInstanceProperties waveProps;
+	if (pCue == NULL)
+	{
+		return 1;
+	}
+
+	FAudio_PlatformLockMutex(pCue->parentBank->parentEngine->apiLock);
+
+	/* Alloc container (including variable length array space) */
+	allocSize = sizeof(FACTCueInstanceProperties);
+	if (pCue->playingSound != NULL)
+	{
+		allocSize += (
+			sizeof(FACTTrackProperties) *
+			pCue->playingSound->sound->trackCount
+		);
+	}
+	cueProps = (FACTCueInstanceProperties*) pCue->parentBank->parentEngine->pMalloc(
+		allocSize
+	);
+	FAudio_zero(cueProps, allocSize);
+
+	/* Cue Properties */
+	FACTSoundBank_GetCueProperties(
+		pCue->parentBank,
+		pCue->index,
+		&cueProps->cueProperties
+	);
+
+	/* Variation Properties */
+	varProps = &cueProps->activeVariationProperties.variationProperties;
+	if (pCue->playingVariation != NULL)
+	{
+		varProps->index = 0; /* TODO: Index of what...? */
+		/* TODO: This is just max - min right? Also why u8 wtf */
+		varProps->weight = (uint8_t) (
+			pCue->playingVariation->maxWeight -
+			pCue->playingVariation->minWeight
+		);
+		if (pCue->variation->flags == 3)
+		{
+			varProps->iaVariableMin =
+				pCue->playingVariation->minWeight;
+			varProps->iaVariableMax =
+				pCue->playingVariation->maxWeight;
+		}
+		else
+		{
+			varProps->iaVariableMin = 0;
+			varProps->iaVariableMax = 0;
+		}
+		varProps->linger = pCue->playingVariation->linger;
+	}
+
+	/* Sound Properties */
+	sndProps = &cueProps->activeVariationProperties.soundProperties;
+	if (pCue->playingSound != NULL)
+	{
+		sndProps->category = pCue->playingSound->sound->category;
+		sndProps->priority = pCue->playingSound->sound->priority;
+		sndProps->pitch = pCue->playingSound->sound->pitch;
+		sndProps->volume = pCue->playingSound->sound->volume;
+		sndProps->numTracks = pCue->playingSound->sound->trackCount;
+
+		for (i = 0; i < sndProps->numTracks; i += 1)
+		{
+			if (FACTWave_GetProperties(
+				pCue->playingSound->tracks[i].activeWave.wave,
+				&waveProps
+			) == 0) {
+				sndProps->arrTrackProperties[i].duration = (uint32_t) (
+					(
+						(float) waveProps.properties.durationInSamples /
+						(float) waveProps.properties.format.nSamplesPerSec
+					) / 1000.0f
+				);
+				sndProps->arrTrackProperties[i].numVariations = 1; /* ? */
+				sndProps->arrTrackProperties[i].numChannels =
+					waveProps.properties.format.nChannels;
+				sndProps->arrTrackProperties[i].waveVariation = 0; /* ? */
+				sndProps->arrTrackProperties[i].loopCount =
+					pCue->playingSound->tracks[i].waveEvt->wave.loopCount;
+			}
+		}
+	}
+
+	FAudio_PlatformUnlockMutex(pCue->parentBank->parentEngine->apiLock);
+
+	*ppProperties = cueProps;
+	return 0;
+}
+
+uint32_t FACTCue_SetOutputVoices(
+	FACTCue *pCue,
+	const FAudioVoiceSends *pSendList /* Optional XAUDIO2_VOICE_SENDS */
+) {
+	/* TODO */
+	return 0;
+}
+
+uint32_t FACTCue_SetOutputVoiceMatrix(
+	FACTCue *pCue,
+	const FAudioVoice *pDestinationVoice, /* Optional! */
+	uint32_t SourceChannels,
+	uint32_t DestinationChannels,
+	const float *pLevelMatrix /* SourceChannels * DestinationChannels */
+) {
+	/* TODO */
+	return 0;
+}
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FACT3D.c b/libs/faudio/src/FACT3D.c
new file mode 100644
index 00000000000..c9a8f61a4d5
--- /dev/null
+++ b/libs/faudio/src/FACT3D.c
@@ -0,0 +1,172 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "FACT3D.h"
+
+uint32_t FACT3DInitialize(
+	FACTAudioEngine *pEngine,
+	F3DAUDIO_HANDLE F3DInstance
+) {
+	float nSpeedOfSound;
+	FAudioWaveFormatExtensible wfxFinalMixFormat;
+
+	if (pEngine == NULL)
+	{
+		return 0;
+	}
+
+	FACTAudioEngine_GetGlobalVariable(
+		pEngine,
+		FACTAudioEngine_GetGlobalVariableIndex(
+			pEngine,
+			"SpeedOfSound"
+		),
+		&nSpeedOfSound
+	);
+	FACTAudioEngine_GetFinalMixFormat(
+		pEngine,
+		&wfxFinalMixFormat
+	);
+	F3DAudioInitialize(
+		wfxFinalMixFormat.dwChannelMask,
+		nSpeedOfSound,
+		F3DInstance
+	);
+	return 0;
+}
+
+uint32_t FACT3DCalculate(
+	F3DAUDIO_HANDLE F3DInstance,
+	const F3DAUDIO_LISTENER *pListener,
+	F3DAUDIO_EMITTER *pEmitter,
+	F3DAUDIO_DSP_SETTINGS *pDSPSettings
+) {
+	static F3DAUDIO_DISTANCE_CURVE_POINT DefaultCurvePoints[2] =
+	{
+		{ 0.0f, 1.0f },
+		{ 1.0f, 1.0f }
+	};
+	static F3DAUDIO_DISTANCE_CURVE DefaultCurve =
+	{
+		(F3DAUDIO_DISTANCE_CURVE_POINT*) &DefaultCurvePoints[0], 2
+	};
+
+	if (pListener == NULL || pEmitter == NULL || pDSPSettings == NULL)
+	{
+		return 0;
+	}
+
+	if (pEmitter->ChannelCount > 1 && pEmitter->pChannelAzimuths == NULL)
+	{
+		pEmitter->ChannelRadius = 1.0f;
+
+		if (pEmitter->ChannelCount == 2)
+		{
+			pEmitter->pChannelAzimuths = (float*) &aStereoLayout[0];
+		}
+		else if (pEmitter->ChannelCount == 3)
+		{
+			pEmitter->pChannelAzimuths = (float*) &a2Point1Layout[0];
+		}
+		else if (pEmitter->ChannelCount == 4)
+		{
+			pEmitter->pChannelAzimuths = (float*) &aQuadLayout[0];
+		}
+		else if (pEmitter->ChannelCount == 5)
+		{
+			pEmitter->pChannelAzimuths = (float*) &a4Point1Layout[0];
+		}
+		else if (pEmitter->ChannelCount == 6)
+		{
+			pEmitter->pChannelAzimuths = (float*) &a5Point1Layout[0];
+		}
+		else if (pEmitter->ChannelCount == 8)
+		{
+			pEmitter->pChannelAzimuths = (float*) &a7Point1Layout[0];
+		}
+		else
+		{
+			return 0;
+		}
+	}
+
+	if (pEmitter->pVolumeCurve == NULL)
+	{
+		pEmitter->pVolumeCurve = &DefaultCurve;
+	}
+	if (pEmitter->pLFECurve == NULL)
+	{
+		pEmitter->pLFECurve = &DefaultCurve;
+	}
+
+	F3DAudioCalculate(
+		F3DInstance,
+		pListener,
+		pEmitter,
+		(
+			F3DAUDIO_CALCULATE_MATRIX |
+			F3DAUDIO_CALCULATE_DOPPLER |
+			F3DAUDIO_CALCULATE_EMITTER_ANGLE
+		),
+		pDSPSettings
+	);
+	return 0;
+}
+
+uint32_t FACT3DApply(
+	F3DAUDIO_DSP_SETTINGS *pDSPSettings,
+	FACTCue *pCue
+) {
+	if (pDSPSettings == NULL || pCue == NULL)
+	{
+		return 0;
+	}
+
+	FACTCue_SetMatrixCoefficients(
+		pCue,
+		pDSPSettings->SrcChannelCount,
+		pDSPSettings->DstChannelCount,
+		pDSPSettings->pMatrixCoefficients
+	);
+	FACTCue_SetVariable(
+		pCue,
+		FACTCue_GetVariableIndex(pCue, "Distance"),
+		pDSPSettings->EmitterToListenerDistance
+	);
+	FACTCue_SetVariable(
+		pCue,
+		FACTCue_GetVariableIndex(pCue, "DopplerPitchScalar"),
+		pDSPSettings->DopplerFactor
+	);
+	FACTCue_SetVariable(
+		pCue,
+		FACTCue_GetVariableIndex(pCue, "OrientationAngle"),
+		pDSPSettings->EmitterToListenerAngle * (180.0f / F3DAUDIO_PI)
+	);
+	return 0;
+}
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FACT_internal.c b/libs/faudio/src/FACT_internal.c
new file mode 100644
index 00000000000..63b191c4aec
--- /dev/null
+++ b/libs/faudio/src/FACT_internal.c
@@ -0,0 +1,3336 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "FACT_internal.h"
+#include "FAudioFX.h"
+
+/* RNG */
+
+#define STB_EXTERN
+#define STB_DEFINE
+#include "stb.h"
+#define FACT_INTERNAL_rng() ((float) stb_frand())
+
+/* XACT Versions */
+
+#define FACT_CONTENT_VERSION_3_4 45
+#define FACT_CONTENT_VERSION_3_1 44
+#define FACT_CONTENT_VERSION_3_0 43
+
+static inline int FACT_INTERNAL_SupportedContent(uint16_t version)
+{
+	return (	version == FACT_CONTENT_VERSION ||
+			version == FACT_CONTENT_VERSION_3_4 ||
+			version == FACT_CONTENT_VERSION_3_1 ||
+			version == FACT_CONTENT_VERSION_3_0	);
+}
+
+#define WAVEBANK_HEADER_VERSION		44
+#define WAVEBANK_HEADER_VERSION_3_4	43
+#define WAVEBANK_HEADER_VERSION_3_1	42
+
+static inline int FACT_INTERNAL_SupportedWBContent(uint16_t version)
+{
+	return (	version == WAVEBANK_HEADER_VERSION ||
+			version == WAVEBANK_HEADER_VERSION_3_4 ||
+			version == WAVEBANK_HEADER_VERSION_3_1	);
+}
+
+/* Helper Functions */
+
+static inline float FACT_INTERNAL_CalculateAmplitudeRatio(float decibel)
+{
+	return (float) FAudio_pow(10.0, decibel / 2000.0);
+}
+
+static inline float FACT_INTERNAL_CalculateFilterFrequency(
+	float desiredFrequency,
+	uint32_t sampleRate
+) {
+	/* This is needed to convert linear frequencies to the value
+	 * FAudio_INTERNAL_FilterVoice expects, in order for it to actually
+	 * filter at the correct frequency.
+	 *
+	 * The formula is...
+	 *
+	 * (2 * sin(pi * (desired filter cutoff frequency) / sampleRate))
+	 *
+	 * ... but it behaves badly as the filter frequency gets too high as a
+	 * fraction of the sample rate, hence the mins.
+	 *
+	 * -@Woflox
+	 */
+	float freq = 2 * FAudio_sin(
+		F3DAUDIO_PI *
+		FAudio_min(desiredFrequency / sampleRate, 0.5f)
+	);
+	return FAudio_min(freq, 1.0f);
+}
+
+static inline void FACT_INTERNAL_ReadFile(
+	FACTReadFileCallback pReadFile,
+	FACTGetOverlappedResultCallback pGetOverlappedResult,
+	void* io,
+	uint32_t offset,
+	uint32_t packetSize,
+	uint8_t **packetBuffer,
+	uint32_t *packetBufferLen,
+	FAudioReallocFunc pRealloc,
+	void* dst,
+	uint32_t len
+) {
+	FACTOverlapped ovlp;
+	uint32_t realOffset, realLen, offPacket, lenPacket, result;
+	uint8_t usePacketBuffer;
+	void *buf;
+
+	ovlp.Internal = NULL;
+	ovlp.InternalHigh = NULL;
+	ovlp.OffsetHigh = 0; /* I sure hope so... */
+	ovlp.hEvent = NULL;
+
+	/* We have to read data in multiples of the sector size, or else
+	 * Win32 ReadFile returns ERROR_INVALID_PARAMETER
+	 */
+	realOffset = offset;
+	realLen = len;
+	usePacketBuffer = 0;
+	if (packetSize > 0)
+	{
+		offPacket = realOffset % packetSize;
+		if (offPacket > 0)
+		{
+			usePacketBuffer = 1;
+			realOffset -= offPacket;
+			realLen += offPacket;
+		}
+		lenPacket = realLen % packetSize;
+		if (lenPacket > 0)
+		{
+			usePacketBuffer = 1;
+			realLen += (packetSize - lenPacket);
+		}
+	}
+
+	/* If we're compensating for sector alignment, use a temp buffer and copy to
+	 * the real destination after we're finished.
+	 */
+	if (usePacketBuffer)
+	{
+		if (*packetBufferLen < realLen)
+		{
+			*packetBufferLen = realLen;
+			*packetBuffer = pRealloc(*packetBuffer, realLen);
+		}
+		buf = *packetBuffer;
+	}
+	else
+	{
+		buf = dst;
+	}
+
+	/* Read, finally. */
+	ovlp.Offset = realOffset;
+	if (!pReadFile(io, buf, realLen, NULL, &ovlp))
+	{
+		while (ovlp.Internal == (void*) 0x103) /* STATUS_PENDING */
+		{
+			/* Don't actually sleep, just yield the thread */
+			FAudio_sleep(0);
+		}
+	}
+	pGetOverlappedResult(io, &ovlp, &result, 1);
+
+	/* Copy the subregion that we actually care about, if applicable */
+	if (usePacketBuffer)
+	{
+		FAudio_memcpy(dst, *packetBuffer + offPacket, len);
+	}
+}
+
+/* Internal Functions */
+
+void FACT_INTERNAL_GetNextWave(
+	FACTCue *cue,
+	FACTSound *sound,
+	FACTTrack *track,
+	FACTTrackInstance *trackInst,
+	FACTEvent *evt,
+	FACTEventInstance *evtInst
+) {
+	FAudioSendDescriptor reverbDesc[2];
+	FAudioVoiceSends reverbSends;
+	const char *wbName;
+	FACTWaveBank *wb = NULL;
+	LinkedList *list;
+	uint16_t wbTrack;
+	uint8_t wbIndex;
+	uint8_t loopCount = 0;
+	float max, next;
+	uint8_t noTrackVariation = 1;
+	uint32_t i;
+
+	/* Track Variation */
+	if (evt->wave.isComplex)
+	{
+		if (	trackInst->activeWave.wave == NULL ||
+			!(evt->wave.complex.variation & 0x00F0)	)
+		{
+			/* No-op, no variation on loop */
+		}
+		/* Ordered, Ordered From Random */
+		else if (	(evt->wave.complex.variation & 0xF) == 0 ||
+				(evt->wave.complex.variation & 0xF) == 1	)
+		{
+			evtInst->valuei += 1;
+			if (evtInst->valuei >= evt->wave.complex.trackCount)
+			{
+				evtInst->valuei = 0;
+			}
+		}
+		/* Random */
+		else if ((evt->wave.complex.variation & 0xF) == 2)
+		{
+			max = 0.0f;
+			for (i = 0; i < evt->wave.complex.trackCount; i += 1)
+			{
+				max += evt->wave.complex.weights[i];
+			}
+			next = FACT_INTERNAL_rng() * max;
+			for (i = evt->wave.complex.trackCount; i > 0; i -= 1)
+			{
+				if (next > (max - evt->wave.complex.weights[i - 1]))
+				{
+					evtInst->valuei = i - 1;
+					break;
+				}
+				max -= evt->wave.complex.weights[i - 1];
+			}
+		}
+		/* Random (No Immediate Repeats), Shuffle */
+		else if (	(evt->wave.complex.variation & 0xF) == 3 ||
+				(evt->wave.complex.variation & 0xF) == 4	)
+		{
+			max = 0.0f;
+			for (i = 0; i < evt->wave.complex.trackCount; i += 1)
+			{
+				if (i == evtInst->valuei)
+				{
+					continue;
+				}
+				max += evt->wave.complex.weights[i];
+			}
+			next = FACT_INTERNAL_rng() * max;
+			for (i = evt->wave.complex.trackCount; i > 0; i -= 1)
+			{
+				if (i - 1 == evtInst->valuei)
+				{
+					continue;
+				}
+				if (next > (max - evt->wave.complex.weights[i - 1]))
+				{
+					evtInst->valuei = i - 1;
+					break;
+				}
+				max -= evt->wave.complex.weights[i - 1];
+			}
+		}
+
+		if (evt->wave.complex.variation & 0x00F0)
+		{
+			noTrackVariation = 0;
+		}
+
+		wbIndex = evt->wave.complex.wavebanks[evtInst->valuei];
+		wbTrack = evt->wave.complex.tracks[evtInst->valuei];
+	}
+	else
+	{
+		wbIndex = evt->wave.simple.wavebank;
+		wbTrack = evt->wave.simple.track;
+	}
+	wbName = cue->parentBank->wavebankNames[wbIndex];
+	list = cue->parentBank->parentEngine->wbList;
+	while (list != NULL)
+	{
+		wb = (FACTWaveBank*) list->entry;
+		if (FAudio_strcmp(wbName, wb->name) == 0)
+		{
+			break;
+		}
+		list = list->next;
+	}
+	FAudio_assert(wb != NULL);
+
+	/* Generate the Wave */
+	if (	evtInst->loopCount == 255 &&
+		noTrackVariation &&
+		!(evt->wave.variationFlags & 0x0F00)	)
+	{
+		/* For infinite loops with no variation, let Wave do the work */
+		loopCount = 255;
+	}
+	FACTWaveBank_Prepare(
+		wb,
+		wbTrack,
+		evt->wave.flags,
+		0,
+		loopCount,
+		&trackInst->upcomingWave.wave
+	);
+	trackInst->upcomingWave.wave->parentCue = cue;
+	if (sound->dspCodeCount > 0) /* Never more than 1...? */
+	{
+		reverbDesc[0].Flags = 0;
+		reverbDesc[0].pOutputVoice = cue->parentBank->parentEngine->master;
+		reverbDesc[1].Flags = 0;
+		reverbDesc[1].pOutputVoice = cue->parentBank->parentEngine->reverbVoice;
+		reverbSends.SendCount = 2;
+		reverbSends.pSends = reverbDesc;
+		FAudioVoice_SetOutputVoices(
+			trackInst->upcomingWave.wave->voice,
+			&reverbSends
+		);
+	}
+
+	/* 3D Audio */
+	if (cue->active3D)
+	{
+		FACTWave_SetMatrixCoefficients(
+			trackInst->upcomingWave.wave,
+			cue->srcChannels,
+			cue->dstChannels,
+			cue->matrixCoefficients
+		);
+	}
+	else
+	{
+		/* TODO: Position/Angle/UseCenterSpeaker */
+	}
+
+	/* Pitch Variation */
+	if (evt->wave.variationFlags & 0x1000)
+	{
+		const int16_t rngPitch = (int16_t) (
+			FACT_INTERNAL_rng() *
+			(evt->wave.maxPitch - evt->wave.minPitch)
+		) + evt->wave.minPitch;
+		if (trackInst->activeWave.wave != NULL)
+		{
+			/* Variation on Loop */
+			if (evt->wave.variationFlags & 0x0100)
+			{
+				/* Add/Replace */
+				if (evt->wave.variationFlags & 0x0004)
+				{
+					trackInst->upcomingWave.basePitch =
+						trackInst->activeWave.basePitch + rngPitch;
+				}
+				else
+				{
+					trackInst->upcomingWave.basePitch = rngPitch + sound->pitch;
+				}
+			}
+		}
+		else
+		{
+			/* Initial Pitch Variation */
+			trackInst->upcomingWave.basePitch = rngPitch + sound->pitch;
+		}
+	}
+	else
+	{
+		trackInst->upcomingWave.basePitch = sound->pitch;
+	}
+
+	/* Volume Variation */
+	if (evt->wave.variationFlags & 0x2000)
+	{
+		const float rngVolume = (
+			FACT_INTERNAL_rng() *
+			(evt->wave.maxVolume - evt->wave.minVolume)
+		) + evt->wave.minVolume;
+		if (trackInst->activeWave.wave != NULL)
+		{
+			/* Variation on Loop */
+			if (evt->wave.variationFlags & 0x0200)
+			{
+				/* Add/Replace */
+				if (evt->wave.variationFlags & 0x0001)
+				{
+					trackInst->upcomingWave.baseVolume =
+						trackInst->activeWave.baseVolume + rngVolume;
+				}
+				else
+				{
+					trackInst->upcomingWave.baseVolume = (
+						rngVolume +
+						sound->volume +
+						track->volume
+					);
+				}
+			}
+		}
+		else
+		{
+			/* Initial Volume Variation */
+			trackInst->upcomingWave.baseVolume = (
+				rngVolume +
+				sound->volume +
+				track->volume
+			);
+		}
+	}
+	else
+	{
+		trackInst->upcomingWave.baseVolume = sound->volume + track->volume;
+	}
+
+	/* Filter Variation, QFactor/Freq are always together */
+	if (evt->wave.variationFlags & 0xC000)
+	{
+		const float rngQFactor = 1.0f / (
+			FACT_INTERNAL_rng() *
+			(evt->wave.maxQFactor - evt->wave.minQFactor) +
+			evt->wave.minQFactor
+		);
+		const float rngFrequency = FACT_INTERNAL_CalculateFilterFrequency(
+			(
+				FACT_INTERNAL_rng() *
+				(evt->wave.maxFrequency - evt->wave.minFrequency) +
+				evt->wave.minFrequency
+			),
+			cue->parentBank->parentEngine->audio->master->master.inputSampleRate
+		);
+		if (trackInst->activeWave.wave != NULL)
+		{
+			/* Variation on Loop */
+			if (evt->wave.variationFlags & 0x0C00)
+			{
+				/* TODO: Add/Replace */
+				/* FIXME: Which is QFactor/Freq?
+				if (evt->wave.variationFlags & 0x0010)
+				{
+				}
+				else
+				{
+				}
+				if (evt->wave.variationFlags & 0x0040)
+				{
+				}
+				else
+				{
+				}
+				*/
+				trackInst->upcomingWave.baseQFactor = rngQFactor;
+				trackInst->upcomingWave.baseFrequency = rngFrequency;
+			}
+		}
+		else
+		{
+			/* Initial Filter Variation */
+			trackInst->upcomingWave.baseQFactor = rngQFactor;
+			trackInst->upcomingWave.baseFrequency = rngFrequency;
+		}
+	}
+	else
+	{
+		trackInst->upcomingWave.baseQFactor = 1.0f / (track->qfactor / 3.0f);
+		trackInst->upcomingWave.baseFrequency = FACT_INTERNAL_CalculateFilterFrequency(
+			track->frequency,
+			cue->parentBank->parentEngine->audio->master->master.inputSampleRate
+		);
+	}
+
+	/* Try to change loop counter at the very end */
+	if (loopCount == 255)
+	{
+		/* For infinite loops with no variation, Wave does the work */
+		evtInst->loopCount = 0;
+	}
+	else if (evtInst->loopCount > 0)
+	{
+		evtInst->loopCount -= 1;
+	}
+}
+
+uint8_t FACT_INTERNAL_CreateSound(FACTCue *cue, uint16_t fadeInMS)
+{
+	int32_t i, j, k;
+	float max, next, weight;
+	const char *wbName;
+	FACTWaveBank *wb = NULL;
+	LinkedList *list;
+	FACTEvent *evt;
+	FACTEventInstance *evtInst;
+	FACTSound *baseSound = NULL;
+	FACTSoundInstance *newSound;
+	FACTRPC *rpc;
+	float lastX;
+
+	union
+	{
+		float maxf;
+		uint8_t maxi;
+	} limitmax;
+	FACTCue *tmp, *wnr;
+	uint16_t categoryIndex;
+	FACTAudioCategory *category;
+
+	if (cue->data->flags & 0x04)
+	{
+		/* Sound */
+		baseSound = cue->sound;
+	}
+	else
+	{
+		/* Variation */
+		if (cue->variation->flags == 3)
+		{
+			/* Interactive */
+			if (cue->parentBank->parentEngine->variables[cue->variation->variable].accessibility & 0x04)
+			{
+				FACTCue_GetVariable(
+					cue,
+					cue->variation->variable,
+					&next
+				);
+			}
+			else
+			{
+				FACTAudioEngine_GetGlobalVariable(
+					cue->parentBank->parentEngine,
+					cue->variation->variable,
+					&next
+				);
+			}
+			for (i = 0; i < cue->variation->entryCount; i += 1)
+			{
+				if (	next <= cue->variation->entries[i].maxWeight &&
+					next >= cue->variation->entries[i].minWeight	)
+				{
+					break;
+				}
+			}
+
+			/* This should only happen when the user control
+			 * variable is none of the sound probabilities, in
+			 * which case we are just silent. But, we should still
+			 * claim to be "playing" in the meantime.
+			 */
+			if (i == cue->variation->entryCount)
+			{
+				return 1;
+			}
+		}
+		else
+		{
+			/* Random */
+			max = 0.0f;
+			for (i = 0; i < cue->variation->entryCount; i += 1)
+			{
+				max += (
+					cue->variation->entries[i].maxWeight -
+					cue->variation->entries[i].minWeight
+				);
+			}
+			next = FACT_INTERNAL_rng() * max;
+
+			/* Use > 0, not >= 0. If we hit 0, that's it! */
+			for (i = cue->variation->entryCount - 1; i > 0; i -= 1)
+			{
+				weight = (
+					cue->variation->entries[i].maxWeight -
+					cue->variation->entries[i].minWeight
+				);
+				if (next > (max - weight))
+				{
+					break;
+				}
+				max -= weight;
+			}
+		}
+
+		if (cue->variation->isComplex)
+		{
+			/* Grab the Sound via the code. FIXME: Do this at load time? */
+			for (j = 0; j < cue->parentBank->soundCount; j += 1)
+			{
+				if (cue->variation->entries[i].soundCode == cue->parentBank->soundCodes[j])
+				{
+					baseSound = &cue->parentBank->sounds[j];
+					break;
+				}
+			}
+		}
+		else
+		{
+			/* Pull in the WaveBank... */
+			wbName = cue->parentBank->wavebankNames[
+				cue->variation->entries[i].simple.wavebank
+			];
+			list = cue->parentBank->parentEngine->wbList;
+			while (list != NULL)
+			{
+				wb = (FACTWaveBank*) list->entry;
+				if (FAudio_strcmp(wbName, wb->name) == 0)
+				{
+					break;
+				}
+				list = list->next;
+			}
+			FAudio_assert(wb != NULL);
+
+			/* Generate the wave... */
+			FACTWaveBank_Prepare(
+				wb,
+				cue->variation->entries[i].simple.track,
+				0,
+				0,
+				0,
+				&cue->simpleWave
+			);
+			cue->simpleWave->parentCue = cue;
+		}
+	}
+
+	/* Alloc SoundInstance variables */
+	if (baseSound != NULL)
+	{
+		/* Category Instance Limits */
+		categoryIndex = baseSound->category;
+		if (categoryIndex != FACTCATEGORY_INVALID)
+		{
+			category = &cue->parentBank->parentEngine->categories[categoryIndex];
+			if (category->instanceCount >= category->instanceLimit)
+			{
+				wnr = NULL;
+				tmp = cue->parentBank->cueList;
+				if (category->maxInstanceBehavior == 0) /* Fail */
+				{
+					cue->state |= FACT_STATE_STOPPED;
+					cue->state &= ~(
+						FACT_STATE_PLAYING |
+						FACT_STATE_STOPPING |
+						FACT_STATE_PAUSED
+					);
+					return 0;
+				}
+				else if (category->maxInstanceBehavior == 1) /* Queue */
+				{
+					/* FIXME: How is this different from Replace Oldest? */
+					while (tmp != NULL)
+					{
+						if (	tmp != cue &&
+							tmp->playingSound != NULL &&
+							tmp->playingSound->sound->category == categoryIndex &&
+							!(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))	)
+						{
+							wnr = tmp;
+							break;
+						}
+						tmp = tmp->next;
+					}
+				}
+				else if (category->maxInstanceBehavior == 2) /* Replace Oldest */
+				{
+					while (tmp != NULL)
+					{
+						if (	tmp != cue &&
+							tmp->playingSound != NULL &&
+							tmp->playingSound->sound->category == categoryIndex &&
+							!(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))	)
+						{
+							wnr = tmp;
+							break;
+						}
+						tmp = tmp->next;
+					}
+				}
+				else if (category->maxInstanceBehavior == 3) /* Replace Quietest */
+				{
+					limitmax.maxf = FACTVOLUME_MAX;
+					while (tmp != NULL)
+					{
+						if (	tmp != cue &&
+							tmp->playingSound != NULL &&
+							tmp->playingSound->sound->category == categoryIndex &&
+							/*FIXME: tmp->playingSound->volume < limitmax.maxf &&*/
+							!(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))	)
+						{
+							wnr = tmp;
+							/* limitmax.maxf = tmp->playingSound->volume; */
+						}
+						tmp = tmp->next;
+					}
+				}
+				else if (category->maxInstanceBehavior == 4) /* Replace Lowest Priority */
+				{
+					limitmax.maxi = 0xFF;
+					while (tmp != NULL)
+					{
+						if (	tmp != cue &&
+							tmp->playingSound != NULL &&
+							tmp->playingSound->sound->category == categoryIndex &&
+							tmp->playingSound->sound->priority < limitmax.maxi &&
+							!(tmp->state & (FACT_STATE_STOPPING | FACT_STATE_STOPPED))	)
+						{
+							wnr = tmp;
+							limitmax.maxi = tmp->playingSound->sound->priority;
+						}
+						tmp = tmp->next;
+					}
+				}
+				if (wnr != NULL)
+				{
+					fadeInMS = category->fadeInMS;
+					if (wnr->playingSound != NULL)
+					{
+						FACT_INTERNAL_BeginFadeOut(wnr->playingSound, category->fadeOutMS);
+					}
+					else
+					{
+						FACTCue_Stop(wnr, 0);
+					}
+				}
+			}
+			category->instanceCount += 1;
+		}
+
+		newSound = (FACTSoundInstance*) cue->parentBank->parentEngine->pMalloc(
+			sizeof(FACTSoundInstance)
+		);
+		newSound->parentCue = cue;
+		newSound->sound = baseSound;
+		newSound->rpcData.rpcVolume = 0.0f;
+		newSound->rpcData.rpcPitch = 0.0f;
+		newSound->rpcData.rpcReverbSend = 0.0f;
+		newSound->rpcData.rpcFilterQFactor = FAUDIO_DEFAULT_FILTER_ONEOVERQ;
+		newSound->rpcData.rpcFilterFreq = FAUDIO_DEFAULT_FILTER_FREQUENCY;
+		newSound->fadeType = (fadeInMS > 0);
+		if (newSound->fadeType)
+		{
+			newSound->fadeStart = FAudio_timems();
+			newSound->fadeTarget = fadeInMS;
+		}
+		else
+		{
+			newSound->fadeStart = 0;
+			newSound->fadeTarget = 0;
+		}
+		newSound->tracks = (FACTTrackInstance*) cue->parentBank->parentEngine->pMalloc(
+			sizeof(FACTTrackInstance) * newSound->sound->trackCount
+		);
+		for (i = 0; i < newSound->sound->trackCount; i += 1)
+		{
+			newSound->tracks[i].rpcData.rpcVolume = 0.0f;
+			newSound->tracks[i].rpcData.rpcPitch = 0.0f;
+			newSound->tracks[i].rpcData.rpcReverbSend = 0.0f;
+			newSound->tracks[i].rpcData.rpcFilterQFactor = FAUDIO_DEFAULT_FILTER_ONEOVERQ;
+			newSound->tracks[i].rpcData.rpcFilterFreq = FAUDIO_DEFAULT_FILTER_FREQUENCY;
+
+			newSound->tracks[i].evtVolume = 0.0f;
+			newSound->tracks[i].evtPitch = 0.0f;
+
+			newSound->tracks[i].activeWave.wave = NULL;
+			newSound->tracks[i].activeWave.baseVolume = 0.0f;
+			newSound->tracks[i].activeWave.basePitch = 0;
+			newSound->tracks[i].activeWave.baseQFactor = FAUDIO_DEFAULT_FILTER_ONEOVERQ;
+			newSound->tracks[i].activeWave.baseFrequency = FAUDIO_DEFAULT_FILTER_FREQUENCY;
+			newSound->tracks[i].upcomingWave.wave = NULL;
+			newSound->tracks[i].upcomingWave.baseVolume = 0.0f;
+			newSound->tracks[i].upcomingWave.basePitch = 0;
+			newSound->tracks[i].upcomingWave.baseQFactor = FAUDIO_DEFAULT_FILTER_ONEOVERQ;
+			newSound->tracks[i].upcomingWave.baseFrequency = FAUDIO_DEFAULT_FILTER_FREQUENCY;
+
+			newSound->tracks[i].events = (FACTEventInstance*) cue->parentBank->parentEngine->pMalloc(
+				sizeof(FACTEventInstance) * newSound->sound->tracks[i].eventCount
+			);
+			for (j = 0; j < newSound->sound->tracks[i].eventCount; j += 1)
+			{
+				evt = &newSound->sound->tracks[i].events[j];
+
+				newSound->tracks[i].events[j].timestamp =
+					newSound->sound->tracks[i].events[j].timestamp;
+				newSound->tracks[i].events[j].loopCount = 0;
+				newSound->tracks[i].events[j].finished = 0;
+				newSound->tracks[i].events[j].value = 0.0f;
+
+				if (	evt->type == FACTEVENT_PLAYWAVE ||
+					evt->type == FACTEVENT_PLAYWAVETRACKVARIATION ||
+					evt->type == FACTEVENT_PLAYWAVEEFFECTVARIATION ||
+					evt->type == FACTEVENT_PLAYWAVETRACKEFFECTVARIATION	)
+				{
+					newSound->tracks[i].events[j].loopCount =
+						newSound->sound->tracks[i].events[j].wave.loopCount;
+
+					evtInst = &newSound->tracks[i].events[j];
+					if (	!evt->wave.isComplex ||
+						(evt->wave.complex.variation & 0xF) == 0	)
+					{
+						evtInst->valuei = 0;
+					}
+					else
+					{
+						max = 0.0f;
+						for (k = 0; k < evt->wave.complex.trackCount; k += 1)
+						{
+							max += evt->wave.complex.weights[k];
+						}
+						next = FACT_INTERNAL_rng() * max;
+						for (k = evt->wave.complex.trackCount - 1; k >= 0; k -= 1)
+						{
+							if (next > (max - evt->wave.complex.weights[k]))
+							{
+								evtInst->valuei = k;
+								break;
+							}
+							max -= evt->wave.complex.weights[k];
+						}
+					}
+					FACT_INTERNAL_GetNextWave(
+						cue,
+						newSound->sound,
+						&newSound->sound->tracks[i],
+						&newSound->tracks[i],
+						evt,
+						evtInst
+					);
+					newSound->tracks[i].waveEvt = evt;
+					newSound->tracks[i].waveEvtInst = evtInst;
+				}
+				else if (	evt->type == FACTEVENT_PITCHREPEATING ||
+						evt->type == FACTEVENT_VOLUMEREPEATING	)
+				{
+					newSound->tracks[i].events[j].loopCount =
+						newSound->sound->tracks[i].events[j].value.repeats;
+				}
+				else if (evt->type == FACTEVENT_MARKERREPEATING)
+				{
+					newSound->tracks[i].events[j].loopCount =
+						newSound->sound->tracks[i].events[j].marker.repeats;
+				}
+			}
+		}
+
+		/* Calculate Max RPC Release Time */
+		cue->maxRpcReleaseTime = 0;
+		for (i = 0; i < newSound->sound->trackCount; i += 1)
+		{
+			for (j = 0; j < newSound->sound->tracks[i].rpcCodeCount; j += 1)
+			{
+				rpc = FACT_INTERNAL_GetRPC(
+					newSound->parentCue->parentBank->parentEngine,
+					newSound->sound->tracks[i].rpcCodes[j]
+				);
+				if (	rpc->parameter == RPC_PARAMETER_VOLUME &&
+					cue->parentBank->parentEngine->variables[rpc->variable].accessibility & 0x04	)
+				{
+					if (FAudio_strcmp(
+						newSound->parentCue->parentBank->parentEngine->variableNames[rpc->variable],
+						"ReleaseTime"
+					) == 0) {
+						lastX = rpc->points[rpc->pointCount - 1].x;
+						if (lastX > cue->maxRpcReleaseTime)
+						{
+							cue->maxRpcReleaseTime = (uint32_t) lastX /* bleh */;
+						}
+					}
+				}
+			}
+		}
+
+		cue->playingSound = newSound;
+	}
+
+	return 1;
+}
+
+void FACT_INTERNAL_SendCueNotification(FACTCue *cue, FACTNoticationsFlags flag, uint8_t type)
+{
+    if (cue->parentBank->parentEngine->notifications & flag)
+    {
+        FACTNotification note;
+
+        note.type = type;
+        note.pvContext = cue->parentBank->parentEngine->cue_context;
+        note.cue.cueIndex = cue->index;
+        note.cue.pSoundBank = cue->parentBank;
+        note.cue.pCue = cue;
+
+        cue->parentBank->parentEngine->notificationCallback(&note);
+    }
+}
+
+void FACT_INTERNAL_DestroySound(FACTSoundInstance *sound)
+{
+	uint8_t i;
+
+	sound->parentCue->playingSound = NULL;
+	for (i = 0; i < sound->sound->trackCount; i += 1)
+	{
+		if (sound->tracks[i].activeWave.wave != NULL)
+		{
+			FACTWave_Destroy(
+				sound->tracks[i].activeWave.wave
+			);
+		}
+		if (sound->tracks[i].upcomingWave.wave != NULL)
+		{
+			FACTWave_Destroy(
+				sound->tracks[i].upcomingWave.wave
+			);
+		}
+		sound->parentCue->parentBank->parentEngine->pFree(
+			sound->tracks[i].events
+		);
+	}
+	sound->parentCue->parentBank->parentEngine->pFree(sound->tracks);
+
+	if (sound->sound->category != FACTCATEGORY_INVALID)
+	{
+		sound->parentCue->parentBank->parentEngine->categories[
+			sound->sound->category
+		].instanceCount -= 1;
+	}
+
+	/* TODO: if (sound->parentCue->playingSounds == NULL) */
+	{
+		sound->parentCue->state |= FACT_STATE_STOPPED;
+		sound->parentCue->state &= ~(FACT_STATE_PLAYING | FACT_STATE_STOPPING);
+		sound->parentCue->data->instanceCount -= 1;
+
+		FACT_INTERNAL_SendCueNotification(sound->parentCue, NOTIFY_CUESTOP, FACTNOTIFICATIONTYPE_CUESTOP);
+	}
+	sound->parentCue->parentBank->parentEngine->pFree(sound);
+}
+
+void FACT_INTERNAL_BeginFadeOut(FACTSoundInstance *sound, uint16_t fadeOutMS)
+{
+	if (fadeOutMS == 0)
+	{
+		/* No fade? Screw it, just delete us */
+		FACT_INTERNAL_DestroySound(sound);
+		return;
+	}
+
+	sound->fadeType = 2; /* Out */
+	sound->fadeStart = FAudio_timems();
+	sound->fadeTarget = fadeOutMS;
+
+	sound->parentCue->state |= FACT_STATE_STOPPING;
+}
+
+void FACT_INTERNAL_BeginReleaseRPC(FACTSoundInstance *sound, uint16_t releaseMS)
+{
+	if (releaseMS == 0)
+	{
+		/* No release RPC? Screw it, just delete us */
+		FACT_INTERNAL_DestroySound(sound);
+		return;
+	}
+
+	sound->fadeType = 3; /* Release RPC */
+	sound->fadeStart = FAudio_timems();
+	sound->fadeTarget = releaseMS;
+
+	sound->parentCue->state |= FACT_STATE_STOPPING;
+}
+
+/* RPC Helper Functions */
+
+FACTRPC* FACT_INTERNAL_GetRPC(
+	FACTAudioEngine *engine,
+	uint32_t code
+) {
+	uint16_t i;
+	for (i = 0; i < engine->rpcCount; i += 1)
+	{
+		if (engine->rpcCodes[i] == code)
+		{
+			return &engine->rpcs[i];
+		}
+	}
+
+	FAudio_assert(0 && "RPC code not found!");
+	return NULL;
+}
+
+float FACT_INTERNAL_CalculateRPC(
+	FACTRPC *rpc,
+	float var
+) {
+	float result;
+	uint8_t i;
+
+	/* Min/Max */
+	if (var <= rpc->points[0].x)
+	{
+		/* Zero to first defined point */
+		return rpc->points[0].y;
+	}
+	if (var >= rpc->points[rpc->pointCount - 1].x)
+	{
+		/* Last defined point to infinity */
+		return rpc->points[rpc->pointCount - 1].y;
+	}
+
+	/* Something between points */
+	result = 0.0f;
+	for (i = 0; i < rpc->pointCount - 1; i += 1)
+	{
+		/* y = b */
+		result = rpc->points[i].y;
+		if (var >= rpc->points[i].x && var <= rpc->points[i + 1].x)
+		{
+			const float maxX = rpc->points[i + 1].x - rpc->points[i].x;
+			const float maxY = rpc->points[i + 1].y - rpc->points[i].y;
+			const float deltaX = (var - rpc->points[i].x);
+			const float deltaXNormalized = deltaX / maxX;
+
+			if (rpc->points[i].type == 0) /* Linear */
+			{
+				result += maxY * deltaXNormalized;
+			}
+			else if (rpc->points[i].type == 1) /* Fast */
+			{
+				result += maxY * (1.0f - FAudio_pow(1.0f - FAudio_pow(deltaXNormalized, 1.0f / 1.5f), 1.5f));
+			}
+			else if (rpc->points[i].type == 2) /* Slow */
+			{
+				result += maxY * (1.0f - FAudio_pow(1.0f - FAudio_pow(deltaXNormalized, 1.5f), 1.0f / 1.5f));
+			}
+			else if (rpc->points[i].type == 3) /* SinCos */
+			{
+				if (maxY > 0.0f)
+				{
+					result += maxY * (1.0f - FAudio_pow(1.0f - FAudio_sqrtf(deltaXNormalized), 2.0f));
+				}
+				else
+				{
+					result += maxY * (1.0f - FAudio_sqrtf(1.0f - FAudio_pow(deltaXNormalized, 2.0f)));
+				}
+			}
+			else
+			{
+				FAudio_assert(0 && "Unrecognized curve type!");
+			}
+
+			break;
+		}
+	}
+	return result;
+}
+
+void FACT_INTERNAL_UpdateRPCs(
+	FACTCue *cue,
+	uint8_t codeCount,
+	uint32_t *codes,
+	FACTInstanceRPCData *data,
+	uint32_t timestamp,
+	uint32_t elapsedTrack
+) {
+	uint8_t i;
+	FACTRPC *rpc;
+	float rpcResult;
+	float variableValue;
+	FACTAudioEngine *engine = cue->parentBank->parentEngine;
+
+	if (codeCount > 0)
+	{
+		/* Do NOT overwrite Frequency/QFactor! */
+		data->rpcVolume = 0.0f;
+		data->rpcPitch = 0.0f;
+		data->rpcReverbSend = 0.0f;
+		for (i = 0; i < codeCount; i += 1)
+		{
+			rpc = FACT_INTERNAL_GetRPC(
+				engine,
+				codes[i]
+			);
+			if (engine->variables[rpc->variable].accessibility & 0x04)
+			{
+				if (FAudio_strcmp(
+					engine->variableNames[rpc->variable],
+					"AttackTime"
+				) == 0) {
+					variableValue = (float) elapsedTrack;
+				}
+				else if (FAudio_strcmp(
+					engine->variableNames[rpc->variable],
+					"ReleaseTime"
+				) == 0) {
+					if (cue->playingSound->fadeType == 3) /* Release RPC */
+					{
+						variableValue = (float) (timestamp - cue->playingSound->fadeStart);
+					}
+					else
+					{
+						variableValue = 0.0f;
+					}
+				}
+				else
+				{
+					variableValue = cue->variableValues[rpc->variable];
+				}
+
+				rpcResult = FACT_INTERNAL_CalculateRPC(
+					rpc,
+					variableValue
+				);
+			}
+			else
+			{
+				rpcResult = FACT_INTERNAL_CalculateRPC(
+					rpc,
+					engine->globalVariableValues[rpc->variable]
+				);
+			}
+			if (rpc->parameter == RPC_PARAMETER_VOLUME)
+			{
+				data->rpcVolume += rpcResult;
+			}
+			else if (rpc->parameter == RPC_PARAMETER_PITCH)
+			{
+				data->rpcPitch += rpcResult;
+			}
+			else if (rpc->parameter == RPC_PARAMETER_REVERBSEND)
+			{
+				data->rpcReverbSend += rpcResult;
+			}
+			else if (rpc->parameter == RPC_PARAMETER_FILTERFREQUENCY)
+			{
+				/* Yes, just overwrite... */
+				data->rpcFilterFreq = FACT_INTERNAL_CalculateFilterFrequency(
+					rpcResult,
+					engine->audio->master->master.inputSampleRate
+				);
+			}
+			else if (rpc->parameter == RPC_PARAMETER_FILTERQFACTOR)
+			{
+				/* Yes, just overwrite... */
+				data->rpcFilterQFactor = 1.0f / rpcResult;
+			}
+			else
+			{
+				FAudio_assert(0 && "Unhandled RPC parameter type!");
+			}
+		}
+	}
+}
+
+/* Engine Update Function */
+
+void FACT_INTERNAL_UpdateEngine(FACTAudioEngine *engine)
+{
+	FAudioFXReverbParameters rvbPar;
+	uint16_t i, j, par;
+	float rpcResult;
+	for (i = 0; i < engine->rpcCount; i += 1)
+	{
+		if (engine->rpcs[i].parameter >= RPC_PARAMETER_COUNT)
+		{
+			/* FIXME: Why did I make this global vars only...? */
+			if (!(engine->variables[engine->rpcs[i].variable].accessibility & 0x04))
+			{
+				for (j = 0; j < engine->dspPresetCount; j += 1)
+				{
+					/* FIXME: This affects all DSP presets!
+					 * What if there's more than one?
+					 */
+					par = engine->rpcs[i].parameter - RPC_PARAMETER_COUNT;
+					rpcResult = FACT_INTERNAL_CalculateRPC(
+						&engine->rpcs[i],
+						engine->globalVariableValues[engine->rpcs[i].variable]
+					);
+					engine->dspPresets[j].parameters[par].value = FAudio_clamp(
+						rpcResult,
+						engine->dspPresets[j].parameters[par].minVal,
+						engine->dspPresets[j].parameters[par].maxVal
+					);
+				}
+			}
+		}
+	}
+
+	/* Set Effect parameters from above RPC changes */
+	if (engine->reverbVoice != NULL)
+	{
+		rvbPar.WetDryMix = engine->dspPresets[0].parameters[21].value;
+		rvbPar.ReflectionsDelay = (uint32_t) engine->dspPresets[0].parameters[0].value;
+		rvbPar.ReverbDelay = (uint8_t) engine->dspPresets[0].parameters[1].value;
+		rvbPar.RearDelay = (uint8_t) engine->dspPresets[0].parameters[12].value;
+		rvbPar.PositionLeft = (uint8_t) engine->dspPresets[0].parameters[2].value;
+		rvbPar.PositionRight = (uint8_t) engine->dspPresets[0].parameters[3].value;
+		rvbPar.PositionMatrixLeft = (uint8_t) engine->dspPresets[0].parameters[4].value;
+		rvbPar.PositionMatrixRight = (uint8_t) engine->dspPresets[0].parameters[5].value;
+		rvbPar.HighEQGain = (uint8_t) engine->dspPresets[0].parameters[10].value;
+		rvbPar.LowEQCutoff = (uint8_t) engine->dspPresets[0].parameters[9].value;
+		rvbPar.LowEQGain = (uint8_t) engine->dspPresets[0].parameters[8].value;
+		rvbPar.LateDiffusion = (uint8_t) engine->dspPresets[0].parameters[7].value;
+		rvbPar.EarlyDiffusion = (uint8_t) engine->dspPresets[0].parameters[6].value;
+		rvbPar.HighEQCutoff = (uint8_t) engine->dspPresets[0].parameters[11].value;
+		rvbPar.RoomFilterMain = engine->dspPresets[0].parameters[14].value;
+		rvbPar.RoomFilterFreq = engine->dspPresets[0].parameters[13].value;
+		rvbPar.RoomFilterHF = engine->dspPresets[0].parameters[15].value;
+		rvbPar.ReflectionsGain = engine->dspPresets[0].parameters[16].value;
+		rvbPar.ReverbGain = engine->dspPresets[0].parameters[17].value;
+		rvbPar.DecayTime = engine->dspPresets[0].parameters[18].value;
+		rvbPar.Density = engine->dspPresets[0].parameters[19].value;
+		rvbPar.RoomSize = engine->dspPresets[0].parameters[20].value;
+
+		FAudioVoice_SetEffectParameters(
+			engine->reverbVoice,
+			0,
+			&rvbPar,
+			sizeof(FAudioFXReverbParameters),
+			0
+		);
+	}
+}
+
+/* Cue Update Functions */
+
+static inline void FACT_INTERNAL_StopTrack(
+	FACTTrack *track,
+	FACTTrackInstance *trackInst,
+	uint8_t immediate
+) {
+	uint8_t i;
+
+	/* Stop the wave (may as-authored or immedate */
+	if (trackInst->activeWave.wave != NULL)
+	{
+		FACTWave_Stop(
+			trackInst->activeWave.wave,
+			immediate
+		);
+	}
+
+	/* If there was another sound coming, it ain't now! */
+	if (trackInst->upcomingWave.wave != NULL)
+	{
+		FACTWave_Destroy(trackInst->upcomingWave.wave);
+		trackInst->upcomingWave.wave = NULL;
+	}
+
+	/* Kill the loop count too */
+	for (i = 0; i < track->eventCount; i += 1)
+	{
+		trackInst->events[i].loopCount = 0;
+		trackInst->events[i].finished = 1;
+	}
+}
+
+void FACT_INTERNAL_ActivateEvent(
+	FACTSoundInstance *sound,
+	FACTTrack *track,
+	FACTTrackInstance *trackInst,
+	FACTEvent *evt,
+	FACTEventInstance *evtInst,
+	uint32_t elapsed
+) {
+	uint8_t i;
+	float svResult;
+	uint8_t skipLoopCheck = 0;
+
+	/* STOP */
+	if (evt->type == FACTEVENT_STOP)
+	{
+		/* Stop Cue */
+		if (evt->stop.flags & 0x02)
+		{
+			if (	evt->stop.flags & 0x01 ||
+				(	sound->parentCue->parentBank->cues[sound->parentCue->index].fadeOutMS == 0 &&
+					sound->parentCue->maxRpcReleaseTime == 0	)	)
+			{
+				for (i = 0; i < sound->sound->trackCount; i += 1)
+				{
+					FACT_INTERNAL_StopTrack(
+						&sound->sound->tracks[i],
+						&sound->tracks[i],
+						1
+					);
+				}
+			}
+			else
+			{
+				if (sound->parentCue->parentBank->cues[sound->parentCue->index].fadeOutMS > 0)
+				{
+					FACT_INTERNAL_BeginFadeOut(
+						sound,
+						sound->parentCue->parentBank->cues[sound->parentCue->index].fadeOutMS
+					);
+				}
+				else if (sound->parentCue->maxRpcReleaseTime > 0)
+				{
+					FACT_INTERNAL_BeginReleaseRPC(
+						sound,
+						sound->parentCue->maxRpcReleaseTime
+					);
+				}
+				else
+				{
+					/* Pretty sure this doesn't happen, but just in case? */
+					sound->parentCue->state |= FACT_STATE_STOPPING;
+				}
+			}
+		}
+
+		/* Stop track */
+		else
+		{
+			FACT_INTERNAL_StopTrack(
+				track,
+				trackInst,
+				evt->stop.flags & 0x01
+			);
+		}
+	}
+
+	/* PLAYWAVE */
+	else if (	evt->type == FACTEVENT_PLAYWAVE ||
+			evt->type == FACTEVENT_PLAYWAVETRACKVARIATION ||
+			evt->type == FACTEVENT_PLAYWAVEEFFECTVARIATION ||
+			evt->type == FACTEVENT_PLAYWAVETRACKEFFECTVARIATION	)
+	{
+		FAudio_assert(trackInst->activeWave.wave == NULL);
+		FAudio_assert(trackInst->upcomingWave.wave != NULL);
+		FAudio_memcpy(
+			&trackInst->activeWave,
+			&trackInst->upcomingWave,
+			sizeof(trackInst->activeWave)
+		);
+		trackInst->upcomingWave.wave = NULL;
+		FACTWave_Play(trackInst->activeWave.wave);
+	}
+
+	/* SETVALUE */
+	else if (	evt->type == FACTEVENT_PITCH ||
+			evt->type == FACTEVENT_PITCHREPEATING ||
+			evt->type == FACTEVENT_VOLUME ||
+			evt->type == FACTEVENT_VOLUMEREPEATING	)
+	{
+		/* Ramp/Equation */
+		if (evt->value.settings & 0x01)
+		{
+			/* FIXME: Incorporate 2nd derivative into the interpolated pitch (slopeDelta) */
+			skipLoopCheck = elapsed <= (evtInst->timestamp + evt->value.ramp.duration);
+			svResult = (
+				evt->value.ramp.initialSlope *
+				evt->value.ramp.duration / 1000 *
+				10 /* "Slices" */
+			) + evt->value.ramp.initialValue;
+			svResult = (
+				(svResult - evt->value.ramp.initialValue)
+			) * FAudio_clamp(
+				(float) (elapsed - evtInst->timestamp) / evt->value.ramp.duration,
+				0.0f,
+				1.0f
+			) + evt->value.ramp.initialValue;
+
+			evtInst->value = svResult;
+		}
+		else
+		{
+			/* Value/Random */
+			if (evt->value.equation.flags & 0x04)
+			{
+				svResult = evt->value.equation.value1;
+			}
+			else if (evt->value.equation.flags & 0x08)
+			{
+				svResult = evt->value.equation.value1 + FACT_INTERNAL_rng() * (
+					evt->value.equation.value2 -
+					evt->value.equation.value1
+				);
+			}
+			else
+			{
+				svResult = 0.0f;
+				FAudio_assert(0 && "Equation flags?");
+			}
+
+			/* Add/Replace */
+			if (evt->value.equation.flags & 0x01)
+			{
+				if(	evt->type == FACTEVENT_PITCH ||
+					evt->type == FACTEVENT_PITCHREPEATING	)
+				{
+					evtInst->value = trackInst->evtPitch + svResult;
+				}
+				else
+				{
+					evtInst->value = trackInst->evtVolume + svResult;
+				}
+			}
+			else
+			{
+				evtInst->value = svResult;
+			}
+		}
+
+		/* Set the result, finally. */
+		if (	evt->type == FACTEVENT_PITCH ||
+			evt->type == FACTEVENT_PITCHREPEATING	)
+		{
+			trackInst->evtPitch = evtInst->value;
+		}
+		else
+		{
+			trackInst->evtVolume = evtInst->value;
+		}
+
+		if (skipLoopCheck)
+		{
+			return;
+		}
+		if (evtInst->loopCount > 0)
+		{
+			if (evtInst->loopCount != 0xFF && evtInst->loopCount != 0xFFFF)
+			{
+				evtInst->loopCount -= 1;
+			}
+
+			evtInst->timestamp += evt->value.frequency;
+			return;
+		}
+	}
+
+	/* MARKER */
+	else if (	evt->type == FACTEVENT_MARKER ||
+			evt->type == FACTEVENT_MARKERREPEATING	)
+	{
+		/* TODO: FACT_INTERNAL_Marker(evt->marker*) */
+		if (evtInst->loopCount > 0)
+		{
+			if (evtInst->loopCount != 0xFF)
+			{
+				evtInst->loopCount -= 1;
+			}
+
+			evtInst->timestamp += evt->marker.frequency;
+			return;
+		}
+	}
+
+	/* ??? */
+	else
+	{
+		FAudio_assert(0 && "Unknown event type!");
+	}
+
+	/* If we made it here, we're done! */
+	evtInst->finished = 1;
+}
+
+uint8_t FACT_INTERNAL_UpdateSound(FACTSoundInstance *sound, uint32_t timestamp)
+{
+	uint8_t i, j;
+	uint32_t waveState;
+	uint32_t elapsedCue;
+	FACTEventInstance *evtInst;
+	FAudioFilterParameters filterParams;
+	uint8_t finished = 1;
+
+	/* Instance limiting Fade in/out */
+	float fadeVolume;
+	if (sound->fadeType == 1) /* Fade In */
+	{
+		if ((timestamp - sound->fadeStart) >= sound->fadeTarget)
+		{
+			/* We've faded in! */
+			fadeVolume = 1.0f;
+			sound->fadeStart = 0;
+			sound->fadeTarget = 0;
+			sound->fadeType = 0;
+		}
+		else
+		{
+			fadeVolume = (
+				(float) (timestamp - sound->fadeStart) /
+				(float) sound->fadeTarget
+			);
+		}
+	}
+	else if (sound->fadeType == 2) /* Fade Out */
+	{
+		if ((timestamp - sound->fadeStart) >= sound->fadeTarget)
+		{
+			/* We've faded out! */
+			return 1;
+		}
+		fadeVolume = 1.0f - (
+			(float) (timestamp - sound->fadeStart) /
+			(float) sound->fadeTarget
+		);
+	}
+	else if (sound->fadeType == 3) /* Release RPC */
+	{
+		if ((timestamp - sound->fadeStart) >= sound->fadeTarget)
+		{
+			/* We've faded out! */
+			return 1;
+		}
+		fadeVolume = 1.0f;
+	}
+	else
+	{
+		fadeVolume = 1.0f;
+	}
+
+	/* To get the time on a single Cue, subtract from the global time
+	 * the latest start time minus the total time elapsed (minus pause time)
+	 */
+	elapsedCue = timestamp - (sound->parentCue->start - sound->parentCue->elapsed);
+
+	/* RPC updates */
+	sound->rpcData.rpcFilterFreq = -1.0f;
+	sound->rpcData.rpcFilterQFactor = -1.0f;
+	FACT_INTERNAL_UpdateRPCs(
+		sound->parentCue,
+		sound->sound->rpcCodeCount,
+		sound->sound->rpcCodes,
+		&sound->rpcData,
+		timestamp,
+		elapsedCue - sound->tracks[0].events[0].timestamp
+	);
+	for (i = 0; i < sound->sound->trackCount; i += 1)
+	{
+		sound->tracks[i].rpcData.rpcFilterFreq = sound->rpcData.rpcFilterFreq;
+		sound->tracks[i].rpcData.rpcFilterQFactor = sound->rpcData.rpcFilterQFactor;
+		FACT_INTERNAL_UpdateRPCs(
+			sound->parentCue,
+			sound->sound->tracks[i].rpcCodeCount,
+			sound->sound->tracks[i].rpcCodes,
+			&sound->tracks[i].rpcData,
+			timestamp,
+			elapsedCue - sound->sound->tracks[i].events[0].timestamp
+		);
+	}
+
+	/* Go through each event for each track */
+	for (i = 0; i < sound->sound->trackCount; i += 1)
+	{
+		/* Event updates */
+		for (j = 0; j < sound->sound->tracks[i].eventCount; j += 1)
+		{
+			evtInst = &sound->tracks[i].events[j];
+			if (!evtInst->finished)
+			{
+				/* Cue's not done yet...! */
+				finished = 0;
+
+				/* Trigger events at the right time */
+				if (elapsedCue >= evtInst->timestamp)
+				{
+					FACT_INTERNAL_ActivateEvent(
+						sound,
+						&sound->sound->tracks[i],
+						&sound->tracks[i],
+						&sound->sound->tracks[i].events[j],
+						evtInst,
+						elapsedCue
+					);
+				}
+			}
+		}
+
+		/* Wave updates */
+		if (sound->tracks[i].activeWave.wave == NULL)
+		{
+			continue;
+		}
+		finished = 0;
+
+		/* Clear out Waves as they finish */
+		FACTWave_GetState(
+			sound->tracks[i].activeWave.wave,
+			&waveState
+		);
+		if (waveState & FACT_STATE_STOPPED)
+		{
+			FACTWave_Destroy(sound->tracks[i].activeWave.wave);
+			FAudio_memcpy(
+				&sound->tracks[i].activeWave,
+				&sound->tracks[i].upcomingWave,
+				sizeof(sound->tracks[i].activeWave)
+			);
+			sound->tracks[i].upcomingWave.wave = NULL;
+			if (sound->tracks[i].activeWave.wave == NULL)
+			{
+				continue;
+			}
+			FACTWave_Play(sound->tracks[i].activeWave.wave);
+		}
+
+		FACTWave_SetVolume(
+			sound->tracks[i].activeWave.wave,
+			FACT_INTERNAL_CalculateAmplitudeRatio(
+				sound->tracks[i].activeWave.baseVolume +
+				sound->rpcData.rpcVolume +
+				sound->tracks[i].rpcData.rpcVolume +
+				sound->tracks[i].evtVolume
+			) * sound->parentCue->parentBank->parentEngine->categories[
+				sound->sound->category
+			].currentVolume *
+			fadeVolume
+		);
+		FACTWave_SetPitch(
+			sound->tracks[i].activeWave.wave,
+			(int16_t) (
+				sound->tracks[i].activeWave.basePitch +
+				sound->rpcData.rpcPitch +
+				sound->tracks[i].rpcData.rpcPitch +
+				sound->tracks[i].evtPitch
+			)
+		);
+		if (sound->sound->tracks[i].filter != 0xFF)
+		{
+			/* FIXME: From what I can gather, filter parameters get
+			 * overwritten by the RPC value if a filter RPC exists.
+			 * Priority is Sound < Sound RPC < Track RPC, I think?
+			 */
+			filterParams.Type = (FAudioFilterType) sound->sound->tracks[i].filter;
+			if (sound->tracks[i].rpcData.rpcFilterFreq >= 0.0f)
+			{
+				filterParams.Frequency = sound->tracks[i].rpcData.rpcFilterFreq;
+			}
+			else
+			{
+				filterParams.Frequency = sound->tracks[i].activeWave.baseFrequency;
+			}
+			if (sound->tracks[i].rpcData.rpcFilterQFactor >= 0.0f)
+			{
+				filterParams.OneOverQ = sound->tracks[i].rpcData.rpcFilterQFactor;
+			}
+			else
+			{
+				filterParams.OneOverQ = sound->tracks[i].activeWave.baseQFactor;
+			}
+			FAudioVoice_SetFilterParameters(
+				sound->tracks[i].activeWave.wave->voice,
+				&filterParams,
+				0
+			);
+		}
+		/* TODO: Wave updates:
+		 * - ReverbSend (SetOutputMatrix on index 1, submix voice)
+		 */
+	}
+
+	return finished;
+}
+
+void FACT_INTERNAL_UpdateCue(FACTCue *cue)
+{
+	uint32_t i;
+	float next;
+	FACTSoundInstance *sound;
+
+	/* Interactive sound selection */
+	if (!(cue->data->flags & 0x04) && cue->variation->flags == 3)
+	{
+		/* Interactive */
+		if (cue->parentBank->parentEngine->variables[cue->variation->variable].accessibility & 0x04)
+		{
+			FACTCue_GetVariable(
+				cue,
+				cue->variation->variable,
+				&next
+			);
+		}
+		else
+		{
+			FACTAudioEngine_GetGlobalVariable(
+				cue->parentBank->parentEngine,
+				cue->variation->variable,
+				&next
+			);
+		}
+		if (next != cue->interactive)
+		{
+			cue->interactive = next;
+
+			/* New sound, time for death! */
+			if (cue->playingSound != NULL)
+			{
+				/* Copy of DestroySound but does not set Cue to STOPPED */
+				sound = cue->playingSound;
+				sound->parentCue->playingSound = NULL;
+				for (i = 0; i < sound->sound->trackCount; i += 1)
+				{
+					if (sound->tracks[i].activeWave.wave != NULL)
+					{
+						FACTWave_Destroy(
+							sound->tracks[i].activeWave.wave
+						);
+					}
+					if (sound->tracks[i].upcomingWave.wave != NULL)
+					{
+						FACTWave_Destroy(
+							sound->tracks[i].upcomingWave.wave
+						);
+					}
+					cue->parentBank->parentEngine->pFree(
+						sound->tracks[i].events
+					);
+				}
+				cue->parentBank->parentEngine->pFree(sound->tracks);
+
+				if (sound->sound->category != FACTCATEGORY_INVALID)
+				{
+					sound->parentCue->parentBank->parentEngine->categories[
+						sound->sound->category
+					].instanceCount -= 1;
+				}
+			}
+
+			/* TODO: Reset cue times? Transition tables...?
+			cue->start = elapsed;
+			cue->elapsed = 0;
+			 */
+
+			FACT_INTERNAL_CreateSound(cue, 0 /* fadeIn */);
+		}
+	}
+}
+
+/* FACT Thread */
+
+int32_t FACT_INTERNAL_APIThread(void* enginePtr)
+{
+	FACTAudioEngine *engine = (FACTAudioEngine*) enginePtr;
+	LinkedList *sbList;
+	FACTCue *cue, *cBackup;
+	uint32_t timestamp, updateTime;
+
+	/* Needs to match the audio thread priority, or else the scheduler will
+	 * let this thread sit around with a lock while the audio thread spins
+	 * infinitely!
+	 */
+	FAudio_PlatformThreadPriority(FAUDIO_THREAD_PRIORITY_HIGH);
+
+threadstart:
+	FAudio_PlatformLockMutex(engine->apiLock);
+
+	/* We want the timestamp to be uniform across all Cues.
+	 * Oftentimes many Cues are played at once with the expectation
+	 * that they will sync, so give them all the same timestamp
+	 * so all the various actions will go together even if it takes
+	 * an extra millisecond to get through the whole Cue list.
+	 */
+	timestamp = FAudio_timems();
+
+	FACT_INTERNAL_UpdateEngine(engine);
+
+	sbList = engine->sbList;
+	while (sbList != NULL)
+	{
+		cue = ((FACTSoundBank*) sbList->entry)->cueList;
+		while (cue != NULL)
+		{
+			FACT_INTERNAL_UpdateCue(cue);
+
+			if (cue->state & FACT_STATE_PAUSED)
+			{
+				cue = cue->next;
+				continue;
+			}
+
+			if (cue->playingSound != NULL)
+			{
+				if (FACT_INTERNAL_UpdateSound(cue->playingSound, timestamp))
+				{
+					FACT_INTERNAL_DestroySound(cue->playingSound);
+				}
+			}
+
+			/* Destroy if it's done and not user-handled. */
+			if (cue->managed && (cue->state & FACT_STATE_STOPPED))
+			{
+				cBackup = cue->next;
+				FACTCue_Destroy(cue);
+				cue = cBackup;
+			}
+			else
+			{
+				cue = cue->next;
+			}
+		}
+		sbList = sbList->next;
+	}
+
+	FAudio_PlatformUnlockMutex(engine->apiLock);
+
+	if (engine->initialized)
+	{
+		/* FIXME: 10ms is based on the XAudio2 update time...? */
+		updateTime = FAudio_timems() - timestamp;
+		if (updateTime < 10)
+		{
+			FAudio_sleep(10 - updateTime);
+		}
+		goto threadstart;
+	}
+
+	return 0;
+}
+
+/* FAudio callbacks */
+
+void FACT_INTERNAL_OnBufferEnd(FAudioVoiceCallback *callback, void* pContext)
+{
+	FAudioBuffer buffer;
+	FAudioBufferWMA bufferWMA;
+	FACTWaveCallback *c = (FACTWaveCallback*) callback;
+	FACTWaveBankEntry *entry;
+	uint32_t end, left, length;
+
+	entry = &c->wave->parentBank->entries[c->wave->index];
+
+	/* Calculate total bytes left in this wave iteration */
+	if (c->wave->loopCount > 0 && entry->LoopRegion.dwTotalSamples > 0)
+	{
+		length = entry->LoopRegion.dwStartSample + entry->LoopRegion.dwTotalSamples;
+		if (entry->Format.wFormatTag == 0x0)
+		{
+			length = (
+				length *
+				entry->Format.nChannels *
+				(1 << entry->Format.wBitsPerSample)
+			);
+		}
+		else if (entry->Format.wFormatTag == 0x2)
+		{
+			length = (
+				length /
+				/* wSamplesPerBlock */
+				((entry->Format.wBlockAlign + 16) * 2) *
+				/* nBlockAlign */
+				((entry->Format.wBlockAlign + 22) * entry->Format.nChannels)
+			);
+		}
+		else
+		{
+			length = entry->PlayRegion.dwLength;
+		}
+	}
+	else
+	{
+		length = entry->PlayRegion.dwLength;
+	}
+	end = entry->PlayRegion.dwOffset + length;
+	left = length - (c->wave->streamOffset - entry->PlayRegion.dwOffset);
+
+	/* Don't bother if we're EOS or the Wave has stopped */
+	if (	(c->wave->streamOffset >= end) ||
+		(c->wave->state & FACT_STATE_STOPPED)	)
+	{
+		return;
+	}
+
+	/* Assign buffer memory */
+	buffer.pAudioData = c->wave->streamCache;
+	buffer.AudioBytes = FAudio_min(
+		c->wave->streamSize,
+		left
+	);
+
+	/* Read! */
+	FACT_INTERNAL_ReadFile(
+		c->wave->parentBank->parentEngine->pReadFile,
+		c->wave->parentBank->parentEngine->pGetOverlappedResult,
+		c->wave->parentBank->io,
+		c->wave->streamOffset,
+		c->wave->parentBank->packetSize,
+		&c->wave->parentBank->packetBuffer,
+		&c->wave->parentBank->packetBufferLen,
+		c->wave->parentBank->parentEngine->pRealloc,
+		c->wave->streamCache,
+		buffer.AudioBytes
+	);
+	c->wave->streamOffset += buffer.AudioBytes;
+
+	/* Last buffer in the stream? */
+	buffer.Flags = 0;
+	if (c->wave->streamOffset >= end)
+	{
+		/* Loop if applicable */
+		if (c->wave->loopCount > 0)
+		{
+			if (c->wave->loopCount != 255)
+			{
+				c->wave->loopCount -= 1;
+			}
+			c->wave->streamOffset = entry->PlayRegion.dwOffset;
+
+			/* Loop start */
+			if (entry->Format.wFormatTag == 0x0)
+			{
+				c->wave->streamOffset += (
+					entry->LoopRegion.dwStartSample *
+					entry->Format.nChannels *
+					(1 << entry->Format.wBitsPerSample)
+				);
+			}
+			else if (entry->Format.wFormatTag == 0x2)
+			{
+				c->wave->streamOffset += (
+					entry->LoopRegion.dwStartSample /
+					/* wSamplesPerBlock */
+					((entry->Format.wBlockAlign + 16) * 2) *
+					/* nBlockAlign */
+					((entry->Format.wBlockAlign + 22) * entry->Format.nChannels)
+				);
+			}
+		}
+		else
+		{
+			buffer.Flags = FAUDIO_END_OF_STREAM;
+		}
+	}
+
+	/* Unused properties */
+	buffer.PlayBegin = 0;
+	buffer.PlayLength = 0;
+	buffer.LoopBegin = 0;
+	buffer.LoopLength = 0;
+	buffer.LoopCount = 0;
+	buffer.pContext = NULL;
+
+	/* Submit, finally. */
+	if (entry->Format.wFormatTag == 0x3)
+	{
+		bufferWMA.pDecodedPacketCumulativeBytes =
+			c->wave->parentBank->seekTables[c->wave->index].entries;
+		bufferWMA.PacketCount =
+			c->wave->parentBank->seekTables[c->wave->index].entryCount;
+		FAudioSourceVoice_SubmitSourceBuffer(
+			c->wave->voice,
+			&buffer,
+			&bufferWMA
+		);
+	}
+	else
+	{
+		FAudioSourceVoice_SubmitSourceBuffer(
+			c->wave->voice,
+			&buffer,
+			NULL
+		);
+	}
+}
+
+void FACT_INTERNAL_OnStreamEnd(FAudioVoiceCallback *callback)
+{
+	FACTWaveCallback *c = (FACTWaveCallback*) callback;
+
+	c->wave->state = FACT_STATE_STOPPED;
+
+	if (	c->wave->parentCue != NULL &&
+		c->wave->parentCue->simpleWave == c->wave	)
+	{
+		c->wave->parentCue->state |= FACT_STATE_STOPPED;
+		c->wave->parentCue->state &= ~(
+			FACT_STATE_PLAYING |
+			FACT_STATE_STOPPING
+		);
+		c->wave->parentCue->data->instanceCount -= 1;
+	}
+}
+
+/* FAudioIOStream functions */
+
+int32_t FACTCALL FACT_INTERNAL_DefaultReadFile(
+	void *hFile,
+	void *buffer,
+	uint32_t nNumberOfBytesToRead,
+	uint32_t *lpNumberOfBytesRead, /* Not referenced! */
+	FACTOverlapped *lpOverlapped
+) {
+	FAudioIOStream *io = (FAudioIOStream*) hFile;
+	lpOverlapped->Internal = (void*) 0x00000103; /* STATUS_PENDING */
+	FAudio_PlatformLockMutex((FAudioMutex) io->lock);
+	io->seek(io->data, (size_t) lpOverlapped->Pointer, FAUDIO_SEEK_SET);
+	lpOverlapped->InternalHigh = (void*) (size_t) (io->read(
+		io->data,
+		buffer,
+		nNumberOfBytesToRead,
+		1
+	) * nNumberOfBytesToRead);
+	FAudio_PlatformUnlockMutex((FAudioMutex) io->lock);
+	lpOverlapped->Internal = 0; /* STATUS_SUCCESS */
+	return 1;
+}
+
+int32_t FACTCALL FACT_INTERNAL_DefaultGetOverlappedResult(
+	void *hFile,
+	FACTOverlapped *lpOverlapped,
+	uint32_t *lpNumberOfBytesTransferred,
+	int32_t bWait
+) {
+	*lpNumberOfBytesTransferred = (uint32_t) (size_t) lpOverlapped->InternalHigh;
+	return 1;
+}
+
+/* Parsing functions */
+
+#define READ_FUNC(type, size, bitsize, suffix) \
+	static inline type read_##suffix(uint8_t **ptr, const uint8_t swapendian) \
+	{ \
+		type result = *((type*) *ptr); \
+		*ptr += size; \
+		return swapendian ? \
+			FAudio_swap##bitsize##BE(result) : \
+			FAudio_swap##bitsize##LE(result); \
+	}
+
+static inline uint8_t read_u8(uint8_t **ptr)
+{
+	uint8_t result = *((uint8_t*) *ptr);
+	*ptr += 1;
+	return result;
+}
+READ_FUNC(uint16_t, 2, 16, u16)
+READ_FUNC(uint32_t, 4, 32, u32)
+READ_FUNC(int16_t, 2, 16, s16)
+READ_FUNC(int32_t, 4, 32, s32)
+static inline float read_f32(uint8_t **ptr, const uint8_t swapendian)
+{
+	float result = *((float*) *ptr);
+	*ptr += 4;
+	return result;
+}
+
+#undef READ_FUNC
+
+static inline float read_volbyte(uint8_t **ptr)
+{
+	/* FIXME: This magnificent beauty came from Mathematica!
+	 * The byte values for all possible input dB values from the .xap are here:
+	 * http://www.flibitijibibo.com/XACTVolume.txt
+	 * Yes, this is actually what the XACT builder really does.
+	 *
+	 * Thanks to Kenny for plotting all that data.
+	 * -flibit
+	 */
+	return (float) ((3969.0 * FAudio_log10(read_u8(ptr) / 28240.0)) + 8715.0);
+}
+
+uint32_t FACT_INTERNAL_ParseAudioEngine(
+	FACTAudioEngine *pEngine,
+	const FACTRuntimeParameters *pParams
+) {
+	uint32_t	categoryOffset,
+			variableOffset,
+			blob1Offset,
+			categoryNameIndexOffset,
+			blob2Offset,
+			variableNameIndexOffset,
+			categoryNameOffset,
+			variableNameOffset,
+			rpcOffset,
+			dspPresetOffset,
+			dspParameterOffset;
+	uint16_t blob1Count, blob2Count;
+	uint8_t version, tool;
+	uint8_t se;
+	uint32_t magic;
+	size_t memsize;
+	uint16_t i, j;
+
+	uint8_t *ptr = (uint8_t*) pParams->pGlobalSettingsBuffer;
+	uint8_t *start = ptr;
+
+	magic = read_u32(&ptr, 0);
+	se = magic == 0x58475346; /* Swap Endian */
+	if (magic != 0x46534758 && magic != 0x58475346) /* 'XGSF' */
+	{
+		return -1; /* TODO: NOT XACT FILE */
+	}
+
+	if (!FACT_INTERNAL_SupportedContent(read_u16(&ptr, se)))
+	{
+		return -2;
+	}
+
+	tool = read_u16(&ptr, se); /* Tool version */
+	if (tool != 42)
+	{
+		return -3;
+	}
+
+	ptr += 2; /* Unknown value */
+
+	/* Last modified, unused */
+	ptr += 8;
+
+	/* XACT Version (Windows == 3, Xbox == 7) */
+	version = read_u8(&ptr);
+	if (	version != 3 &&
+		version != 7	)
+	{
+		return -4; /* TODO: VERSION TOO OLD */
+	}
+
+	/* Object counts */
+	pEngine->categoryCount = read_u16(&ptr, se);
+	pEngine->variableCount = read_u16(&ptr, se);
+	blob1Count = read_u16(&ptr, se);
+	blob2Count = read_u16(&ptr, se);
+	pEngine->rpcCount = read_u16(&ptr, se);
+	pEngine->dspPresetCount = read_u16(&ptr, se);
+	pEngine->dspParameterCount = read_u16(&ptr, se);
+
+	/* Object offsets */
+	categoryOffset = read_u32(&ptr, se);
+	variableOffset = read_u32(&ptr, se);
+	blob1Offset = read_u32(&ptr, se);
+	categoryNameIndexOffset = read_u32(&ptr, se);
+	blob2Offset = read_u32(&ptr, se);
+	variableNameIndexOffset = read_u32(&ptr, se);
+	categoryNameOffset = read_u32(&ptr, se);
+	variableNameOffset = read_u32(&ptr, se);
+	rpcOffset = read_u32(&ptr, se);
+	dspPresetOffset = read_u32(&ptr, se);
+	dspParameterOffset = read_u32(&ptr, se);
+
+	/* Category data */
+	FAudio_assert((ptr - start) == categoryOffset);
+	pEngine->categories = (FACTAudioCategory*) pEngine->pMalloc(
+		sizeof(FACTAudioCategory) * pEngine->categoryCount
+	);
+	for (i = 0; i < pEngine->categoryCount; i += 1)
+	{
+		pEngine->categories[i].instanceLimit = read_u8(&ptr);
+		pEngine->categories[i].fadeInMS = read_u16(&ptr, se);
+		pEngine->categories[i].fadeOutMS = read_u16(&ptr, se);
+		pEngine->categories[i].maxInstanceBehavior = read_u8(&ptr) >> 3;
+		pEngine->categories[i].parentCategory = read_u16(&ptr, se);
+		pEngine->categories[i].volume = FACT_INTERNAL_CalculateAmplitudeRatio(
+			read_volbyte(&ptr)
+		);
+		pEngine->categories[i].visibility = read_u8(&ptr);
+		pEngine->categories[i].instanceCount = 0;
+		pEngine->categories[i].currentVolume = 1.0f;
+	}
+
+	/* Variable data */
+	FAudio_assert((ptr - start) == variableOffset);
+	pEngine->variables = (FACTVariable*) pEngine->pMalloc(
+		sizeof(FACTVariable) * pEngine->variableCount
+	);
+	for (i = 0; i < pEngine->variableCount; i += 1)
+	{
+		pEngine->variables[i].accessibility = read_u8(&ptr);
+		pEngine->variables[i].initialValue = read_f32(&ptr, se);
+		pEngine->variables[i].minValue = read_f32(&ptr, se);
+		pEngine->variables[i].maxValue = read_f32(&ptr, se);
+	}
+
+	/* Global variable storage. Some unused data for non-global vars */
+	pEngine->globalVariableValues = (float*) pEngine->pMalloc(
+		sizeof(float) * pEngine->variableCount
+	);
+	for (i = 0; i < pEngine->variableCount; i += 1)
+	{
+		pEngine->globalVariableValues[i] = pEngine->variables[i].initialValue;
+	}
+
+	/* RPC data */
+	if (pEngine->rpcCount > 0)
+	{
+		FAudio_assert((ptr - start) == rpcOffset);
+		pEngine->rpcs = (FACTRPC*) pEngine->pMalloc(
+			sizeof(FACTRPC) *
+			pEngine->rpcCount
+		);
+		pEngine->rpcCodes = (uint32_t*) pEngine->pMalloc(
+			sizeof(uint32_t) *
+			pEngine->rpcCount
+		);
+		for (i = 0; i < pEngine->rpcCount; i += 1)
+		{
+			pEngine->rpcCodes[i] = (uint32_t) (ptr - start);
+			pEngine->rpcs[i].variable = read_u16(&ptr, se);
+			pEngine->rpcs[i].pointCount = read_u8(&ptr);
+			pEngine->rpcs[i].parameter = read_u16(&ptr, se);
+			pEngine->rpcs[i].points = (FACTRPCPoint*) pEngine->pMalloc(
+				sizeof(FACTRPCPoint) *
+				pEngine->rpcs[i].pointCount
+			);
+			for (j = 0; j < pEngine->rpcs[i].pointCount; j += 1)
+			{
+				pEngine->rpcs[i].points[j].x = read_f32(&ptr, se);
+				pEngine->rpcs[i].points[j].y = read_f32(&ptr, se);
+				pEngine->rpcs[i].points[j].type = read_u8(&ptr);
+			}
+		}
+	}
+
+	/* DSP Preset data */
+	if (pEngine->dspPresetCount > 0)
+	{
+		FAudio_assert((ptr - start) == dspPresetOffset);
+		pEngine->dspPresets = (FACTDSPPreset*) pEngine->pMalloc(
+			sizeof(FACTDSPPreset) *
+			pEngine->dspPresetCount
+		);
+		pEngine->dspPresetCodes = (uint32_t*) pEngine->pMalloc(
+			sizeof(uint32_t) *
+			pEngine->dspPresetCount
+		);
+		for (i = 0; i < pEngine->dspPresetCount; i += 1)
+		{
+			pEngine->dspPresetCodes[i] = (uint32_t) (ptr - start);
+			pEngine->dspPresets[i].accessibility = read_u8(&ptr);
+			pEngine->dspPresets[i].parameterCount = read_u16(&ptr, se);
+			ptr += 2; /* Unknown value */
+			pEngine->dspPresets[i].parameters = (FACTDSPParameter*) pEngine->pMalloc(
+				sizeof(FACTDSPParameter) *
+				pEngine->dspPresets[i].parameterCount
+			); /* This will be filled in just a moment... */
+		}
+
+		/* DSP Parameter data */
+		FAudio_assert((ptr - start) == dspParameterOffset);
+		for (i = 0; i < pEngine->dspPresetCount; i += 1)
+		{
+			for (j = 0; j < pEngine->dspPresets[i].parameterCount; j += 1)
+			{
+				pEngine->dspPresets[i].parameters[j].type = read_u8(&ptr);
+				pEngine->dspPresets[i].parameters[j].value = read_f32(&ptr, se);
+				pEngine->dspPresets[i].parameters[j].minVal = read_f32(&ptr, se);
+				pEngine->dspPresets[i].parameters[j].maxVal = read_f32(&ptr, se);
+				pEngine->dspPresets[i].parameters[j].unknown = read_u16(&ptr, se);
+			}
+		}
+	}
+
+	/* Blob #1, no idea what this is... */
+	FAudio_assert((ptr - start) == blob1Offset);
+	ptr += blob1Count * 2;
+
+	/* Category Name Index data */
+	FAudio_assert((ptr - start) == categoryNameIndexOffset);
+	ptr += pEngine->categoryCount * 6; /* FIXME: index as assert value? */
+
+	/* Category Name data */
+	FAudio_assert((ptr - start) == categoryNameOffset);
+	pEngine->categoryNames = (char**) pEngine->pMalloc(
+		sizeof(char*) *
+		pEngine->categoryCount
+	);
+	for (i = 0; i < pEngine->categoryCount; i += 1)
+	{
+		memsize = FAudio_strlen((char*) ptr) + 1; /* Dastardly! */
+		pEngine->categoryNames[i] = (char*) pEngine->pMalloc(memsize);
+		FAudio_memcpy(pEngine->categoryNames[i], ptr, memsize);
+		ptr += memsize;
+	}
+
+	/* Blob #2, no idea what this is... */
+	FAudio_assert((ptr - start) == blob2Offset);
+	ptr += blob2Count * 2;
+
+	/* Variable Name Index data */
+	FAudio_assert((ptr - start) == variableNameIndexOffset);
+	ptr += pEngine->variableCount * 6; /* FIXME: index as assert value? */
+
+	/* Variable Name data */
+	FAudio_assert((ptr - start) == variableNameOffset);
+	pEngine->variableNames = (char**) pEngine->pMalloc(
+		sizeof(char*) *
+		pEngine->variableCount
+	);
+	for (i = 0; i < pEngine->variableCount; i += 1)
+	{
+		memsize = FAudio_strlen((char*) ptr) + 1; /* Dastardly! */
+		pEngine->variableNames[i] = (char*) pEngine->pMalloc(memsize);
+		FAudio_memcpy(pEngine->variableNames[i], ptr, memsize);
+		ptr += memsize;
+	}
+
+	/* Store this pointer in case we're asked to free it */
+	if (pParams->globalSettingsFlags & FACT_FLAG_MANAGEDATA)
+	{
+		pEngine->settings = pParams->pGlobalSettingsBuffer;
+	}
+
+	/* Finally. */
+	FAudio_assert((ptr - start) == pParams->globalSettingsBufferSize);
+	return 0;
+}
+
+void FACT_INTERNAL_ParseTrackEvents(
+	uint8_t **ptr,
+	uint8_t se,
+	FACTTrack *track,
+	FAudioMallocFunc pMalloc
+) {
+	uint32_t evtInfo;
+	uint8_t minWeight, maxWeight, separator;
+	uint8_t i;
+	uint16_t j;
+
+	track->eventCount = read_u8(ptr);
+	track->events = (FACTEvent*) pMalloc(
+		sizeof(FACTEvent) *
+		track->eventCount
+	);
+	FAudio_zero(track->events, sizeof(FACTEvent) * track->eventCount);
+	for (i = 0; i < track->eventCount; i += 1)
+	{
+		evtInfo = read_u32(ptr, se);
+		track->events[i].randomOffset = read_u16(ptr, se);
+
+		track->events[i].type = evtInfo & 0x001F;
+		track->events[i].timestamp = (evtInfo >> 5) & 0xFFFF;
+
+		separator = read_u8(ptr);
+		FAudio_assert(separator == 0xFF); /* Separator? */
+
+		#define EVTTYPE(t) (track->events[i].type == t)
+		if (EVTTYPE(FACTEVENT_STOP))
+		{
+			track->events[i].stop.flags = read_u8(ptr);
+		}
+		else if (EVTTYPE(FACTEVENT_PLAYWAVE))
+		{
+			/* Basic Wave */
+			track->events[i].wave.isComplex = 0;
+			track->events[i].wave.flags = read_u8(ptr);
+			track->events[i].wave.simple.track = read_u16(ptr, se);
+			track->events[i].wave.simple.wavebank = read_u8(ptr);
+			track->events[i].wave.loopCount = read_u8(ptr);
+			track->events[i].wave.position = read_u16(ptr, se);
+			track->events[i].wave.angle = read_u16(ptr, se);
+
+			/* No Effect Variation */
+			track->events[i].wave.variationFlags = 0;
+		}
+		else if (EVTTYPE(FACTEVENT_PLAYWAVETRACKVARIATION))
+		{
+			/* Complex Wave */
+			track->events[i].wave.isComplex = 1;
+			track->events[i].wave.flags = read_u8(ptr);
+			track->events[i].wave.loopCount = read_u8(ptr);
+			track->events[i].wave.position = read_u16(ptr, se);
+			track->events[i].wave.angle = read_u16(ptr, se);
+
+			/* Track Variation */
+			evtInfo = read_u32(ptr, se);
+			track->events[i].wave.complex.trackCount = evtInfo & 0xFFFF;
+			track->events[i].wave.complex.variation = (evtInfo >> 16) & 0xFFFF;
+			*ptr += 4; /* Unknown values */
+			track->events[i].wave.complex.tracks = (uint16_t*) pMalloc(
+				sizeof(uint16_t) *
+				track->events[i].wave.complex.trackCount
+			);
+			track->events[i].wave.complex.wavebanks = (uint8_t*) pMalloc(
+				sizeof(uint8_t) *
+				track->events[i].wave.complex.trackCount
+			);
+			track->events[i].wave.complex.weights = (uint8_t*) pMalloc(
+				sizeof(uint8_t) *
+				track->events[i].wave.complex.trackCount
+			);
+			for (j = 0; j < track->events[i].wave.complex.trackCount; j += 1)
+			{
+				track->events[i].wave.complex.tracks[j] = read_u16(ptr, se);
+				track->events[i].wave.complex.wavebanks[j] = read_u8(ptr);
+				minWeight = read_u8(ptr);
+				maxWeight = read_u8(ptr);
+				track->events[i].wave.complex.weights[j] = (
+					maxWeight - minWeight
+				);
+			}
+
+			/* No Effect Variation */
+			track->events[i].wave.variationFlags = 0;
+		}
+		else if (EVTTYPE(FACTEVENT_PLAYWAVEEFFECTVARIATION))
+		{
+			/* Basic Wave */
+			track->events[i].wave.isComplex = 0;
+			track->events[i].wave.flags = read_u8(ptr);
+			track->events[i].wave.simple.track = read_u16(ptr, se);
+			track->events[i].wave.simple.wavebank = read_u8(ptr);
+			track->events[i].wave.loopCount = read_u8(ptr);
+			track->events[i].wave.position = read_u16(ptr, se);
+			track->events[i].wave.angle = read_u16(ptr, se);
+
+			/* Effect Variation */
+			track->events[i].wave.minPitch = read_s16(ptr, se);
+			track->events[i].wave.maxPitch = read_s16(ptr, se);
+			track->events[i].wave.minVolume = read_volbyte(ptr);
+			track->events[i].wave.maxVolume = read_volbyte(ptr);
+			track->events[i].wave.minFrequency = read_f32(ptr, se);
+			track->events[i].wave.maxFrequency = read_f32(ptr, se);
+			track->events[i].wave.minQFactor = read_f32(ptr, se);
+			track->events[i].wave.maxQFactor = read_f32(ptr, se);
+			track->events[i].wave.variationFlags = read_u16(ptr, se);
+		}
+		else if (EVTTYPE(FACTEVENT_PLAYWAVETRACKEFFECTVARIATION))
+		{
+			/* Complex Wave */
+			track->events[i].wave.isComplex = 1;
+			track->events[i].wave.flags = read_u8(ptr);
+			track->events[i].wave.loopCount = read_u8(ptr);
+			track->events[i].wave.position = read_u16(ptr, se);
+			track->events[i].wave.angle = read_u16(ptr, se);
+
+			/* Effect Variation */
+			track->events[i].wave.minPitch = read_s16(ptr, se);
+			track->events[i].wave.maxPitch = read_s16(ptr, se);
+			track->events[i].wave.minVolume = read_volbyte(ptr);
+			track->events[i].wave.maxVolume = read_volbyte(ptr);
+			track->events[i].wave.minFrequency = read_f32(ptr, se);
+			track->events[i].wave.maxFrequency = read_f32(ptr, se);
+			track->events[i].wave.minQFactor = read_f32(ptr, se);
+			track->events[i].wave.maxQFactor = read_f32(ptr, se);
+			track->events[i].wave.variationFlags = read_u16(ptr, se);
+
+			/* Track Variation */
+			evtInfo = read_u32(ptr, se);
+			track->events[i].wave.complex.trackCount = evtInfo & 0xFFFF;
+			track->events[i].wave.complex.variation = (evtInfo >> 16) & 0xFFFF;
+			*ptr += 4; /* Unknown values */
+			track->events[i].wave.complex.tracks = (uint16_t*) pMalloc(
+				sizeof(uint16_t) *
+				track->events[i].wave.complex.trackCount
+			);
+			track->events[i].wave.complex.wavebanks = (uint8_t*) pMalloc(
+				sizeof(uint8_t) *
+				track->events[i].wave.complex.trackCount
+			);
+			track->events[i].wave.complex.weights = (uint8_t*) pMalloc(
+				sizeof(uint8_t) *
+				track->events[i].wave.complex.trackCount
+			);
+			for (j = 0; j < track->events[i].wave.complex.trackCount; j += 1)
+			{
+				track->events[i].wave.complex.tracks[j] = read_u16(ptr, se);
+				track->events[i].wave.complex.wavebanks[j] = read_u8(ptr);
+				minWeight = read_u8(ptr);
+				maxWeight = read_u8(ptr);
+				track->events[i].wave.complex.weights[j] = (
+					maxWeight - minWeight
+				);
+			}
+		}
+		else if (	EVTTYPE(FACTEVENT_PITCH) ||
+				EVTTYPE(FACTEVENT_VOLUME) ||
+				EVTTYPE(FACTEVENT_PITCHREPEATING) ||
+				EVTTYPE(FACTEVENT_VOLUMEREPEATING)	)
+		{
+			track->events[i].value.settings = read_u8(ptr);
+			if (track->events[i].value.settings & 1) /* Ramp */
+			{
+				track->events[i].value.repeats = 0;
+				track->events[i].value.ramp.initialValue = read_f32(ptr, se);
+				track->events[i].value.ramp.initialSlope = read_f32(ptr, se) * 100;
+				track->events[i].value.ramp.slopeDelta = read_f32(ptr, se);
+				track->events[i].value.ramp.duration = read_u16(ptr, se);
+			}
+			else /* Equation */
+			{
+				track->events[i].value.equation.flags = read_u8(ptr);
+
+				/* SetValue, SetRandomValue, anything else? */
+				FAudio_assert(track->events[i].value.equation.flags & 0x0C);
+
+				track->events[i].value.equation.value1 = read_f32(ptr, se);
+				track->events[i].value.equation.value2 = read_f32(ptr, se);
+
+				*ptr += 5; /* Unknown values */
+
+				if (	EVTTYPE(FACTEVENT_PITCHREPEATING) ||
+					EVTTYPE(FACTEVENT_VOLUMEREPEATING)	)
+				{
+					track->events[i].value.repeats = read_u16(ptr, se);
+					track->events[i].value.frequency = read_u16(ptr, se);
+				}
+				else
+				{
+					track->events[i].value.repeats = 0;
+				}
+			}
+		}
+		else if (EVTTYPE(FACTEVENT_MARKER))
+		{
+			track->events[i].marker.marker = read_u32(ptr, se);
+			track->events[i].marker.repeats = 0;
+			track->events[i].marker.frequency = 0;
+		}
+		else if (EVTTYPE(FACTEVENT_MARKERREPEATING))
+		{
+			track->events[i].marker.marker = read_u32(ptr, se);
+			track->events[i].marker.repeats = read_u16(ptr, se);
+			track->events[i].marker.frequency = read_u16(ptr, se);
+		}
+		else
+		{
+			FAudio_assert(0 && "Unknown event type!");
+		}
+		#undef EVTTYPE
+	}
+}
+
+uint32_t FACT_INTERNAL_ParseSoundBank(
+	FACTAudioEngine *pEngine,
+	const void *pvBuffer,
+	uint32_t dwSize,
+	FACTSoundBank **ppSoundBank
+) {
+	FACTSoundBank *sb;
+	uint16_t	contentVersion,
+			cueSimpleCount,
+			cueComplexCount,
+			cueTotalAlign;
+	int32_t	cueSimpleOffset,
+		cueComplexOffset,
+		cueNameOffset,
+		variationOffset,
+		transitionOffset,
+		wavebankNameOffset,
+		cueHashOffset,
+		cueNameIndexOffset,
+		soundOffset;
+	uint32_t entryCountAndFlags;
+	uint16_t filterData;
+	uint8_t platform;
+	size_t memsize;
+	uint16_t i, j, k, cur, tool;
+	uint8_t *ptrBookmark;
+
+	uint8_t *ptr = (uint8_t*) pvBuffer;
+	uint8_t *start = ptr;
+
+	uint32_t magic = read_u32(&ptr, 0);
+	uint8_t se = magic == 0x5344424B; /* Swap Endian */
+
+	if (magic != 0x4B424453 && magic != 0x5344424B)  /* 'SDBK' */
+	{
+		return -1; /* TODO: NOT XACT FILE */
+	}
+
+	contentVersion = read_u16(&ptr, se);
+	if (!FACT_INTERNAL_SupportedContent(contentVersion))
+	{
+		return -2;
+	}
+
+	tool = read_u16(&ptr, se); /* Tool version */
+	if (tool != 43)
+	{
+		return -3;
+	}
+
+	/* CRC, unused */
+	ptr += 2;
+
+	/* Last modified, unused */
+	ptr += 8;
+
+	/* Windows == 1, Xbox == 3. 0 is unknown, probably old content */
+	platform = read_u8(&ptr);
+	if (	platform != 0 &&
+		platform != 1 &&
+		platform != 3	)
+	{
+		return -4; /* TODO: WRONG PLATFORM */
+	}
+
+	sb = (FACTSoundBank*) pEngine->pMalloc(sizeof(FACTSoundBank));
+	sb->parentEngine = pEngine;
+	sb->cueList = NULL;
+	sb->notifyOnDestroy = 0;
+	sb->usercontext = NULL;
+
+	cueSimpleCount = read_u16(&ptr, se);
+	cueComplexCount = read_u16(&ptr, se);
+
+	ptr += 2; /* Unknown value */
+
+	cueTotalAlign = read_u16(&ptr, se); /* FIXME: Why? */
+	sb->cueCount = cueSimpleCount + cueComplexCount;
+	sb->wavebankCount = read_u8(&ptr);
+	sb->soundCount = read_u16(&ptr, se);
+
+	/* Cue name length, unused */
+	ptr += 2;
+
+	ptr += 2; /* Unknown value */
+
+	cueSimpleOffset = read_s32(&ptr, se);
+	cueComplexOffset = read_s32(&ptr, se);
+	cueNameOffset = read_s32(&ptr, se);
+
+	ptr += 4; /* Unknown value */
+
+	variationOffset = read_s32(&ptr, se);
+	transitionOffset = read_s32(&ptr, se);
+	wavebankNameOffset = read_s32(&ptr, se);
+	cueHashOffset = read_s32(&ptr, se);
+	cueNameIndexOffset = read_s32(&ptr, se);
+	soundOffset = read_s32(&ptr, se);
+
+	/* SoundBank Name */
+	memsize = FAudio_strlen((char*) ptr) + 1; /* Dastardly! */
+	sb->name = (char*) pEngine->pMalloc(memsize);
+	FAudio_memcpy(sb->name, ptr, memsize);
+	ptr += 64;
+
+	/* WaveBank Name data */
+	FAudio_assert((ptr - start) == wavebankNameOffset);
+	sb->wavebankNames = (char**) pEngine->pMalloc(
+		sizeof(char*) *
+		sb->wavebankCount
+	);
+	for (i = 0; i < sb->wavebankCount; i += 1)
+	{
+		memsize = FAudio_strlen((char*) ptr) + 1;
+		sb->wavebankNames[i] = (char*) pEngine->pMalloc(memsize);
+		FAudio_memcpy(sb->wavebankNames[i], ptr, memsize);
+		ptr += 64;
+	}
+
+	/* Sound data */
+	FAudio_assert((ptr - start) == soundOffset);
+	sb->sounds = (FACTSound*) pEngine->pMalloc(
+		sizeof(FACTSound) *
+		sb->soundCount
+	);
+	sb->soundCodes = (uint32_t*) pEngine->pMalloc(
+		sizeof(uint32_t) *
+		sb->soundCount
+	);
+	for (i = 0; i < sb->soundCount; i += 1)
+	{
+		sb->soundCodes[i] = (uint32_t) (ptr - start);
+		sb->sounds[i].flags = read_u8(&ptr);
+		sb->sounds[i].category = read_u16(&ptr, se);
+		sb->sounds[i].volume = read_volbyte(&ptr);
+		sb->sounds[i].pitch = read_s16(&ptr, se);
+		sb->sounds[i].priority = read_u8(&ptr);
+
+		/* Length of sound entry, unused */
+		ptr += 2;
+
+		/* Simple/Complex Track data */
+		if (sb->sounds[i].flags & 0x01)
+		{
+			sb->sounds[i].trackCount = read_u8(&ptr);
+			memsize = sizeof(FACTTrack) * sb->sounds[i].trackCount;
+			sb->sounds[i].tracks = (FACTTrack*) pEngine->pMalloc(memsize);
+			FAudio_zero(sb->sounds[i].tracks, memsize);
+		}
+		else
+		{
+			sb->sounds[i].trackCount = 1;
+			memsize = sizeof(FACTTrack) * sb->sounds[i].trackCount;
+			sb->sounds[i].tracks = (FACTTrack*) pEngine->pMalloc(memsize);
+			FAudio_zero(sb->sounds[i].tracks, memsize);
+			sb->sounds[i].tracks[0].volume = 0.0f;
+			sb->sounds[i].tracks[0].filter = 0xFF;
+			sb->sounds[i].tracks[0].eventCount = 1;
+			sb->sounds[i].tracks[0].events = (FACTEvent*) pEngine->pMalloc(
+				sizeof(FACTEvent)
+			);
+			FAudio_zero(
+				sb->sounds[i].tracks[0].events,
+				sizeof(FACTEvent)
+			);
+			sb->sounds[i].tracks[0].events[0].type = FACTEVENT_PLAYWAVE;
+			sb->sounds[i].tracks[0].events[0].wave.position = 0; /* FIXME */
+			sb->sounds[i].tracks[0].events[0].wave.angle = 0; /* FIXME */
+			sb->sounds[i].tracks[0].events[0].wave.simple.track = read_u16(&ptr, se);
+			sb->sounds[i].tracks[0].events[0].wave.simple.wavebank = read_u8(&ptr);
+		}
+
+		/* RPC Code data */
+		if (sb->sounds[i].flags & 0x0E)
+		{
+			const uint16_t rpcDataLength = read_u16(&ptr, se);
+			ptrBookmark = ptr - 2;
+
+			#define COPYRPCBLOCK(loc) \
+				loc.rpcCodeCount = read_u8(&ptr); \
+				memsize = sizeof(uint32_t) * loc.rpcCodeCount; \
+				loc.rpcCodes = (uint32_t*) pEngine->pMalloc(memsize); \
+				for (k = 0; k < loc.rpcCodeCount; k += 1) \
+				{ \
+					loc.rpcCodes[k] = read_u32(&ptr, se); \
+				} \
+
+			/* Sound has attached RPCs */
+			if (sb->sounds[i].flags & 0x02)
+			{
+				COPYRPCBLOCK(sb->sounds[i])
+			}
+			else
+			{
+				sb->sounds[i].rpcCodeCount = 0;
+				sb->sounds[i].rpcCodes = NULL;
+			}
+
+			/* Tracks have attached RPCs */
+			if (sb->sounds[i].flags & 0x04)
+			{
+				for (j = 0; j < sb->sounds[i].trackCount; j += 1)
+				{
+					COPYRPCBLOCK(sb->sounds[i].tracks[j])
+				}
+			}
+			else
+			{
+				for (j = 0; j < sb->sounds[i].trackCount; j += 1)
+				{
+					sb->sounds[i].tracks[j].rpcCodeCount = 0;
+					sb->sounds[i].tracks[j].rpcCodes = NULL;
+				}
+			}
+
+			#undef COPYRPCBLOCK
+
+			/* FIXME: Does 0x08 mean something for RPCs...? */
+			FAudio_assert((ptr - ptrBookmark) == rpcDataLength);
+		}
+		else
+		{
+			sb->sounds[i].rpcCodeCount = 0;
+			sb->sounds[i].rpcCodes = NULL;
+			for (j = 0; j < sb->sounds[i].trackCount; j += 1)
+			{
+				sb->sounds[i].tracks[j].rpcCodeCount = 0;
+				sb->sounds[i].tracks[j].rpcCodes = NULL;
+			}
+		}
+
+		/* DSP Preset Code data */
+		if (sb->sounds[i].flags & 0x10)
+		{
+			/* DSP presets length, unused */
+			ptr += 2;
+
+			sb->sounds[i].dspCodeCount = read_u8(&ptr);
+			memsize = sizeof(uint32_t) * sb->sounds[i].dspCodeCount;
+			sb->sounds[i].dspCodes = (uint32_t*) pEngine->pMalloc(memsize);
+			for (j = 0; j < sb->sounds[i].dspCodeCount; j += 1)
+			{
+				sb->sounds[i].dspCodes[j] = read_u32(&ptr, se);
+			}
+		}
+		else
+		{
+			sb->sounds[i].dspCodeCount = 0;
+			sb->sounds[i].dspCodes = NULL;
+		}
+
+		/* Track data */
+		if (sb->sounds[i].flags & 0x01)
+		{
+			for (j = 0; j < sb->sounds[i].trackCount; j += 1)
+			{
+				sb->sounds[i].tracks[j].volume = read_volbyte(&ptr);
+
+				sb->sounds[i].tracks[j].code = read_u32(&ptr, se);
+
+				if (contentVersion == FACT_CONTENT_VERSION_3_0)
+				{
+					/* 3.0 doesn't have track filter data */
+					sb->sounds[i].tracks[j].filter = 0xFF;
+					sb->sounds[i].tracks[j].qfactor = 0;
+					sb->sounds[i].tracks[j].frequency = 0;
+					continue;
+				}
+
+				filterData = read_u16(&ptr, se);
+				if (filterData & 0x0001)
+				{
+					sb->sounds[i].tracks[j].filter =
+						(filterData >> 1) & 0x02;
+				}
+				else
+				{
+					/* Huh...? */
+					sb->sounds[i].tracks[j].filter = 0xFF;
+				}
+				sb->sounds[i].tracks[j].qfactor = (filterData >> 8) & 0xFF;
+				sb->sounds[i].tracks[j].frequency = read_u16(&ptr, se);
+			}
+
+			/* All Track events are stored at the end of the block */
+			for (j = 0; j < sb->sounds[i].trackCount; j += 1)
+			{
+				FAudio_assert((ptr - start) == sb->sounds[i].tracks[j].code);
+				FACT_INTERNAL_ParseTrackEvents(
+					&ptr,
+					se,
+					&sb->sounds[i].tracks[j],
+					pEngine->pMalloc
+				);
+			}
+		}
+	}
+
+	/* All Cue data */
+	sb->variationCount = 0;
+	sb->transitionCount = 0;
+	sb->cues = (FACTCueData*) pEngine->pMalloc(
+		sizeof(FACTCueData) *
+		sb->cueCount
+	);
+	cur = 0;
+
+	/* Simple Cue data */
+	FAudio_assert(cueSimpleCount == 0 || (ptr - start) == cueSimpleOffset);
+	for (i = 0; i < cueSimpleCount; i += 1, cur += 1)
+	{
+		sb->cues[cur].flags = read_u8(&ptr);
+		sb->cues[cur].sbCode = read_u32(&ptr, se);
+		sb->cues[cur].transitionOffset = 0;
+		sb->cues[cur].instanceLimit = 0xFF;
+		sb->cues[cur].fadeInMS = 0;
+		sb->cues[cur].fadeOutMS = 0;
+		sb->cues[cur].maxInstanceBehavior = 0;
+		sb->cues[cur].instanceCount = 0;
+	}
+
+	/* Complex Cue data */
+	FAudio_assert(cueComplexCount == 0 || (ptr - start) == cueComplexOffset);
+	for (i = 0; i < cueComplexCount; i += 1, cur += 1)
+	{
+		sb->cues[cur].flags = read_u8(&ptr);
+		sb->cues[cur].sbCode = read_u32(&ptr, se);
+		sb->cues[cur].transitionOffset = read_u32(&ptr, se);
+		if (sb->cues[cur].transitionOffset == 0xFFFFFFFF)
+		{
+			/* FIXME: Why */
+			sb->cues[cur].transitionOffset = 0;
+		}
+		sb->cues[cur].instanceLimit = read_u8(&ptr);
+		sb->cues[cur].fadeInMS = read_u16(&ptr, se);
+		sb->cues[cur].fadeOutMS = read_u16(&ptr, se);
+		sb->cues[cur].maxInstanceBehavior = read_u8(&ptr) >> 3;
+		sb->cues[cur].instanceCount = 0;
+
+		if (!(sb->cues[cur].flags & 0x04))
+		{
+			/* FIXME: Is this the only way to get this...? */
+			sb->variationCount += 1;
+		}
+		if (sb->cues[cur].transitionOffset > 0)
+		{
+			/* FIXME: Is this the only way to get this...? */
+			sb->transitionCount += 1;
+		}
+	}
+
+	/* Variation data */
+	if (sb->variationCount > 0)
+	{
+		FAudio_assert((ptr - start) == variationOffset);
+		sb->variations = (FACTVariationTable*) pEngine->pMalloc(
+			sizeof(FACTVariationTable) *
+			sb->variationCount
+		);
+		sb->variationCodes = (uint32_t*) pEngine->pMalloc(
+			sizeof(uint32_t) *
+			sb->variationCount
+		);
+	}
+	else
+	{
+		sb->variations = NULL;
+		sb->variationCodes = NULL;
+	}
+	for (i = 0; i < sb->variationCount; i += 1)
+	{
+		sb->variationCodes[i] = (uint32_t) (ptr - start);
+		entryCountAndFlags = read_u32(&ptr, se);
+		sb->variations[i].entryCount = entryCountAndFlags & 0xFFFF;
+		sb->variations[i].flags = (entryCountAndFlags >> (16 + 3)) & 0x07;
+		ptr += 2; /* Unknown value */
+		sb->variations[i].variable = read_s16(&ptr, se);
+		memsize = sizeof(FACTVariation) * sb->variations[i].entryCount;
+		sb->variations[i].entries = (FACTVariation*) pEngine->pMalloc(
+			memsize
+		);
+		FAudio_zero(sb->variations[i].entries, memsize);
+
+		if (sb->variations[i].flags == 0)
+		{
+			/* Wave with byte min/max */
+			sb->variations[i].isComplex = 0;
+			for (j = 0; j < sb->variations[i].entryCount; j += 1)
+			{
+				sb->variations[i].entries[j].simple.track = read_u16(&ptr, se);
+				sb->variations[i].entries[j].simple.wavebank = read_u8(&ptr);
+				sb->variations[i].entries[j].minWeight = read_u8(&ptr) / 255.0f;
+				sb->variations[i].entries[j].maxWeight = read_u8(&ptr) / 255.0f;
+			}
+		}
+		else if (sb->variations[i].flags == 1)
+		{
+			/* Complex with byte min/max */
+			sb->variations[i].isComplex = 1;
+			for (j = 0; j < sb->variations[i].entryCount; j += 1)
+			{
+				sb->variations[i].entries[j].soundCode = read_u32(&ptr, se);
+				sb->variations[i].entries[j].minWeight = read_u8(&ptr) / 255.0f;
+				sb->variations[i].entries[j].maxWeight = read_u8(&ptr) / 255.0f;
+			}
+		}
+		else if (sb->variations[i].flags == 3)
+		{
+			/* Complex Interactive Variation with float min/max */
+			sb->variations[i].isComplex = 1;
+			for (j = 0; j < sb->variations[i].entryCount; j += 1)
+			{
+				sb->variations[i].entries[j].soundCode = read_u32(&ptr, se);
+				sb->variations[i].entries[j].minWeight = read_f32(&ptr, se);
+				sb->variations[i].entries[j].maxWeight = read_f32(&ptr, se);
+				sb->variations[i].entries[j].linger = read_u32(&ptr, se);
+			}
+		}
+		else if (sb->variations[i].flags == 4)
+		{
+			/* Compact Wave */
+			sb->variations[i].isComplex = 0;
+			for (j = 0; j < sb->variations[i].entryCount; j += 1)
+			{
+				sb->variations[i].entries[j].simple.track = read_u16(&ptr, se);
+				sb->variations[i].entries[j].simple.wavebank = read_u8(&ptr);
+				sb->variations[i].entries[j].minWeight = 0.0f;
+				sb->variations[i].entries[j].maxWeight = 1.0f;
+			}
+		}
+		else
+		{
+			FAudio_assert(0 && "Unknown variation type!");
+		}
+	}
+
+	/* Transition data */
+	if (sb->transitionCount > 0)
+	{
+		FAudio_assert((ptr - start) == transitionOffset);
+		sb->transitions = (FACTTransitionTable*) pEngine->pMalloc(
+			sizeof(FACTTransitionTable) *
+			sb->transitionCount
+		);
+		sb->transitionCodes = (uint32_t*) pEngine->pMalloc(
+			sizeof(uint32_t) *
+			sb->transitionCount
+		);
+	}
+	else
+	{
+		sb->transitions = NULL;
+		sb->transitionCodes = NULL;
+	}
+	for (i = 0; i < sb->transitionCount; i += 1)
+	{
+		sb->transitionCodes[i] = (uint32_t) (ptr - start);
+		sb->transitions[i].entryCount = read_u32(&ptr, se);
+		memsize = sizeof(FACTTransition) * sb->transitions[i].entryCount;
+		sb->transitions[i].entries = (FACTTransition*) pEngine->pMalloc(
+			memsize
+		);
+		FAudio_zero(sb->transitions[i].entries, memsize);
+		for (j = 0; j < sb->transitions[i].entryCount; j += 1)
+		{
+			sb->transitions[i].entries[j].soundCode = read_s32(&ptr, se);
+			sb->transitions[i].entries[j].srcMarkerMin = read_u32(&ptr, se);
+			sb->transitions[i].entries[j].srcMarkerMax = read_u32(&ptr, se);
+			sb->transitions[i].entries[j].dstMarkerMin = read_u32(&ptr, se);
+			sb->transitions[i].entries[j].dstMarkerMax = read_u32(&ptr, se);
+			sb->transitions[i].entries[j].fadeIn = read_u16(&ptr, se);
+			sb->transitions[i].entries[j].fadeOut = read_u16(&ptr, se);
+			sb->transitions[i].entries[j].flags = read_u16(&ptr, se);
+		}
+	}
+
+	/* Cue Hash data? No idea what this is... */
+	if (cueHashOffset != -1)
+	{
+		FAudio_assert((ptr - start) == cueHashOffset);
+		ptr += 2 * cueTotalAlign;
+	}
+
+	/* Cue Name Index data */
+	if (cueNameIndexOffset != -1)
+	{
+		FAudio_assert((ptr - start) == cueNameIndexOffset);
+		ptr += 6 * sb->cueCount; /* FIXME: index as assert value? */
+	}
+
+	/* Cue Name data */
+	if (cueNameOffset != -1)
+	{
+		FAudio_assert((ptr - start) == cueNameOffset);
+		sb->cueNames = (char**) pEngine->pMalloc(
+			sizeof(char*) *
+			sb->cueCount
+		);
+		for (i = 0; i < sb->cueCount; i += 1)
+		{
+			memsize = FAudio_strlen((char*) ptr) + 1;
+			sb->cueNames[i] = (char*) pEngine->pMalloc(memsize);
+			FAudio_memcpy(sb->cueNames[i], ptr, memsize);
+			ptr += memsize;
+		}
+	}
+	else
+	{
+		sb->cueNames = NULL;
+	}
+
+	/* Add to the Engine SoundBank list */
+	LinkedList_AddEntry(
+		&pEngine->sbList,
+		sb,
+		pEngine->sbLock,
+		pEngine->pMalloc
+	);
+
+	/* Finally. */
+	FAudio_assert((ptr - start) == dwSize);
+	*ppSoundBank = sb;
+	return 0;
+}
+
+/* This parser is based on the unxwb project, written by Luigi Auriemma.
+ *
+ * While the unxwb project was released under the GPL, Luigi has given us
+ * permission to use the unxwb sources under the zlib license.
+ *
+ * The unxwb website can be found here:
+ *
+ * http://aluigi.altervista.org/papers.htm#xbox
+ */
+uint32_t FACT_INTERNAL_ParseWaveBank(
+	FACTAudioEngine *pEngine,
+	void* io,
+	uint32_t offset,
+	uint32_t packetSize,
+	FACTReadFileCallback pRead,
+	FACTGetOverlappedResultCallback pOverlap,
+	uint16_t isStreaming,
+	FACTWaveBank **ppWaveBank
+) {
+	uint8_t se = 0; /* Swap Endian */
+	FACTWaveBank *wb;
+	size_t memsize;
+	uint32_t i, j;
+	FACTWaveBankHeader header;
+	FACTWaveBankData wbinfo;
+	uint32_t compactEntry;
+	int32_t seekTableOffset;
+	uint32_t fileOffset;
+	uint8_t *packetBuffer = NULL;
+	uint32_t packetBufferLen = 0;
+	uint16_t *pcm;
+
+	#define SEEKSET(loc) \
+		fileOffset = offset + loc;
+	#define SEEKCUR(loc) \
+		fileOffset += loc;
+	#define READ(dst, size) \
+		FACT_INTERNAL_ReadFile( \
+			pRead, \
+			pOverlap, \
+			io, \
+			fileOffset, \
+			packetSize, \
+			&packetBuffer, \
+			&packetBufferLen, \
+			pEngine->pRealloc, \
+			dst, \
+			size \
+		); \
+		SEEKCUR(size)
+
+	#define DOSWAP_16(x) x = FAudio_swap16BE(x)
+	#define DOSWAP_32(x) x = FAudio_swap32BE(x)
+	#define DOSWAP_64(x) x = FAudio_swap64BE(x)
+
+	fileOffset = offset;
+	READ(&header, sizeof(header))
+	se = header.dwSignature == 0x57424E44;
+	if (se)
+	{
+		DOSWAP_32(header.dwSignature);
+		DOSWAP_32(header.dwVersion);
+		DOSWAP_32(header.dwHeaderVersion);
+		for (i = 0; i < FACT_WAVEBANK_SEGIDX_COUNT; i += 1)
+		{
+			DOSWAP_32(header.Segments[i].dwOffset);
+			DOSWAP_32(header.Segments[i].dwLength);
+		}
+	}
+	if (header.dwSignature != 0x444E4257)
+	{
+		return -1; /* TODO: NOT XACT FILE */
+	}
+
+	if (!FACT_INTERNAL_SupportedContent(header.dwVersion))
+	{
+		return -2;
+	}
+
+	if (!FACT_INTERNAL_SupportedWBContent(header.dwHeaderVersion))
+	{
+		return -3;
+	}
+
+	wb = (FACTWaveBank*) pEngine->pMalloc(sizeof(FACTWaveBank));
+	wb->parentEngine = pEngine;
+	wb->waveList = NULL;
+	wb->waveLock = FAudio_PlatformCreateMutex();
+	wb->packetSize = packetSize;
+	wb->io = io;
+	wb->notifyOnDestroy = 0;
+	wb->usercontext = NULL;
+
+	/* WaveBank Data */
+	SEEKSET(header.Segments[FACT_WAVEBANK_SEGIDX_BANKDATA].dwOffset)
+	READ(&wbinfo, sizeof(wbinfo))
+	if (se)
+	{
+		DOSWAP_32(wbinfo.dwFlags);
+		DOSWAP_32(wbinfo.dwEntryCount);
+		DOSWAP_32(wbinfo.dwEntryMetaDataElementSize);
+		DOSWAP_32(wbinfo.dwEntryNameElementSize);
+		DOSWAP_32(wbinfo.dwAlignment);
+		DOSWAP_32(wbinfo.CompactFormat.dwValue);
+		DOSWAP_64(wbinfo.BuildTime);
+	}
+	wb->streaming = (wbinfo.dwFlags & FACT_WAVEBANK_TYPE_STREAMING);
+	wb->entryCount = wbinfo.dwEntryCount;
+	memsize = FAudio_strlen(wbinfo.szBankName) + 1;
+	wb->name = (char*) pEngine->pMalloc(memsize);
+	FAudio_memcpy(wb->name, wbinfo.szBankName, memsize);
+	memsize = sizeof(FACTWaveBankEntry) * wbinfo.dwEntryCount;
+	wb->entries = (FACTWaveBankEntry*) pEngine->pMalloc(memsize);
+	FAudio_zero(wb->entries, memsize);
+	memsize = sizeof(uint32_t) * wbinfo.dwEntryCount;
+	wb->entryRefs = (uint32_t*) pEngine->pMalloc(memsize);
+	FAudio_zero(wb->entryRefs, memsize);
+
+	/* FIXME: How much do we care about this? */
+	FAudio_assert(wb->streaming == isStreaming);
+	wb->streaming = isStreaming;
+
+	/* WaveBank Entry Metadata */
+	SEEKSET(header.Segments[FACT_WAVEBANK_SEGIDX_ENTRYMETADATA].dwOffset)
+	if (wbinfo.dwFlags & FACT_WAVEBANK_FLAGS_COMPACT)
+	{
+		for (i = 0; i < wbinfo.dwEntryCount - 1; i += 1)
+		{
+			READ(&compactEntry, sizeof(compactEntry))
+			if (se)
+			{
+				DOSWAP_32(compactEntry);
+			}
+			wb->entries[i].PlayRegion.dwOffset = (
+				(compactEntry & ((1 << 21) - 1)) *
+				wbinfo.dwAlignment
+			);
+			wb->entries[i].PlayRegion.dwLength = (
+				(compactEntry >> 21) & ((1 << 11) - 1)
+			);
+
+			/* TODO: Deviation table */
+			SEEKCUR(wbinfo.dwEntryMetaDataElementSize)
+			wb->entries[i].PlayRegion.dwLength = (
+				(compactEntry & ((1 << 21) - 1)) *
+				wbinfo.dwAlignment
+			) - wb->entries[i].PlayRegion.dwOffset;
+
+			wb->entries[i].PlayRegion.dwOffset +=
+				header.Segments[FACT_WAVEBANK_SEGIDX_ENTRYWAVEDATA].dwOffset;
+		}
+
+		READ(&compactEntry, sizeof(compactEntry))
+		if (se)
+		{
+			DOSWAP_32(compactEntry);
+		}
+		wb->entries[i].PlayRegion.dwOffset = (
+			(compactEntry & ((1 << 21) - 1)) *
+			wbinfo.dwAlignment
+		);
+
+		/* TODO: Deviation table */
+		SEEKCUR(wbinfo.dwEntryMetaDataElementSize)
+		wb->entries[i].PlayRegion.dwLength = (
+			header.Segments[FACT_WAVEBANK_SEGIDX_ENTRYWAVEDATA].dwLength -
+			wb->entries[i].PlayRegion.dwOffset
+		);
+
+		wb->entries[i].PlayRegion.dwOffset +=
+			header.Segments[FACT_WAVEBANK_SEGIDX_ENTRYWAVEDATA].dwOffset;
+	}
+	else
+	{
+		for (i = 0; i < wbinfo.dwEntryCount; i += 1)
+		{
+			READ(&wb->entries[i], wbinfo.dwEntryMetaDataElementSize)
+			if (se)
+			{
+				DOSWAP_32(wb->entries[i].dwFlagsAndDuration);
+				DOSWAP_32(wb->entries[i].Format.dwValue);
+				DOSWAP_32(wb->entries[i].PlayRegion.dwOffset);
+				DOSWAP_32(wb->entries[i].PlayRegion.dwLength);
+				DOSWAP_32(wb->entries[i].LoopRegion.dwStartSample);
+				DOSWAP_32(wb->entries[i].LoopRegion.dwTotalSamples);
+			}
+			wb->entries[i].PlayRegion.dwOffset +=
+				header.Segments[FACT_WAVEBANK_SEGIDX_ENTRYWAVEDATA].dwOffset;
+
+			/* If it's in-memory big-endian PCM, swap! */
+			if (	se &&
+				!wb->streaming &&
+				wb->entries[i].Format.wFormatTag == 0x0 &&
+				wb->entries[i].Format.wBitsPerSample == 1	)
+			{
+				pcm = (uint16_t*) FAudio_memptr(
+					(FAudioIOStream*) wb->io,
+					wb->entries[i].PlayRegion.dwOffset
+				);
+				for (j = 0; j < wb->entries[i].PlayRegion.dwLength; j += 2, pcm += 1)
+				{
+					DOSWAP_16(*pcm);
+				}
+			}
+		}
+
+		/* FIXME: This is a bit hacky. */
+		if (wbinfo.dwEntryMetaDataElementSize < 24)
+		{
+			for (i = 0; i < wbinfo.dwEntryCount; i += 1)
+			{
+				wb->entries[i].PlayRegion.dwLength =
+					header.Segments[FACT_WAVEBANK_SEGIDX_ENTRYWAVEDATA].dwLength;
+			}
+		}
+	}
+
+	/* WaveBank Seek Tables */
+	if (	wbinfo.dwFlags & FACT_WAVEBANK_FLAGS_SEEKTABLES &&
+		header.Segments[FACT_WAVEBANK_SEGIDX_SEEKTABLES].dwLength > 0	)
+	{
+		/* The seek table data layout is an absolute disaster! */
+		wb->seekTables = (FACTSeekTable*) pEngine->pMalloc(
+			wbinfo.dwEntryCount * sizeof(FACTSeekTable)
+		);
+		for (i = 0; i < wbinfo.dwEntryCount; i += 1)
+		{
+			/* Get the table offset... */
+			SEEKSET(
+				header.Segments[FACT_WAVEBANK_SEGIDX_SEEKTABLES].dwOffset +
+				i * sizeof(uint32_t)
+			)
+			READ(&seekTableOffset, sizeof(int32_t))
+			if (se)
+			{
+				DOSWAP_32(seekTableOffset);
+			}
+
+			/* If the offset is -1, this wave needs no table */
+			if (seekTableOffset == -1)
+			{
+				wb->seekTables[i].entryCount = 0;
+				wb->seekTables[i].entries = NULL;
+				continue;
+			}
+
+			/* Go to the table offset, after the offset table... */
+			SEEKSET(
+				header.Segments[FACT_WAVEBANK_SEGIDX_SEEKTABLES].dwOffset +
+				(wbinfo.dwEntryCount * sizeof(uint32_t)) +
+				seekTableOffset
+			)
+
+			/* Read the table, finally. */
+			READ(&wb->seekTables[i].entryCount, sizeof(uint32_t))
+			if (se)
+			{
+				DOSWAP_32(wb->seekTables[i].entryCount);
+			}
+			wb->seekTables[i].entries = (uint32_t*) pEngine->pMalloc(
+				wb->seekTables[i].entryCount * sizeof(uint32_t)
+			);
+			READ(
+				wb->seekTables[i].entries,
+				wb->seekTables[i].entryCount * sizeof(uint32_t)
+			)
+			if (se)
+			{
+				for (j = 0; j < wb->seekTables[i].entryCount; j += 1)
+				{
+					DOSWAP_32(wb->seekTables[i].entries[j]);
+				}
+			}
+		}
+	}
+	else
+	{
+		wb->seekTables = NULL;
+	}
+
+	/* WaveBank Entry Names */
+	if (wbinfo.dwFlags & FACT_WAVEBANK_FLAGS_ENTRYNAMES)
+	{
+		SEEKSET(header.Segments[FACT_WAVEBANK_SEGIDX_ENTRYNAMES].dwOffset)
+		wb->waveBankNames = (char*) pEngine->pMalloc(64 * wbinfo.dwEntryCount);
+		READ(wb->waveBankNames, 64 * wbinfo.dwEntryCount);
+	}
+	else
+	{
+		wb->waveBankNames = NULL;
+	}
+
+	/* Add to the Engine WaveBank list */
+	LinkedList_AddEntry(
+		&pEngine->wbList,
+		wb,
+		pEngine->wbLock,
+		pEngine->pMalloc
+	);
+
+	/* Finally. */
+	wb->packetBuffer = packetBuffer;
+	wb->packetBufferLen = packetBufferLen;
+	*ppWaveBank = wb;
+	return 0;
+}
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FACT_internal.h b/libs/faudio/src/FACT_internal.h
new file mode 100644
index 00000000000..6bf522e64c4
--- /dev/null
+++ b/libs/faudio/src/FACT_internal.h
@@ -0,0 +1,644 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "FACT.h"
+#include "FACT3D.h"
+#include "FAudio_internal.h"
+
+/* Internal AudioEngine Types */
+
+typedef struct FACTAudioCategory
+{
+	uint8_t instanceLimit;
+	uint16_t fadeInMS;
+	uint16_t fadeOutMS;
+	uint8_t maxInstanceBehavior;
+	int16_t parentCategory;
+	float volume;
+	uint8_t visibility;
+
+	uint8_t instanceCount;
+	float currentVolume;
+} FACTAudioCategory;
+
+typedef struct FACTVariable
+{
+	uint8_t accessibility;
+	float initialValue;
+	float minValue;
+	float maxValue;
+} FACTVariable;
+
+typedef struct FACTRPCPoint
+{
+	float x;
+	float y;
+	uint8_t type;
+} FACTRPCPoint;
+
+typedef enum FACTRPCParameter
+{
+	RPC_PARAMETER_VOLUME,
+	RPC_PARAMETER_PITCH,
+	RPC_PARAMETER_REVERBSEND,
+	RPC_PARAMETER_FILTERFREQUENCY,
+	RPC_PARAMETER_FILTERQFACTOR,
+	RPC_PARAMETER_COUNT /* If >=, DSP Parameter! */
+} FACTRPCParameter;
+
+typedef struct FACTRPC
+{
+	uint16_t variable;
+	uint8_t pointCount;
+	uint16_t parameter;
+	FACTRPCPoint *points;
+} FACTRPC;
+
+typedef struct FACTDSPParameter
+{
+	uint8_t type;
+	float value;
+	float minVal;
+	float maxVal;
+	uint16_t unknown;
+} FACTDSPParameter;
+
+typedef struct FACTDSPPreset
+{
+	uint8_t accessibility;
+	uint16_t parameterCount;
+	FACTDSPParameter *parameters;
+} FACTDSPPreset;
+
+typedef enum FACTNoticationsFlags
+{
+	NOTIFY_CUEPREPARED           = 0x00000001,
+	NOTIFY_CUEPLAY               = 0x00000002,
+	NOTIFY_CUESTOP               = 0x00000004,
+	NOTIFY_CUEDESTROY            = 0x00000008,
+	NOTIFY_MARKER                = 0x00000010,
+	NOTIFY_SOUNDBANKDESTROY      = 0x00000020,
+	NOTIFY_WAVEBANKDESTROY       = 0x00000040,
+	NOTIFY_LOCALVARIABLECHANGED  = 0x00000080,
+	NOTIFY_GLOBALVARIABLECHANGED = 0x00000100,
+	NOTIFY_GUICONNECTED          = 0x00000200,
+	NOTIFY_GUIDISCONNECTED       = 0x00000400,
+	NOTIFY_WAVEPREPARED          = 0x00000800,
+	NOTIFY_WAVEPLAY              = 0x00001000,
+	NOTIFY_WAVESTOP              = 0x00002000,
+	NOTIFY_WAVELOOPED            = 0x00004000,
+	NOTIFY_WAVEDESTROY           = 0x00008000,
+	NOTIFY_WAVEBANKPREPARED      = 0x00010000
+} FACTNoticationsFlags;
+
+/* Internal SoundBank Types */
+
+typedef enum
+{
+	FACTEVENT_STOP =				0,
+	FACTEVENT_PLAYWAVE =				1,
+	FACTEVENT_PLAYWAVETRACKVARIATION =		3,
+	FACTEVENT_PLAYWAVEEFFECTVARIATION =		4,
+	FACTEVENT_PLAYWAVETRACKEFFECTVARIATION =	6,
+	FACTEVENT_PITCH =				7,
+	FACTEVENT_VOLUME =				8,
+	FACTEVENT_MARKER =				9,
+	FACTEVENT_PITCHREPEATING =			16,
+	FACTEVENT_VOLUMEREPEATING =			17,
+	FACTEVENT_MARKERREPEATING =			18
+} FACTEventType;
+
+typedef struct FACTEvent
+{
+	uint16_t type;
+	uint16_t timestamp;
+	uint16_t randomOffset;
+	FAUDIONAMELESS union
+	{
+		/* Play Wave Event */
+		struct
+		{
+			uint8_t flags;
+			uint8_t loopCount;
+			uint16_t position;
+			uint16_t angle;
+
+			/* Track Variation */
+			uint8_t isComplex;
+			FAUDIONAMELESS union
+			{
+				struct
+				{
+					uint16_t track;
+					uint8_t wavebank;
+				} simple;
+				struct
+				{
+					uint16_t variation;
+					uint16_t trackCount;
+					uint16_t *tracks;
+					uint8_t *wavebanks;
+					uint8_t *weights;
+				} complex;
+			};
+
+			/* Effect Variation */
+			int16_t minPitch;
+			int16_t maxPitch;
+			float minVolume;
+			float maxVolume;
+			float minFrequency;
+			float maxFrequency;
+			float minQFactor;
+			float maxQFactor;
+			uint16_t variationFlags;
+		} wave;
+		/* Set Pitch/Volume Event */
+		struct
+		{
+			uint8_t settings;
+			uint16_t repeats;
+			uint16_t frequency;
+			FAUDIONAMELESS union
+			{
+				struct
+				{
+					float initialValue;
+					float initialSlope;
+					float slopeDelta;
+					uint16_t duration;
+				} ramp;
+				struct
+				{
+					uint8_t flags;
+					float value1;
+					float value2;
+				} equation;
+			};
+		} value;
+		/* Stop Event */
+		struct
+		{
+			uint8_t flags;
+		} stop;
+		/* Marker Event */
+		struct
+		{
+			uint32_t marker;
+			uint16_t repeats;
+			uint16_t frequency;
+		} marker;
+	};
+} FACTEvent;
+
+typedef struct FACTTrack
+{
+	uint32_t code;
+
+	float volume;
+	uint8_t filter;
+	uint8_t qfactor;
+	uint16_t frequency;
+
+	uint8_t rpcCodeCount;
+	uint32_t *rpcCodes;
+
+	uint8_t eventCount;
+	FACTEvent *events;
+} FACTTrack;
+
+typedef struct FACTSound
+{
+	uint8_t flags;
+	uint16_t category;
+	float volume;
+	int16_t pitch;
+	uint8_t priority;
+
+	uint8_t trackCount;
+	uint8_t rpcCodeCount;
+	uint8_t dspCodeCount;
+
+	FACTTrack *tracks;
+	uint32_t *rpcCodes;
+	uint32_t *dspCodes;
+} FACTSound;
+
+typedef struct FACTCueData
+{
+	uint8_t flags;
+	uint32_t sbCode;
+	uint32_t transitionOffset;
+	uint8_t instanceLimit;
+	uint16_t fadeInMS;
+	uint16_t fadeOutMS;
+	uint8_t maxInstanceBehavior;
+	uint8_t instanceCount;
+} FACTCueData;
+
+typedef struct FACTVariation
+{
+	FAUDIONAMELESS union
+	{
+		struct
+		{
+			uint16_t track;
+			uint8_t wavebank;
+		} simple;
+		uint32_t soundCode;
+	};
+	float minWeight;
+	float maxWeight;
+	uint32_t linger;
+} FACTVariation;
+
+typedef struct FACTVariationTable
+{
+	uint8_t flags;
+	int16_t variable;
+	uint8_t isComplex;
+
+	uint16_t entryCount;
+	FACTVariation *entries;
+} FACTVariationTable;
+
+typedef struct FACTTransition
+{
+	int32_t soundCode;
+	uint32_t srcMarkerMin;
+	uint32_t srcMarkerMax;
+	uint32_t dstMarkerMin;
+	uint32_t dstMarkerMax;
+	uint16_t fadeIn;
+	uint16_t fadeOut;
+	uint16_t flags;
+} FACTTransition;
+
+typedef struct FACTTransitionTable
+{
+	uint32_t entryCount;
+	FACTTransition *entries;
+} FACTTransitionTable;
+
+/* Internal WaveBank Types */
+
+typedef struct FACTSeekTable
+{
+	uint32_t entryCount;
+	uint32_t *entries;
+} FACTSeekTable;
+
+/* Internal Cue Types */
+
+typedef struct FACTInstanceRPCData
+{
+	float rpcVolume;
+	float rpcPitch;
+	float rpcReverbSend;
+	float rpcFilterFreq;
+	float rpcFilterQFactor;
+} FACTInstanceRPCData;
+
+typedef struct FACTEventInstance
+{
+	uint32_t timestamp;
+	uint16_t loopCount;
+	uint8_t finished;
+	FAUDIONAMELESS union
+	{
+		float value;
+		uint32_t valuei;
+	};
+} FACTEventInstance;
+
+typedef struct FACTTrackInstance
+{
+	/* Tracks which events have fired */
+	FACTEventInstance *events;
+
+	/* RPC instance data */
+	FACTInstanceRPCData rpcData;
+
+	/* SetPitch/SetVolume data */
+	float evtPitch;
+	float evtVolume;
+
+	/* Wave playback */
+	struct
+	{
+		FACTWave *wave;
+		float baseVolume;
+		int16_t basePitch;
+		float baseQFactor;
+		float baseFrequency;
+	} activeWave, upcomingWave;
+	FACTEvent *waveEvt;
+	FACTEventInstance *waveEvtInst;
+} FACTTrackInstance;
+
+typedef struct FACTSoundInstance
+{
+	/* Base Sound reference */
+	FACTSound *sound;
+
+	/* Per-instance track information */
+	FACTTrackInstance *tracks;
+
+	/* RPC instance data */
+	FACTInstanceRPCData rpcData;
+
+	/* Fade data */
+	uint32_t fadeStart;
+	uint16_t fadeTarget;
+	uint8_t fadeType; /* In (1), Out (2), Release RPC (3) */
+
+	/* Engine references */
+	FACTCue *parentCue;
+} FACTSoundInstance;
+
+/* Internal Wave Types */
+
+typedef struct FACTWaveCallback
+{
+	FAudioVoiceCallback callback;
+	FACTWave *wave;
+} FACTWaveCallback;
+
+/* Public XACT Types */
+
+struct FACTAudioEngine
+{
+	uint32_t refcount;
+	FACTNotificationCallback notificationCallback;
+	FACTReadFileCallback pReadFile;
+	FACTGetOverlappedResultCallback pGetOverlappedResult;
+
+	uint16_t categoryCount;
+	uint16_t variableCount;
+	uint16_t rpcCount;
+	uint16_t dspPresetCount;
+	uint16_t dspParameterCount;
+
+	char **categoryNames;
+	char **variableNames;
+	uint32_t *rpcCodes;
+	uint32_t *dspPresetCodes;
+
+	FACTAudioCategory *categories;
+	FACTVariable *variables;
+	FACTRPC *rpcs;
+	FACTDSPPreset *dspPresets;
+
+	/* Engine references */
+	LinkedList *sbList;
+	LinkedList *wbList;
+	FAudioMutex sbLock;
+	FAudioMutex wbLock;
+	float *globalVariableValues;
+
+	/* FAudio references */
+	FAudio *audio;
+	FAudioMasteringVoice *master;
+	FAudioSubmixVoice *reverbVoice;
+
+	/* Engine thread */
+	FAudioThread apiThread;
+	FAudioMutex apiLock;
+	uint8_t initialized;
+
+	/* Allocator callbacks */
+	FAudioMallocFunc pMalloc;
+	FAudioFreeFunc pFree;
+	FAudioReallocFunc pRealloc;
+
+	/* Peristent Notifications */
+	FACTNoticationsFlags notifications;
+	void *cue_context;
+	void *sb_context;
+	void *wb_context;
+	void *wave_context;
+
+	/* Settings handle */
+	void *settings;
+};
+
+struct FACTSoundBank
+{
+	/* Engine references */
+	FACTAudioEngine *parentEngine;
+	FACTCue *cueList;
+	uint8_t notifyOnDestroy;
+	void *usercontext;
+
+	/* Array sizes */
+	uint16_t cueCount;
+	uint8_t wavebankCount;
+	uint16_t soundCount;
+	uint16_t variationCount;
+	uint16_t transitionCount;
+
+	/* Strings, strings everywhere! */
+	char **wavebankNames;
+	char **cueNames;
+
+	/* Actual SoundBank information */
+	char *name;
+	FACTCueData *cues;
+	FACTSound *sounds;
+	uint32_t *soundCodes;
+	FACTVariationTable *variations;
+	uint32_t *variationCodes;
+	FACTTransitionTable *transitions;
+	uint32_t *transitionCodes;
+};
+
+struct FACTWaveBank
+{
+	/* Engine references */
+	FACTAudioEngine *parentEngine;
+	LinkedList *waveList;
+	FAudioMutex waveLock;
+	uint8_t notifyOnDestroy;
+	void *usercontext;
+
+	/* Actual WaveBank information */
+	char *name;
+	uint32_t entryCount;
+	FACTWaveBankEntry *entries;
+	uint32_t *entryRefs;
+	FACTSeekTable *seekTables;
+	char *waveBankNames;
+
+	/* I/O information */
+	uint32_t packetSize;
+	uint16_t streaming;
+	uint8_t *packetBuffer;
+	uint32_t packetBufferLen;
+	void* io;
+};
+
+struct FACTWave
+{
+	/* Engine references */
+	FACTWaveBank *parentBank;
+	FACTCue *parentCue;
+	uint16_t index;
+	uint8_t notifyOnDestroy;
+	void *usercontext;
+
+	/* Playback */
+	uint32_t state;
+	float volume;
+	int16_t pitch;
+	uint8_t loopCount;
+
+	/* Stream data */
+	uint32_t streamSize;
+	uint32_t streamOffset;
+	uint8_t *streamCache;
+
+	/* FAudio references */
+	uint16_t srcChannels;
+	FAudioSourceVoice *voice;
+	FACTWaveCallback callback;
+};
+
+struct FACTCue
+{
+	/* Engine references */
+	FACTSoundBank *parentBank;
+	FACTCue *next;
+	uint8_t managed;
+	uint16_t index;
+	uint8_t notifyOnDestroy;
+	void *usercontext;
+
+	/* Sound data */
+	FACTCueData *data;
+	FAUDIONAMELESS union
+	{
+		FACTVariationTable *variation;
+
+		/* This is only used in scenarios where there is only one
+		 * Sound; XACT does not generate variation tables for
+		 * Cues with only one Sound.
+		 */
+		FACTSound *sound;
+	};
+
+	/* Instance data */
+	float *variableValues;
+	float interactive;
+
+	/* Playback */
+	uint32_t state;
+	FACTWave *simpleWave;
+	FACTSoundInstance *playingSound;
+	FACTVariation *playingVariation;
+	uint32_t maxRpcReleaseTime;
+
+	/* 3D Data */
+	uint8_t active3D;
+	uint32_t srcChannels;
+	uint32_t dstChannels;
+	float matrixCoefficients[2 * 8]; /* Stereo input, 7.1 output */
+
+	/* Timer */
+	uint32_t start;
+	uint32_t elapsed;
+};
+
+/* Internal functions */
+
+void FACT_INTERNAL_GetNextWave(
+	FACTCue *cue,
+	FACTSound *sound,
+	FACTTrack *track,
+	FACTTrackInstance *trackInst,
+	FACTEvent *evt,
+	FACTEventInstance *evtInst
+);
+uint8_t FACT_INTERNAL_CreateSound(FACTCue *cue, uint16_t fadeInMS);
+void FACT_INTERNAL_DestroySound(FACTSoundInstance *sound);
+void FACT_INTERNAL_BeginFadeOut(FACTSoundInstance *sound, uint16_t fadeOutMS);
+void FACT_INTERNAL_BeginReleaseRPC(FACTSoundInstance *sound, uint16_t releaseMS);
+
+void FACT_INTERNAL_SendCueNotification(FACTCue *cue, FACTNoticationsFlags flag, uint8_t type);
+
+/* RPC Helper Functions */
+
+FACTRPC* FACT_INTERNAL_GetRPC(FACTAudioEngine *engine, uint32_t code);
+
+/* FACT Thread */
+
+int32_t FAUDIOCALL FACT_INTERNAL_APIThread(void* enginePtr);
+
+/* FAudio callbacks */
+
+void FACT_INTERNAL_OnBufferEnd(FAudioVoiceCallback *callback, void* pContext);
+void FACT_INTERNAL_OnStreamEnd(FAudioVoiceCallback *callback);
+
+/* FAudioIOStream functions */
+
+int32_t FACTCALL FACT_INTERNAL_DefaultReadFile(
+	void *hFile,
+	void *buffer,
+	uint32_t nNumberOfBytesToRead,
+	uint32_t *lpNumberOfBytesRead,
+	FACTOverlapped *lpOverlapped
+);
+
+int32_t FACTCALL FACT_INTERNAL_DefaultGetOverlappedResult(
+	void *hFile,
+	FACTOverlapped *lpOverlapped,
+	uint32_t *lpNumberOfBytesTransferred,
+	int32_t bWait
+);
+
+/* Parsing functions */
+
+uint32_t FACT_INTERNAL_ParseAudioEngine(
+	FACTAudioEngine *pEngine,
+	const FACTRuntimeParameters *pParams
+);
+uint32_t FACT_INTERNAL_ParseSoundBank(
+	FACTAudioEngine *pEngine,
+	const void *pvBuffer,
+	uint32_t dwSize,
+	FACTSoundBank **ppSoundBank
+);
+uint32_t FACT_INTERNAL_ParseWaveBank(
+	FACTAudioEngine *pEngine,
+	void* io,
+	uint32_t offset,
+	uint32_t packetSize,
+	FACTReadFileCallback pRead,
+	FACTGetOverlappedResultCallback pOverlap,
+	uint16_t isStreaming,
+	FACTWaveBank **ppWaveBank
+);
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FAPOBase.c b/libs/faudio/src/FAPOBase.c
new file mode 100644
index 00000000000..91b294ddab2
--- /dev/null
+++ b/libs/faudio/src/FAPOBase.c
@@ -0,0 +1,451 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "FAPOBase.h"
+#include "FAudio_internal.h"
+
+/* FAPOBase Interface */
+
+void CreateFAPOBase(
+	FAPOBase *fapo,
+	const FAPORegistrationProperties *pRegistrationProperties,
+	uint8_t *pParameterBlocks,
+	uint32_t uParameterBlockByteSize,
+	uint8_t fProducer
+) {
+	CreateFAPOBaseWithCustomAllocatorEXT(
+		fapo,
+		pRegistrationProperties,
+		pParameterBlocks,
+		uParameterBlockByteSize,
+		fProducer,
+		FAudio_malloc,
+		FAudio_free,
+		FAudio_realloc
+	);
+}
+
+void CreateFAPOBaseWithCustomAllocatorEXT(
+	FAPOBase *fapo,
+	const FAPORegistrationProperties *pRegistrationProperties,
+	uint8_t *pParameterBlocks,
+	uint32_t uParameterBlockByteSize,
+	uint8_t fProducer,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc
+) {
+	/* Base Classes/Interfaces */
+	#define ASSIGN_VT(name) \
+		fapo->base.name = (name##Func) FAPOBase_##name;
+	ASSIGN_VT(AddRef)
+	ASSIGN_VT(Release)
+	ASSIGN_VT(GetRegistrationProperties)
+	ASSIGN_VT(IsInputFormatSupported)
+	ASSIGN_VT(IsOutputFormatSupported)
+	ASSIGN_VT(Initialize)
+	ASSIGN_VT(Reset)
+	ASSIGN_VT(LockForProcess)
+	ASSIGN_VT(UnlockForProcess)
+	ASSIGN_VT(CalcInputFrames)
+	ASSIGN_VT(CalcOutputFrames)
+	ASSIGN_VT(SetParameters)
+	ASSIGN_VT(GetParameters)
+	#undef ASSIGN_VT
+
+	/* Public Virtual Functions */
+	fapo->OnSetParameters = (OnSetParametersFunc)
+		FAPOBase_OnSetParameters;
+
+	/* Private Variables */
+	fapo->m_pRegistrationProperties = pRegistrationProperties; /* FIXME */
+	fapo->m_pfnMatrixMixFunction = NULL; /* FIXME */
+	fapo->m_pfl32MatrixCoefficients = NULL; /* FIXME */
+	fapo->m_nSrcFormatType = 0; /* FIXME */
+	fapo->m_fIsScalarMatrix = 0; /* FIXME: */
+	fapo->m_fIsLocked = 0;
+	fapo->m_pParameterBlocks = pParameterBlocks;
+	fapo->m_pCurrentParameters = pParameterBlocks;
+	fapo->m_pCurrentParametersInternal = pParameterBlocks;
+	fapo->m_uCurrentParametersIndex = 0;
+	fapo->m_uParameterBlockByteSize = uParameterBlockByteSize;
+	fapo->m_fNewerResultsReady = 0;
+	fapo->m_fProducer = fProducer;
+
+	/* Allocator Callbacks */
+	fapo->pMalloc = customMalloc;
+	fapo->pFree = customFree;
+	fapo->pRealloc = customRealloc;
+
+	/* Protected Variables */
+	fapo->m_lReferenceCount = 1;
+}
+
+int32_t FAPOBase_AddRef(FAPOBase *fapo)
+{
+	fapo->m_lReferenceCount += 1;
+	return fapo->m_lReferenceCount;
+}
+
+int32_t FAPOBase_Release(FAPOBase *fapo)
+{
+	fapo->m_lReferenceCount -= 1;
+	if (fapo->m_lReferenceCount == 0)
+	{
+		fapo->Destructor(fapo);
+		return 0;
+	}
+	return fapo->m_lReferenceCount;
+}
+
+uint32_t FAPOBase_GetRegistrationProperties(
+	FAPOBase *fapo,
+	FAPORegistrationProperties **ppRegistrationProperties
+) {
+	*ppRegistrationProperties = (FAPORegistrationProperties*) fapo->pMalloc(
+		sizeof(FAPORegistrationProperties)
+	);
+	FAudio_memcpy(
+		*ppRegistrationProperties,
+		fapo->m_pRegistrationProperties,
+		sizeof(FAPORegistrationProperties)
+	);
+	return 0;
+}
+
+uint32_t FAPOBase_IsInputFormatSupported(
+	FAPOBase *fapo,
+	const FAudioWaveFormatEx *pOutputFormat,
+	const FAudioWaveFormatEx *pRequestedInputFormat,
+	FAudioWaveFormatEx **ppSupportedInputFormat
+) {
+	if (	pRequestedInputFormat->wFormatTag != FAPOBASE_DEFAULT_FORMAT_TAG ||
+		pRequestedInputFormat->nChannels < FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS ||
+		pRequestedInputFormat->nChannels > FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS ||
+		pRequestedInputFormat->nSamplesPerSec < FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE ||
+		pRequestedInputFormat->nSamplesPerSec > FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE ||
+		pRequestedInputFormat->wBitsPerSample != FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE	)
+	{
+		if (ppSupportedInputFormat != NULL)
+		{
+			(*ppSupportedInputFormat)->wFormatTag =
+				FAPOBASE_DEFAULT_FORMAT_TAG;
+			(*ppSupportedInputFormat)->nChannels = FAudio_clamp(
+				pRequestedInputFormat->nChannels,
+				FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS,
+				FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS
+			);
+			(*ppSupportedInputFormat)->nSamplesPerSec = FAudio_clamp(
+				pRequestedInputFormat->nSamplesPerSec,
+				FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE,
+				FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE
+			);
+			(*ppSupportedInputFormat)->wBitsPerSample =
+				FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE;
+		}
+		return FAPO_E_FORMAT_UNSUPPORTED;
+	}
+	return 0;
+}
+
+uint32_t FAPOBase_IsOutputFormatSupported(
+	FAPOBase *fapo,
+	const FAudioWaveFormatEx *pInputFormat,
+	const FAudioWaveFormatEx *pRequestedOutputFormat,
+	FAudioWaveFormatEx **ppSupportedOutputFormat
+) {
+	if (	pRequestedOutputFormat->wFormatTag != FAPOBASE_DEFAULT_FORMAT_TAG ||
+		pRequestedOutputFormat->nChannels < FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS ||
+		pRequestedOutputFormat->nChannels > FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS ||
+		pRequestedOutputFormat->nSamplesPerSec < FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE ||
+		pRequestedOutputFormat->nSamplesPerSec > FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE ||
+		pRequestedOutputFormat->wBitsPerSample != FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE	)
+	{
+		if (ppSupportedOutputFormat != NULL)
+		{
+			(*ppSupportedOutputFormat)->wFormatTag =
+				FAPOBASE_DEFAULT_FORMAT_TAG;
+			(*ppSupportedOutputFormat)->nChannels = FAudio_clamp(
+				pRequestedOutputFormat->nChannels,
+				FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS,
+				FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS
+			);
+			(*ppSupportedOutputFormat)->nSamplesPerSec = FAudio_clamp(
+				pRequestedOutputFormat->nSamplesPerSec,
+				FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE,
+				FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE
+			);
+			(*ppSupportedOutputFormat)->wBitsPerSample =
+				FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE;
+		}
+		return FAPO_E_FORMAT_UNSUPPORTED;
+	}
+	return 0;
+}
+
+uint32_t FAPOBase_Initialize(
+	FAPOBase *fapo,
+	const void* pData,
+	uint32_t DataByteSize
+) {
+	return 0;
+}
+
+void FAPOBase_Reset(FAPOBase *fapo)
+{
+}
+
+uint32_t FAPOBase_LockForProcess(
+	FAPOBase *fapo,
+	uint32_t InputLockedParameterCount,
+	const FAPOLockForProcessBufferParameters *pInputLockedParameters,
+	uint32_t OutputLockedParameterCount,
+	const FAPOLockForProcessBufferParameters *pOutputLockedParameters
+) {
+	/* Verify parameter counts... */
+	if (	InputLockedParameterCount < fapo->m_pRegistrationProperties->MinInputBufferCount ||
+		InputLockedParameterCount > fapo->m_pRegistrationProperties->MaxInputBufferCount ||
+		OutputLockedParameterCount < fapo->m_pRegistrationProperties->MinOutputBufferCount ||
+		OutputLockedParameterCount > fapo->m_pRegistrationProperties->MaxOutputBufferCount	)
+	{
+		return FAUDIO_E_INVALID_ARG;
+	}
+
+
+	/* Validate input/output formats */
+	#define VERIFY_FORMAT_FLAG(flag, prop) \
+		if (	(fapo->m_pRegistrationProperties->Flags & flag) && \
+			(pInputLockedParameters->pFormat->prop != pOutputLockedParameters->pFormat->prop)	) \
+		{ \
+			return FAUDIO_E_INVALID_ARG; \
+		}
+	VERIFY_FORMAT_FLAG(FAPO_FLAG_CHANNELS_MUST_MATCH, nChannels)
+	VERIFY_FORMAT_FLAG(FAPO_FLAG_FRAMERATE_MUST_MATCH, nSamplesPerSec)
+	VERIFY_FORMAT_FLAG(FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH, wBitsPerSample)
+	#undef VERIFY_FORMAT_FLAG
+	if (	(fapo->m_pRegistrationProperties->Flags & FAPO_FLAG_BUFFERCOUNT_MUST_MATCH) &&
+		(InputLockedParameterCount != OutputLockedParameterCount)	)
+	{
+		return FAUDIO_E_INVALID_ARG;
+	}
+	fapo->m_fIsLocked = 1;
+	return 0;
+}
+
+void FAPOBase_UnlockForProcess(FAPOBase *fapo)
+{
+	fapo->m_fIsLocked = 0;
+}
+
+uint32_t FAPOBase_CalcInputFrames(FAPOBase *fapo, uint32_t OutputFrameCount)
+{
+	return OutputFrameCount;
+}
+
+uint32_t FAPOBase_CalcOutputFrames(FAPOBase *fapo, uint32_t InputFrameCount)
+{
+	return InputFrameCount;
+}
+
+uint32_t FAPOBase_ValidateFormatDefault(
+	FAPOBase *fapo,
+	FAudioWaveFormatEx *pFormat,
+	uint8_t fOverwrite
+) {
+	if (	pFormat->wFormatTag != FAPOBASE_DEFAULT_FORMAT_TAG ||
+		pFormat->nChannels < FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS ||
+		pFormat->nChannels > FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS ||
+		pFormat->nSamplesPerSec < FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE ||
+		pFormat->nSamplesPerSec > FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE ||
+		pFormat->wBitsPerSample != FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE	)
+	{
+		if (fOverwrite)
+		{
+			pFormat->wFormatTag =
+				FAPOBASE_DEFAULT_FORMAT_TAG;
+			pFormat->nChannels = FAudio_clamp(
+				pFormat->nChannels,
+				FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS,
+				FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS
+			);
+			pFormat->nSamplesPerSec = FAudio_clamp(
+				pFormat->nSamplesPerSec,
+				FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE,
+				FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE
+			);
+			pFormat->wBitsPerSample =
+				FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE;
+		}
+		return FAPO_E_FORMAT_UNSUPPORTED;
+	}
+	return 0;
+}
+
+uint32_t FAPOBase_ValidateFormatPair(
+	FAPOBase *fapo,
+	const FAudioWaveFormatEx *pSupportedFormat,
+	FAudioWaveFormatEx *pRequestedFormat,
+	uint8_t fOverwrite
+) {
+	if (	pRequestedFormat->wFormatTag != FAPOBASE_DEFAULT_FORMAT_TAG ||
+		pRequestedFormat->nChannels < FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS ||
+		pRequestedFormat->nChannels > FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS ||
+		pRequestedFormat->nSamplesPerSec < FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE ||
+		pRequestedFormat->nSamplesPerSec > FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE ||
+		pRequestedFormat->wBitsPerSample != FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE	)
+	{
+		if (fOverwrite)
+		{
+			pRequestedFormat->wFormatTag =
+				FAPOBASE_DEFAULT_FORMAT_TAG;
+			pRequestedFormat->nChannels = FAudio_clamp(
+				pRequestedFormat->nChannels,
+				FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS,
+				FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS
+			);
+			pRequestedFormat->nSamplesPerSec = FAudio_clamp(
+				pRequestedFormat->nSamplesPerSec,
+				FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE,
+				FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE
+			);
+			pRequestedFormat->wBitsPerSample =
+				FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE;
+		}
+		return FAPO_E_FORMAT_UNSUPPORTED;
+	}
+	return 0;
+}
+
+void FAPOBase_ProcessThru(
+	FAPOBase *fapo,
+	void* pInputBuffer,
+	float *pOutputBuffer,
+	uint32_t FrameCount,
+	uint16_t InputChannelCount,
+	uint16_t OutputChannelCount,
+	uint8_t MixWithOutput
+) {
+	uint32_t i, co, ci;
+	float *input = (float*) pInputBuffer;
+
+	if (MixWithOutput)
+	{
+		/* TODO: SSE */
+		for (i = 0; i < FrameCount; i += 1)
+		for (co = 0; co < OutputChannelCount; co += 1)
+		for (ci = 0; ci < InputChannelCount; ci += 1)
+		{
+			/* Add, don't overwrite! */
+			pOutputBuffer[i * OutputChannelCount + co] +=
+				input[i * InputChannelCount + ci];
+		}
+	}
+	else
+	{
+		/* TODO: SSE */
+		for (i = 0; i < FrameCount; i += 1)
+		for (co = 0; co < OutputChannelCount; co += 1)
+		for (ci = 0; ci < InputChannelCount; ci += 1)
+		{
+			/* Overwrite, don't add! */
+			pOutputBuffer[i * OutputChannelCount + co] =
+				input[i * InputChannelCount + ci];
+		}
+	}
+}
+
+void FAPOBase_SetParameters(
+	FAPOBase *fapo,
+	const void* pParameters,
+	uint32_t ParameterByteSize
+) {
+	FAudio_assert(!fapo->m_fProducer);
+
+	/* User callback for validation */
+	fapo->OnSetParameters(
+		fapo,
+		pParameters,
+		ParameterByteSize
+	);
+
+	/* Increment parameter block index... */
+	fapo->m_uCurrentParametersIndex += 1;
+	if (fapo->m_uCurrentParametersIndex == 3)
+	{
+		fapo->m_uCurrentParametersIndex = 0;
+	}
+	fapo->m_pCurrentParametersInternal = fapo->m_pParameterBlocks + (
+		fapo->m_uParameterBlockByteSize *
+		fapo->m_uCurrentParametersIndex
+	);
+
+	/* Copy to what will eventually be the next parameter update */
+	FAudio_memcpy(
+		fapo->m_pCurrentParametersInternal,
+		pParameters,
+		ParameterByteSize
+	);
+}
+
+void FAPOBase_GetParameters(
+	FAPOBase *fapo,
+	void* pParameters,
+	uint32_t ParameterByteSize
+) {
+	/* Copy what's current as of the last Process */
+	FAudio_memcpy(
+		pParameters,
+		fapo->m_pCurrentParameters,
+		ParameterByteSize
+	);
+}
+
+void FAPOBase_OnSetParameters(
+	FAPOBase *fapo,
+	const void* parameters,
+	uint32_t parametersSize
+) {
+}
+
+uint8_t FAPOBase_ParametersChanged(FAPOBase *fapo)
+{
+	/* Internal will get updated when SetParameters is called */
+	return fapo->m_pCurrentParametersInternal != fapo->m_pCurrentParameters;
+}
+
+uint8_t* FAPOBase_BeginProcess(FAPOBase *fapo)
+{
+	/* Set the latest block as "current", this is what Process will use now */
+	fapo->m_pCurrentParameters = fapo->m_pCurrentParametersInternal;
+	return fapo->m_pCurrentParameters;
+}
+
+void FAPOBase_EndProcess(FAPOBase *fapo)
+{
+	/* I'm 100% sure my parameter block increment is wrong... */
+}
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FAPOFX.c b/libs/faudio/src/FAPOFX.c
new file mode 100644
index 00000000000..70739bd0614
--- /dev/null
+++ b/libs/faudio/src/FAPOFX.c
@@ -0,0 +1,89 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "FAPOFX.h"
+#include "FAudio_internal.h"
+
+uint32_t FAPOFX_CreateFX(
+	const FAudioGUID *clsid,
+	FAPO **pEffect,
+	const void *pInitData,
+	uint32_t InitDataByteSize
+) {
+	return FAPOFX_CreateFXWithCustomAllocatorEXT(
+		clsid,
+		pEffect,
+		pInitData,
+		InitDataByteSize,
+		FAudio_malloc,
+		FAudio_free,
+		FAudio_realloc
+	);
+}
+
+uint32_t FAPOFX_CreateFXWithCustomAllocatorEXT(
+	const FAudioGUID *clsid,
+	FAPO **pEffect,
+	const void *pInitData,
+	uint32_t InitDataByteSize,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc
+) {
+	#define CHECK_AND_RETURN(effect) \
+		if (FAudio_memcmp(clsid, &FAPOFX_CLSID_FX##effect, sizeof(FAudioGUID)) == 0) \
+		{ \
+			return FAPOFXCreate##effect( \
+				pEffect, \
+				pInitData, \
+				InitDataByteSize, \
+				customMalloc, \
+				customFree, \
+				customRealloc, \
+				0 \
+			); \
+		} \
+		else if (FAudio_memcmp(clsid, &FAPOFX_CLSID_FX##effect##_LEGACY, sizeof(FAudioGUID)) == 0) \
+		{ \
+			return FAPOFXCreate##effect( \
+				pEffect, \
+				pInitData, \
+				InitDataByteSize, \
+				customMalloc, \
+				customFree, \
+				customRealloc, \
+				1 \
+			); \
+		}
+	CHECK_AND_RETURN(EQ)
+	CHECK_AND_RETURN(MasteringLimiter)
+	CHECK_AND_RETURN(Reverb)
+	CHECK_AND_RETURN(Echo)
+	#undef CHECK_AND_RETURN
+	return -1;
+}
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FAPOFX_echo.c b/libs/faudio/src/FAPOFX_echo.c
new file mode 100644
index 00000000000..d87d5c5b95b
--- /dev/null
+++ b/libs/faudio/src/FAPOFX_echo.c
@@ -0,0 +1,248 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "FAPOFX.h"
+#include "FAudio_internal.h"
+
+/* FXEcho FAPO Implementation */
+
+const FAudioGUID FAPOFX_CLSID_FXEcho =
+{
+	0x5039D740,
+	0xF736,
+	0x449A,
+	{
+		0x84,
+		0xD3,
+		0xA5,
+		0x62,
+		0x02,
+		0x55,
+		0x7B,
+		0x87
+	}
+};
+
+static FAPORegistrationProperties FXEchoProperties =
+{
+	/* .clsid = */ {0},
+	/* .FriendlyName = */
+	{
+		'F', 'X', 'E', 'c', 'h', 'o', '\0'
+	},
+	/*.CopyrightInfo = */
+	{
+		'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')',
+		'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0'
+	},
+	/*.MajorVersion = */ 0,
+	/*.MinorVersion = */ 0,
+	/*.Flags = */(
+		FAPO_FLAG_FRAMERATE_MUST_MATCH |
+		FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH |
+		FAPO_FLAG_BUFFERCOUNT_MUST_MATCH |
+		FAPO_FLAG_INPLACE_SUPPORTED |
+		FAPO_FLAG_INPLACE_REQUIRED
+	),
+	/*.MinInputBufferCount = */ 1,
+	/*.MaxInputBufferCount = */  1,
+	/*.MinOutputBufferCount = */ 1,
+	/*.MaxOutputBufferCount =*/ 1
+};
+
+const FAudioGUID FAPOFX_CLSID_FXEcho_LEGACY =
+{
+	0xA90BC001,
+	0xE897,
+	0xE897,
+	{
+		0x74,
+		0x39,
+		0x43,
+		0x55,
+		0x00,
+		0x00,
+		0x00,
+		0x03
+	}
+};
+
+static FAPORegistrationProperties FXEchoProperties_LEGACY =
+{
+	/* .clsid = */ {0},
+	/* .FriendlyName = */
+	{
+		'F', 'X', 'E', 'c', 'h', 'o', '\0'
+	},
+	/*.CopyrightInfo = */
+	{
+		'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')',
+		'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0'
+	},
+	/*.MajorVersion = */ 0,
+	/*.MinorVersion = */ 0,
+	/*.Flags = */(
+		FAPO_FLAG_FRAMERATE_MUST_MATCH |
+		FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH |
+		FAPO_FLAG_BUFFERCOUNT_MUST_MATCH |
+		FAPO_FLAG_INPLACE_SUPPORTED |
+		FAPO_FLAG_INPLACE_REQUIRED
+	),
+	/*.MinInputBufferCount = */ 1,
+	/*.MaxInputBufferCount = */  1,
+	/*.MinOutputBufferCount = */ 1,
+	/*.MaxOutputBufferCount =*/ 1
+};
+
+typedef struct FAPOFXEcho
+{
+	FAPOBase base;
+
+	/* TODO */
+} FAPOFXEcho;
+
+uint32_t FAPOFXEcho_Initialize(
+	FAPOFXEcho *fapo,
+	const void* pData,
+	uint32_t DataByteSize
+) {
+	#define INITPARAMS(offset) \
+		FAudio_memcpy( \
+			fapo->base.m_pParameterBlocks + DataByteSize * offset, \
+			pData, \
+			DataByteSize \
+		);
+	INITPARAMS(0)
+	INITPARAMS(1)
+	INITPARAMS(2)
+	#undef INITPARAMS
+	return 0;
+}
+
+void FAPOFXEcho_Process(
+	FAPOFXEcho *fapo,
+	uint32_t InputProcessParameterCount,
+	const FAPOProcessBufferParameters* pInputProcessParameters,
+	uint32_t OutputProcessParameterCount,
+	FAPOProcessBufferParameters* pOutputProcessParameters,
+	int32_t IsEnabled
+) {
+	FAPOBase_BeginProcess(&fapo->base);
+
+	/* TODO */
+
+	FAPOBase_EndProcess(&fapo->base);
+}
+
+void FAPOFXEcho_Free(void* fapo)
+{
+	FAPOFXEcho *echo = (FAPOFXEcho*) fapo;
+	echo->base.pFree(echo->base.m_pParameterBlocks);
+	echo->base.pFree(fapo);
+}
+
+/* Public API */
+
+uint32_t FAPOFXCreateEcho(
+	FAPO **pEffect,
+	const void *pInitData,
+	uint32_t InitDataByteSize,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc,
+	uint8_t legacy
+) {
+	const FAPOFXEchoParameters fxdefault =
+	{
+		FAPOFXECHO_DEFAULT_WETDRYMIX,
+		FAPOFXECHO_DEFAULT_FEEDBACK,
+		FAPOFXECHO_DEFAULT_DELAY
+	};
+
+	/* Allocate... */
+	FAPOFXEcho *result = (FAPOFXEcho*) customMalloc(
+		sizeof(FAPOFXEcho)
+	);
+	uint8_t *params = (uint8_t*) customMalloc(
+		sizeof(FAPOFXEchoParameters) * 3
+	);
+	if (pInitData == NULL)
+	{
+		FAudio_zero(params, sizeof(FAPOFXEchoParameters) * 3);
+		#define INITPARAMS(offset) \
+			FAudio_memcpy( \
+				params + sizeof(FAPOFXEchoParameters) * offset, \
+				&fxdefault, \
+				sizeof(FAPOFXEchoParameters) \
+			);
+		INITPARAMS(0)
+		INITPARAMS(1)
+		INITPARAMS(2)
+		#undef INITPARAMS
+	}
+	else
+	{
+		FAudio_assert(InitDataByteSize == sizeof(FAPOFXEchoParameters));
+		FAudio_memcpy(params, pInitData, InitDataByteSize);
+		FAudio_memcpy(params + InitDataByteSize, pInitData, InitDataByteSize);
+		FAudio_memcpy(params + (InitDataByteSize * 2), pInitData, InitDataByteSize);
+	}
+
+	/* Initialize... */
+	FAudio_memcpy(
+		&FXEchoProperties_LEGACY.clsid,
+		&FAPOFX_CLSID_FXEcho_LEGACY,
+		sizeof(FAudioGUID)
+	);
+	FAudio_memcpy(
+		&FXEchoProperties.clsid,
+		&FAPOFX_CLSID_FXEcho,
+		sizeof(FAudioGUID)
+	);
+	CreateFAPOBaseWithCustomAllocatorEXT(
+		&result->base,
+		legacy ? &FXEchoProperties_LEGACY : &FXEchoProperties,
+		params,
+		sizeof(FAPOFXEchoParameters),
+		0,
+		customMalloc,
+		customFree,
+		customRealloc
+	);
+
+	/* Function table... */
+	result->base.base.Initialize = (InitializeFunc)
+		FAPOFXEcho_Initialize;
+	result->base.base.Process = (ProcessFunc)
+		FAPOFXEcho_Process;
+	result->base.Destructor = FAPOFXEcho_Free;
+
+	/* Finally. */
+	*pEffect = &result->base.base;
+	return 0;
+}
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FAPOFX_eq.c b/libs/faudio/src/FAPOFX_eq.c
new file mode 100644
index 00000000000..4ac45728aa4
--- /dev/null
+++ b/libs/faudio/src/FAPOFX_eq.c
@@ -0,0 +1,257 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "FAPOFX.h"
+#include "FAudio_internal.h"
+
+/* FXEQ FAPO Implementation */
+
+const FAudioGUID FAPOFX_CLSID_FXEQ =
+{
+	0xF5E01117,
+	0xD6C4,
+	0x485A,
+	{
+		0xA3,
+		0xF5,
+		0x69,
+		0x51,
+		0x96,
+		0xF3,
+		0xDB,
+		0xFA
+	}
+};
+
+static FAPORegistrationProperties FXEQProperties =
+{
+	/* .clsid = */ {0},
+	/* .FriendlyName = */
+	{
+		'F', 'X', 'E', 'Q', '\0'
+	},
+	/*.CopyrightInfo = */
+	{
+		'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')',
+		'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0'
+	},
+	/*.MajorVersion = */ 0,
+	/*.MinorVersion = */ 0,
+	/*.Flags = */(
+		FAPO_FLAG_FRAMERATE_MUST_MATCH |
+		FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH |
+		FAPO_FLAG_BUFFERCOUNT_MUST_MATCH |
+		FAPO_FLAG_INPLACE_SUPPORTED |
+		FAPO_FLAG_INPLACE_REQUIRED
+	),
+	/*.MinInputBufferCount = */ 1,
+	/*.MaxInputBufferCount = */  1,
+	/*.MinOutputBufferCount = */ 1,
+	/*.MaxOutputBufferCount =*/ 1
+};
+
+const FAudioGUID FAPOFX_CLSID_FXEQ_LEGACY =
+{
+	0xA90BC001,
+	0xE897,
+	0xE897,
+	{
+		0x74,
+		0x39,
+		0x43,
+		0x55,
+		0x00,
+		0x00,
+		0x00,
+		0x00
+	}
+};
+
+static FAPORegistrationProperties FXEQProperties_LEGACY =
+{
+	/* .clsid = */ {0},
+	/* .FriendlyName = */
+	{
+		'F', 'X', 'E', 'Q', '\0'
+	},
+	/*.CopyrightInfo = */
+	{
+		'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')',
+		'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0'
+	},
+	/*.MajorVersion = */ 0,
+	/*.MinorVersion = */ 0,
+	/*.Flags = */(
+		FAPO_FLAG_FRAMERATE_MUST_MATCH |
+		FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH |
+		FAPO_FLAG_BUFFERCOUNT_MUST_MATCH |
+		FAPO_FLAG_INPLACE_SUPPORTED |
+		FAPO_FLAG_INPLACE_REQUIRED
+	),
+	/*.MinInputBufferCount = */ 1,
+	/*.MaxInputBufferCount = */  1,
+	/*.MinOutputBufferCount = */ 1,
+	/*.MaxOutputBufferCount =*/ 1
+};
+
+typedef struct FAPOFXEQ
+{
+	FAPOBase base;
+
+	/* TODO */
+} FAPOFXEQ;
+
+uint32_t FAPOFXEQ_Initialize(
+	FAPOFXEQ *fapo,
+	const void* pData,
+	uint32_t DataByteSize
+) {
+	#define INITPARAMS(offset) \
+		FAudio_memcpy( \
+			fapo->base.m_pParameterBlocks + DataByteSize * offset, \
+			pData, \
+			DataByteSize \
+		);
+	INITPARAMS(0)
+	INITPARAMS(1)
+	INITPARAMS(2)
+	#undef INITPARAMS
+	return 0;
+}
+
+void FAPOFXEQ_Process(
+	FAPOFXEQ *fapo,
+	uint32_t InputProcessParameterCount,
+	const FAPOProcessBufferParameters* pInputProcessParameters,
+	uint32_t OutputProcessParameterCount,
+	FAPOProcessBufferParameters* pOutputProcessParameters,
+	int32_t IsEnabled
+) {
+	FAPOBase_BeginProcess(&fapo->base);
+
+	/* TODO */
+
+	FAPOBase_EndProcess(&fapo->base);
+}
+
+void FAPOFXEQ_Free(void* fapo)
+{
+	FAPOFXEQ *eq = (FAPOFXEQ*) fapo;
+	eq->base.pFree(eq->base.m_pParameterBlocks);
+	eq->base.pFree(fapo);
+}
+
+/* Public API */
+
+uint32_t FAPOFXCreateEQ(
+	FAPO **pEffect,
+	const void *pInitData,
+	uint32_t InitDataByteSize,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc,
+	uint8_t legacy
+) {
+	const FAPOFXEQParameters fxdefault =
+	{
+		FAPOFXEQ_DEFAULT_FREQUENCY_CENTER_0,
+		FAPOFXEQ_DEFAULT_GAIN,
+		FAPOFXEQ_DEFAULT_BANDWIDTH,
+		FAPOFXEQ_DEFAULT_FREQUENCY_CENTER_1,
+		FAPOFXEQ_DEFAULT_GAIN,
+		FAPOFXEQ_DEFAULT_BANDWIDTH,
+		FAPOFXEQ_DEFAULT_FREQUENCY_CENTER_2,
+		FAPOFXEQ_DEFAULT_GAIN,
+		FAPOFXEQ_DEFAULT_BANDWIDTH,
+		FAPOFXEQ_DEFAULT_FREQUENCY_CENTER_3,
+		FAPOFXEQ_DEFAULT_GAIN,
+		FAPOFXEQ_DEFAULT_BANDWIDTH
+	};
+
+	/* Allocate... */
+	FAPOFXEQ *result = (FAPOFXEQ*) customMalloc(
+		sizeof(FAPOFXEQ)
+	);
+	uint8_t *params = (uint8_t*) customMalloc(
+		sizeof(FAPOFXEQParameters) * 3
+	);
+	if (pInitData == NULL)
+	{
+		FAudio_zero(params, sizeof(FAPOFXEQParameters) * 3);
+		#define INITPARAMS(offset) \
+			FAudio_memcpy( \
+				params + sizeof(FAPOFXEQParameters) * offset, \
+				&fxdefault, \
+				sizeof(FAPOFXEQParameters) \
+			);
+		INITPARAMS(0)
+		INITPARAMS(1)
+		INITPARAMS(2)
+		#undef INITPARAMS
+	}
+	else
+	{
+		FAudio_assert(InitDataByteSize == sizeof(FAPOFXEQParameters));
+		FAudio_memcpy(params, pInitData, InitDataByteSize);
+		FAudio_memcpy(params + InitDataByteSize, pInitData, InitDataByteSize);
+		FAudio_memcpy(params + (InitDataByteSize * 2), pInitData, InitDataByteSize);
+	}
+
+	/* Initialize... */
+	FAudio_memcpy(
+		&FXEQProperties_LEGACY.clsid,
+		&FAPOFX_CLSID_FXEQ_LEGACY,
+		sizeof(FAudioGUID)
+	);
+	FAudio_memcpy(
+		&FXEQProperties.clsid,
+		&FAPOFX_CLSID_FXEQ,
+		sizeof(FAudioGUID)
+	);
+	CreateFAPOBaseWithCustomAllocatorEXT(
+		&result->base,
+		legacy ? &FXEQProperties_LEGACY : &FXEQProperties,
+		params,
+		sizeof(FAPOFXEQParameters),
+		0,
+		customMalloc,
+		customFree,
+		customRealloc
+	);
+
+	/* Function table... */
+	result->base.base.Initialize = (InitializeFunc)
+		FAPOFXEQ_Initialize;
+	result->base.base.Process = (ProcessFunc)
+		FAPOFXEQ_Process;
+	result->base.Destructor = FAPOFXEQ_Free;
+
+	/* Finally. */
+	*pEffect = &result->base.base;
+	return 0;
+}
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FAPOFX_masteringlimiter.c b/libs/faudio/src/FAPOFX_masteringlimiter.c
new file mode 100644
index 00000000000..1b02ffe90ae
--- /dev/null
+++ b/libs/faudio/src/FAPOFX_masteringlimiter.c
@@ -0,0 +1,247 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "FAPOFX.h"
+#include "FAudio_internal.h"
+
+/* FXMasteringLimiter FAPO Implementation */
+
+const FAudioGUID FAPOFX_CLSID_FXMasteringLimiter =
+{
+	0xC4137916,
+	0x2BE1,
+	0x46FD,
+	{
+		0x85,
+		0x99,
+		0x44,
+		0x15,
+		0x36,
+		0xF4,
+		0x98,
+		0x56
+	}
+};
+
+static FAPORegistrationProperties FXMasteringLimiterProperties =
+{
+	/* .clsid = */ {0},
+	/* .FriendlyName = */
+	{
+		'F', 'X', 'M', 'a', 's', 't', 'e', 'r', 'i', 'n', 'g', 'L', 'i', 'm', 'i', 't', 'e', 'r', '\0'
+	},
+	/*.CopyrightInfo = */
+	{
+		'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')',
+		'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0'
+	},
+	/*.MajorVersion = */ 0,
+	/*.MinorVersion = */ 0,
+	/*.Flags = */(
+		FAPO_FLAG_FRAMERATE_MUST_MATCH |
+		FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH |
+		FAPO_FLAG_BUFFERCOUNT_MUST_MATCH |
+		FAPO_FLAG_INPLACE_SUPPORTED |
+		FAPO_FLAG_INPLACE_REQUIRED
+	),
+	/*.MinInputBufferCount = */ 1,
+	/*.MaxInputBufferCount = */  1,
+	/*.MinOutputBufferCount = */ 1,
+	/*.MaxOutputBufferCount =*/ 1
+};
+
+const FAudioGUID FAPOFX_CLSID_FXMasteringLimiter_LEGACY =
+{
+	0xA90BC001,
+	0xE897,
+	0xE897,
+	{
+		0x74,
+		0x39,
+		0x43,
+		0x55,
+		0x00,
+		0x00,
+		0x00,
+		0x01
+	}
+};
+
+static FAPORegistrationProperties FXMasteringLimiterProperties_LEGACY =
+{
+	/* .clsid = */ {0},
+	/* .FriendlyName = */
+	{
+		'F', 'X', 'M', 'a', 's', 't', 'e', 'r', 'i', 'n', 'g', 'L', 'i', 'm', 'i', 't', 'e', 'r', '\0'
+	},
+	/*.CopyrightInfo = */
+	{
+		'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')',
+		'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0'
+	},
+	/*.MajorVersion = */ 0,
+	/*.MinorVersion = */ 0,
+	/*.Flags = */(
+		FAPO_FLAG_FRAMERATE_MUST_MATCH |
+		FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH |
+		FAPO_FLAG_BUFFERCOUNT_MUST_MATCH |
+		FAPO_FLAG_INPLACE_SUPPORTED |
+		FAPO_FLAG_INPLACE_REQUIRED
+	),
+	/*.MinInputBufferCount = */ 1,
+	/*.MaxInputBufferCount = */  1,
+	/*.MinOutputBufferCount = */ 1,
+	/*.MaxOutputBufferCount =*/ 1
+};
+
+typedef struct FAPOFXMasteringLimiter
+{
+	FAPOBase base;
+
+	/* TODO */
+} FAPOFXMasteringLimiter;
+
+uint32_t FAPOFXMasteringLimiter_Initialize(
+	FAPOFXMasteringLimiter *fapo,
+	const void* pData,
+	uint32_t DataByteSize
+) {
+	#define INITPARAMS(offset) \
+		FAudio_memcpy( \
+			fapo->base.m_pParameterBlocks + DataByteSize * offset, \
+			pData, \
+			DataByteSize \
+		);
+	INITPARAMS(0)
+	INITPARAMS(1)
+	INITPARAMS(2)
+	#undef INITPARAMS
+	return 0;
+}
+
+void FAPOFXMasteringLimiter_Process(
+	FAPOFXMasteringLimiter *fapo,
+	uint32_t InputProcessParameterCount,
+	const FAPOProcessBufferParameters* pInputProcessParameters,
+	uint32_t OutputProcessParameterCount,
+	FAPOProcessBufferParameters* pOutputProcessParameters,
+	int32_t IsEnabled
+) {
+	FAPOBase_BeginProcess(&fapo->base);
+
+	/* TODO */
+
+	FAPOBase_EndProcess(&fapo->base);
+}
+
+void FAPOFXMasteringLimiter_Free(void* fapo)
+{
+	FAPOFXMasteringLimiter *limiter = (FAPOFXMasteringLimiter*) fapo;
+	limiter->base.pFree(limiter->base.m_pParameterBlocks);
+	limiter->base.pFree(fapo);
+}
+
+/* Public API */
+
+uint32_t FAPOFXCreateMasteringLimiter(
+	FAPO **pEffect,
+	const void *pInitData,
+	uint32_t InitDataByteSize,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc,
+	uint8_t legacy
+) {
+	const FAPOFXMasteringLimiterParameters fxdefault =
+	{
+		FAPOFXMASTERINGLIMITER_DEFAULT_RELEASE,
+		FAPOFXMASTERINGLIMITER_DEFAULT_LOUDNESS
+	};
+
+	/* Allocate... */
+	FAPOFXMasteringLimiter *result = (FAPOFXMasteringLimiter*) customMalloc(
+		sizeof(FAPOFXMasteringLimiter)
+	);
+	uint8_t *params = (uint8_t*) customMalloc(
+		sizeof(FAPOFXMasteringLimiterParameters) * 3
+	);
+	if (pInitData == NULL)
+	{
+		FAudio_zero(params, sizeof(FAPOFXMasteringLimiterParameters) * 3);
+		#define INITPARAMS(offset) \
+			FAudio_memcpy( \
+				params + sizeof(FAPOFXMasteringLimiterParameters) * offset, \
+				&fxdefault, \
+				sizeof(FAPOFXMasteringLimiterParameters) \
+			);
+		INITPARAMS(0)
+		INITPARAMS(1)
+		INITPARAMS(2)
+		#undef INITPARAMS
+	}
+	else
+	{
+		FAudio_assert(InitDataByteSize == sizeof(FAPOFXMasteringLimiterParameters));
+		FAudio_memcpy(params, pInitData, InitDataByteSize);
+		FAudio_memcpy(params + InitDataByteSize, pInitData, InitDataByteSize);
+		FAudio_memcpy(params + (InitDataByteSize * 2), pInitData, InitDataByteSize);
+	}
+
+	/* Initialize... */
+	FAudio_memcpy(
+		&FXMasteringLimiterProperties_LEGACY.clsid,
+		&FAPOFX_CLSID_FXMasteringLimiter_LEGACY,
+		sizeof(FAudioGUID)
+	);
+	FAudio_memcpy(
+		&FXMasteringLimiterProperties.clsid,
+		&FAPOFX_CLSID_FXMasteringLimiter,
+		sizeof(FAudioGUID)
+	);
+	CreateFAPOBaseWithCustomAllocatorEXT(
+		&result->base,
+		legacy ? &FXMasteringLimiterProperties_LEGACY : &FXMasteringLimiterProperties,
+		params,
+		sizeof(FAPOFXMasteringLimiterParameters),
+		0,
+		customMalloc,
+		customFree,
+		customRealloc
+	);
+
+	/* Function table... */
+	result->base.base.Initialize = (InitializeFunc)
+		FAPOFXMasteringLimiter_Initialize;
+	result->base.base.Process = (ProcessFunc)
+		FAPOFXMasteringLimiter_Process;
+	result->base.Destructor = FAPOFXMasteringLimiter_Free;
+
+	/* Finally. */
+	*pEffect = &result->base.base;
+	return 0;
+}
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FAPOFX_reverb.c b/libs/faudio/src/FAPOFX_reverb.c
new file mode 100644
index 00000000000..782dddafa25
--- /dev/null
+++ b/libs/faudio/src/FAPOFX_reverb.c
@@ -0,0 +1,247 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "FAPOFX.h"
+#include "FAudio_internal.h"
+
+/* FXReverb FAPO Implementation */
+
+const FAudioGUID FAPOFX_CLSID_FXReverb =
+{
+	0x7D9ACA56,
+	0xCB68,
+	0x4807,
+	{
+		0xB6,
+		0x32,
+		0xB1,
+		0x37,
+		0x35,
+		0x2E,
+		0x85,
+		0x96
+	}
+};
+
+static FAPORegistrationProperties FXReverbProperties =
+{
+	/* .clsid = */ {0},
+	/* .FriendlyName = */
+	{
+		'F', 'X', 'R', 'e', 'v', 'e', 'r', 'b', '\0'
+	},
+	/*.CopyrightInfo = */
+	{
+		'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')',
+		'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0'
+	},
+	/*.MajorVersion = */ 0,
+	/*.MinorVersion = */ 0,
+	/*.Flags = */(
+		FAPO_FLAG_FRAMERATE_MUST_MATCH |
+		FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH |
+		FAPO_FLAG_BUFFERCOUNT_MUST_MATCH |
+		FAPO_FLAG_INPLACE_SUPPORTED |
+		FAPO_FLAG_INPLACE_REQUIRED
+	),
+	/*.MinInputBufferCount = */ 1,
+	/*.MaxInputBufferCount = */  1,
+	/*.MinOutputBufferCount = */ 1,
+	/*.MaxOutputBufferCount =*/ 1
+};
+
+const FAudioGUID FAPOFX_CLSID_FXReverb_LEGACY =
+{
+	0xA90BC001,
+	0xE897,
+	0xE897,
+	{
+		0x74,
+		0x39,
+		0x43,
+		0x55,
+		0x00,
+		0x00,
+		0x00,
+		0x02
+	}
+};
+
+static FAPORegistrationProperties FXReverbProperties_LEGACY =
+{
+	/* .clsid = */ {0},
+	/* .FriendlyName = */
+	{
+		'F', 'X', 'R', 'e', 'v', 'e', 'r', 'b', '\0'
+	},
+	/*.CopyrightInfo = */
+	{
+		'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')',
+		'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0'
+	},
+	/*.MajorVersion = */ 0,
+	/*.MinorVersion = */ 0,
+	/*.Flags = */(
+		FAPO_FLAG_FRAMERATE_MUST_MATCH |
+		FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH |
+		FAPO_FLAG_BUFFERCOUNT_MUST_MATCH |
+		FAPO_FLAG_INPLACE_SUPPORTED |
+		FAPO_FLAG_INPLACE_REQUIRED
+	),
+	/*.MinInputBufferCount = */ 1,
+	/*.MaxInputBufferCount = */  1,
+	/*.MinOutputBufferCount = */ 1,
+	/*.MaxOutputBufferCount =*/ 1
+};
+
+typedef struct FAPOFXReverb
+{
+	FAPOBase base;
+
+	/* TODO */
+} FAPOFXReverb;
+
+uint32_t FAPOFXReverb_Initialize(
+	FAPOFXReverb *fapo,
+	const void* pData,
+	uint32_t DataByteSize
+) {
+	#define INITPARAMS(offset) \
+		FAudio_memcpy( \
+			fapo->base.m_pParameterBlocks + DataByteSize * offset, \
+			pData, \
+			DataByteSize \
+		);
+	INITPARAMS(0)
+	INITPARAMS(1)
+	INITPARAMS(2)
+	#undef INITPARAMS
+	return 0;
+}
+
+void FAPOFXReverb_Process(
+	FAPOFXReverb *fapo,
+	uint32_t InputProcessParameterCount,
+	const FAPOProcessBufferParameters* pInputProcessParameters,
+	uint32_t OutputProcessParameterCount,
+	FAPOProcessBufferParameters* pOutputProcessParameters,
+	int32_t IsEnabled
+) {
+	FAPOBase_BeginProcess(&fapo->base);
+
+	/* TODO */
+
+	FAPOBase_EndProcess(&fapo->base);
+}
+
+void FAPOFXReverb_Free(void* fapo)
+{
+	FAPOFXReverb *reverb = (FAPOFXReverb*) fapo;
+	reverb->base.pFree(reverb->base.m_pParameterBlocks);
+	reverb->base.pFree(fapo);
+}
+
+/* Public API */
+
+uint32_t FAPOFXCreateReverb(
+	FAPO **pEffect,
+	const void *pInitData,
+	uint32_t InitDataByteSize,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc,
+	uint8_t legacy
+) {
+	const FAPOFXReverbParameters fxdefault =
+	{
+		FAPOFXREVERB_DEFAULT_DIFFUSION,
+		FAPOFXREVERB_DEFAULT_ROOMSIZE,
+	};
+
+	/* Allocate... */
+	FAPOFXReverb *result = (FAPOFXReverb*) customMalloc(
+		sizeof(FAPOFXReverb)
+	);
+	uint8_t *params = (uint8_t*) customMalloc(
+		sizeof(FAPOFXReverbParameters) * 3
+	);
+	if (pInitData == NULL)
+	{
+		FAudio_zero(params, sizeof(FAPOFXReverbParameters) * 3);
+		#define INITPARAMS(offset) \
+			FAudio_memcpy( \
+				params + sizeof(FAPOFXReverbParameters) * offset, \
+				&fxdefault, \
+				sizeof(FAPOFXReverbParameters) \
+			);
+		INITPARAMS(0)
+		INITPARAMS(1)
+		INITPARAMS(2)
+		#undef INITPARAMS
+	}
+	else
+	{
+		FAudio_assert(InitDataByteSize == sizeof(FAPOFXReverbParameters));
+		FAudio_memcpy(params, pInitData, InitDataByteSize);
+		FAudio_memcpy(params + InitDataByteSize, pInitData, InitDataByteSize);
+		FAudio_memcpy(params + (InitDataByteSize * 2), pInitData, InitDataByteSize);
+	}
+
+	/* Initialize... */
+	FAudio_memcpy(
+		&FXReverbProperties_LEGACY.clsid,
+		&FAPOFX_CLSID_FXReverb_LEGACY,
+		sizeof(FAudioGUID)
+	);
+	FAudio_memcpy(
+		&FXReverbProperties.clsid,
+		&FAPOFX_CLSID_FXReverb,
+		sizeof(FAudioGUID)
+	);
+	CreateFAPOBaseWithCustomAllocatorEXT(
+		&result->base,
+		legacy ? &FXReverbProperties_LEGACY : &FXReverbProperties,
+		params,
+		sizeof(FAPOFXReverbParameters),
+		0,
+		customMalloc,
+		customFree,
+		customRealloc
+	);
+
+	/* Function table... */
+	result->base.base.Initialize = (InitializeFunc)
+		FAPOFXReverb_Initialize;
+	result->base.base.Process = (ProcessFunc)
+		FAPOFXReverb_Process;
+	result->base.Destructor = FAPOFXReverb_Free;
+
+	/* Finally. */
+	*pEffect = &result->base.base;
+	return 0;
+}
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FAudio.c b/libs/faudio/src/FAudio.c
new file mode 100644
index 00000000000..81363b983c7
--- /dev/null
+++ b/libs/faudio/src/FAudio.c
@@ -0,0 +1,3201 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "FAudio_internal.h"
+
+#define MAKE_SUBFORMAT_GUID(guid, fmt) \
+	FAudioGUID DATAFORMAT_SUBTYPE_##guid = \
+	{ \
+		(uint16_t) (fmt), \
+		0x0000, \
+		0x0010, \
+		{ \
+			0x80, \
+			0x00, \
+			0x00, \
+			0xAA, \
+			0x00, \
+			0x38, \
+			0x9B, \
+			0x71 \
+		} \
+	}
+MAKE_SUBFORMAT_GUID(PCM, 1);
+MAKE_SUBFORMAT_GUID(ADPCM, 2);
+MAKE_SUBFORMAT_GUID(IEEE_FLOAT, 3);
+MAKE_SUBFORMAT_GUID(XMAUDIO2, FAUDIO_FORMAT_XMAUDIO2);
+MAKE_SUBFORMAT_GUID(WMAUDIO2, FAUDIO_FORMAT_WMAUDIO2);
+MAKE_SUBFORMAT_GUID(WMAUDIO3, FAUDIO_FORMAT_WMAUDIO3);
+MAKE_SUBFORMAT_GUID(WMAUDIO_LOSSLESS, FAUDIO_FORMAT_WMAUDIO_LOSSLESS);
+#undef MAKE_SUBFORMAT_GUID
+
+#ifdef FAUDIO_DUMP_VOICES
+static void FAudio_DUMPVOICE_Init(const FAudioSourceVoice *voice);
+static void FAudio_DUMPVOICE_Finalize(const FAudioSourceVoice *voice);
+static void FAudio_DUMPVOICE_WriteBuffer(
+	const FAudioSourceVoice *voice,
+	const FAudioBuffer *pBuffer,
+	const FAudioBufferWMA *pBufferWMA,
+	const uint32_t playBegin,
+	const uint32_t playLength
+);
+#endif /* FAUDIO_DUMP_VOICES */
+
+/* FAudio Version */
+
+uint32_t FAudioLinkedVersion(void)
+{
+	return FAUDIO_COMPILED_VERSION;
+}
+
+/* FAudio Interface */
+
+uint32_t FAudioCreate(
+	FAudio **ppFAudio,
+	uint32_t Flags,
+	FAudioProcessor XAudio2Processor
+) {
+	FAudioCOMConstructEXT(ppFAudio, FAUDIO_TARGET_VERSION);
+	FAudio_Initialize(*ppFAudio, Flags, XAudio2Processor);
+	return 0;
+}
+
+uint32_t FAudioCOMConstructEXT(FAudio **ppFAudio, uint8_t version)
+{
+	return FAudioCOMConstructWithCustomAllocatorEXT(
+		ppFAudio,
+		version,
+		FAudio_malloc,
+		FAudio_free,
+		FAudio_realloc
+	);
+}
+
+uint32_t FAudioCreateWithCustomAllocatorEXT(
+	FAudio **ppFAudio,
+	uint32_t Flags,
+	FAudioProcessor XAudio2Processor,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc
+) {
+	FAudioCOMConstructWithCustomAllocatorEXT(
+		ppFAudio,
+		FAUDIO_TARGET_VERSION,
+		customMalloc,
+		customFree,
+		customRealloc
+	);
+	FAudio_Initialize(*ppFAudio, Flags, XAudio2Processor);
+	return 0;
+}
+
+uint32_t FAudioCOMConstructWithCustomAllocatorEXT(
+	FAudio **ppFAudio,
+	uint8_t version,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc
+) {
+#ifndef FAUDIO_DISABLE_DEBUGCONFIGURATION
+	FAudioDebugConfiguration debugInit = {0};
+#endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */
+	FAudio_PlatformAddRef();
+	*ppFAudio = (FAudio*) customMalloc(sizeof(FAudio));
+	FAudio_zero(*ppFAudio, sizeof(FAudio));
+	(*ppFAudio)->version = version;
+#ifndef FAUDIO_DISABLE_DEBUGCONFIGURATION
+	FAudio_SetDebugConfiguration(*ppFAudio, &debugInit, NULL);
+#endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */
+	(*ppFAudio)->sourceLock = FAudio_PlatformCreateMutex();
+	LOG_MUTEX_CREATE((*ppFAudio), (*ppFAudio)->sourceLock)
+	(*ppFAudio)->submixLock = FAudio_PlatformCreateMutex();
+	LOG_MUTEX_CREATE((*ppFAudio), (*ppFAudio)->submixLock)
+	(*ppFAudio)->callbackLock = FAudio_PlatformCreateMutex();
+	LOG_MUTEX_CREATE((*ppFAudio), (*ppFAudio)->callbackLock)
+	(*ppFAudio)->operationLock = FAudio_PlatformCreateMutex();
+	LOG_MUTEX_CREATE((*ppFAudio), (*ppFAudio)->operationLock)
+	(*ppFAudio)->pMalloc = customMalloc;
+	(*ppFAudio)->pFree = customFree;
+	(*ppFAudio)->pRealloc = customRealloc;
+	(*ppFAudio)->refcount = 1;
+	return 0;
+}
+
+uint32_t FAudio_AddRef(FAudio *audio)
+{
+	LOG_API_ENTER(audio)
+	audio->refcount += 1;
+	LOG_API_EXIT(audio)
+	return audio->refcount;
+}
+
+uint32_t FAudio_Release(FAudio *audio)
+{
+	uint32_t refcount;
+	LOG_API_ENTER(audio)
+	audio->refcount -= 1;
+	refcount = audio->refcount;
+	if (audio->refcount == 0)
+	{
+		FAudio_OPERATIONSET_ClearAll(audio);
+		FAudio_StopEngine(audio);
+		audio->pFree(audio->decodeCache);
+		audio->pFree(audio->resampleCache);
+		audio->pFree(audio->effectChainCache);
+		LOG_MUTEX_DESTROY(audio, audio->sourceLock)
+		FAudio_PlatformDestroyMutex(audio->sourceLock);
+		LOG_MUTEX_DESTROY(audio, audio->submixLock)
+		FAudio_PlatformDestroyMutex(audio->submixLock);
+		LOG_MUTEX_DESTROY(audio, audio->callbackLock)
+		FAudio_PlatformDestroyMutex(audio->callbackLock);
+		LOG_MUTEX_DESTROY(audio, audio->operationLock)
+		FAudio_PlatformDestroyMutex(audio->operationLock);
+		audio->pFree(audio);
+		FAudio_PlatformRelease();
+	}
+	else
+	{
+		LOG_API_EXIT(audio)
+	}
+	return refcount;
+}
+
+uint32_t FAudio_GetDeviceCount(FAudio *audio, uint32_t *pCount)
+{
+	LOG_API_ENTER(audio)
+	*pCount = FAudio_PlatformGetDeviceCount();
+	LOG_API_EXIT(audio)
+	return 0;
+}
+
+uint32_t FAudio_GetDeviceDetails(
+	FAudio *audio,
+	uint32_t Index,
+	FAudioDeviceDetails *pDeviceDetails
+) {
+	uint32_t result;
+	LOG_API_ENTER(audio)
+	result = FAudio_PlatformGetDeviceDetails(Index, pDeviceDetails);
+	LOG_API_EXIT(audio)
+	return result;
+}
+
+uint32_t FAudio_Initialize(
+	FAudio *audio,
+	uint32_t Flags,
+	FAudioProcessor XAudio2Processor
+) {
+	LOG_API_ENTER(audio)
+	FAudio_assert(Flags == 0 || Flags == FAUDIO_DEBUG_ENGINE);
+	FAudio_assert(XAudio2Processor == FAUDIO_DEFAULT_PROCESSOR);
+
+	audio->initFlags = Flags;
+
+	/* FIXME: This is lazy... */
+	audio->decodeCache = (float*) audio->pMalloc(sizeof(float));
+	audio->resampleCache = (float*) audio->pMalloc(sizeof(float));
+	audio->decodeSamples = 1;
+	audio->resampleSamples = 1;
+
+	FAudio_StartEngine(audio);
+	LOG_API_EXIT(audio)
+	return 0;
+}
+
+uint32_t FAudio_RegisterForCallbacks(
+	FAudio *audio,
+	FAudioEngineCallback *pCallback
+) {
+	LOG_API_ENTER(audio)
+	LinkedList_AddEntry(
+		&audio->callbacks,
+		pCallback,
+		audio->callbackLock,
+		audio->pMalloc
+	);
+	LOG_API_EXIT(audio)
+	return 0;
+}
+
+void FAudio_UnregisterForCallbacks(
+	FAudio *audio,
+	FAudioEngineCallback *pCallback
+) {
+	LOG_API_ENTER(audio)
+	LinkedList_RemoveEntry(
+		&audio->callbacks,
+		pCallback,
+		audio->callbackLock,
+		audio->pFree
+	);
+	LOG_API_EXIT(audio)
+}
+
+uint32_t FAudio_CreateSourceVoice(
+	FAudio *audio,
+	FAudioSourceVoice **ppSourceVoice,
+	const FAudioWaveFormatEx *pSourceFormat,
+	uint32_t Flags,
+	float MaxFrequencyRatio,
+	FAudioVoiceCallback *pCallback,
+	const FAudioVoiceSends *pSendList,
+	const FAudioEffectChain *pEffectChain
+) {
+	uint32_t i;
+
+	LOG_API_ENTER(audio)
+	LOG_FORMAT(audio, pSourceFormat)
+
+	*ppSourceVoice = (FAudioSourceVoice*) audio->pMalloc(sizeof(FAudioVoice));
+	FAudio_zero(*ppSourceVoice, sizeof(FAudioSourceVoice));
+	(*ppSourceVoice)->audio = audio;
+	(*ppSourceVoice)->type = FAUDIO_VOICE_SOURCE;
+	(*ppSourceVoice)->flags = Flags;
+	(*ppSourceVoice)->filter.Type = FAUDIO_DEFAULT_FILTER_TYPE;
+	(*ppSourceVoice)->filter.Frequency = FAUDIO_DEFAULT_FILTER_FREQUENCY;
+	(*ppSourceVoice)->filter.OneOverQ = FAUDIO_DEFAULT_FILTER_ONEOVERQ;
+	(*ppSourceVoice)->sendLock = FAudio_PlatformCreateMutex();
+	LOG_MUTEX_CREATE(audio, (*ppSourceVoice)->sendLock)
+	(*ppSourceVoice)->effectLock = FAudio_PlatformCreateMutex();
+	LOG_MUTEX_CREATE(audio, (*ppSourceVoice)->effectLock)
+	(*ppSourceVoice)->filterLock = FAudio_PlatformCreateMutex();
+	LOG_MUTEX_CREATE(audio, (*ppSourceVoice)->filterLock)
+	(*ppSourceVoice)->volumeLock = FAudio_PlatformCreateMutex();
+	LOG_MUTEX_CREATE(audio, (*ppSourceVoice)->volumeLock)
+
+	/* Source Properties */
+	FAudio_assert(MaxFrequencyRatio <= FAUDIO_MAX_FREQ_RATIO);
+	(*ppSourceVoice)->src.maxFreqRatio = MaxFrequencyRatio;
+
+	if (	pSourceFormat->wFormatTag == FAUDIO_FORMAT_PCM ||
+		pSourceFormat->wFormatTag == FAUDIO_FORMAT_IEEE_FLOAT ||
+		pSourceFormat->wFormatTag == FAUDIO_FORMAT_WMAUDIO2	)
+	{
+		FAudioWaveFormatExtensible *fmtex = (FAudioWaveFormatExtensible*) audio->pMalloc(
+			sizeof(FAudioWaveFormatExtensible)
+		);
+		/* convert PCM to EXTENSIBLE */
+		fmtex->Format.wFormatTag = FAUDIO_FORMAT_EXTENSIBLE;
+		fmtex->Format.nChannels = pSourceFormat->nChannels;
+		fmtex->Format.nSamplesPerSec = pSourceFormat->nSamplesPerSec;
+		fmtex->Format.nAvgBytesPerSec = pSourceFormat->nAvgBytesPerSec;
+		fmtex->Format.nBlockAlign = pSourceFormat->nBlockAlign;
+		fmtex->Format.wBitsPerSample = pSourceFormat->wBitsPerSample;
+		fmtex->Format.cbSize = sizeof(FAudioWaveFormatExtensible) - sizeof(FAudioWaveFormatEx);
+		fmtex->Samples.wValidBitsPerSample = pSourceFormat->wBitsPerSample;
+		fmtex->dwChannelMask = 0;
+		if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_PCM)
+		{
+			FAudio_memcpy(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_PCM, sizeof(FAudioGUID));
+		}
+		else if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_IEEE_FLOAT)
+		{
+			FAudio_memcpy(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(FAudioGUID));
+		}
+		else if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_WMAUDIO2)
+		{
+			FAudio_memcpy(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_WMAUDIO2, sizeof(FAudioGUID));
+		}
+		(*ppSourceVoice)->src.format = &fmtex->Format;
+	}
+	else if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_MSADPCM)
+	{
+		FAudioADPCMWaveFormat *fmtex = (FAudioADPCMWaveFormat*) audio->pMalloc(
+			sizeof(FAudioADPCMWaveFormat)
+		);
+
+		/* Copy what we can, ideally the sizes match! */
+		size_t cbSize = sizeof(FAudioWaveFormatEx) + pSourceFormat->cbSize;
+		FAudio_memcpy(
+			fmtex,
+			pSourceFormat,
+			FAudio_min(cbSize, sizeof(FAudioADPCMWaveFormat))
+		);
+		if (cbSize < sizeof(FAudioADPCMWaveFormat))
+		{
+			FAudio_zero(
+				((uint8_t*) fmtex) + cbSize,
+				sizeof(FAudioADPCMWaveFormat) - cbSize
+			);
+		}
+
+		/* XAudio2 does not validate this input! */
+		fmtex->wfx.cbSize = sizeof(FAudioADPCMWaveFormat) - sizeof(FAudioWaveFormatEx);
+		fmtex->wSamplesPerBlock = ((
+			fmtex->wfx.nBlockAlign / fmtex->wfx.nChannels
+		) - 6) * 2;
+		(*ppSourceVoice)->src.format = &fmtex->wfx;
+	}
+	else if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
+	{
+		FAudioXMA2WaveFormat *fmtex = (FAudioXMA2WaveFormat*) audio->pMalloc(
+			sizeof(FAudioXMA2WaveFormat)
+		);
+
+		/* Copy what we can, ideally the sizes match! */
+		size_t cbSize = sizeof(FAudioWaveFormatEx) + pSourceFormat->cbSize;
+		FAudio_memcpy(
+			fmtex,
+			pSourceFormat,
+			FAudio_min(cbSize, sizeof(FAudioXMA2WaveFormat))
+		);
+		if (cbSize < sizeof(FAudioXMA2WaveFormat))
+		{
+			FAudio_zero(
+				((uint8_t*) fmtex) + cbSize,
+				sizeof(FAudioADPCMWaveFormat) - cbSize
+			);
+		}
+
+		/* Does XAudio2 validate this input?! */
+		fmtex->wfx.cbSize = sizeof(FAudioXMA2WaveFormat) - sizeof(FAudioWaveFormatEx);
+		(*ppSourceVoice)->src.format = &fmtex->wfx;
+	}
+	else
+	{
+		/* direct copy anything else */
+		(*ppSourceVoice)->src.format = (FAudioWaveFormatEx*) audio->pMalloc(
+			sizeof(FAudioWaveFormatEx) + pSourceFormat->cbSize
+		);
+		FAudio_memcpy(
+			(*ppSourceVoice)->src.format,
+			pSourceFormat,
+			sizeof(FAudioWaveFormatEx) + pSourceFormat->cbSize
+		);
+	}
+
+	(*ppSourceVoice)->src.callback = pCallback;
+	(*ppSourceVoice)->src.active = 0;
+	(*ppSourceVoice)->src.freqRatio = 1.0f;
+	(*ppSourceVoice)->src.totalSamples = 0;
+	(*ppSourceVoice)->src.bufferList = NULL;
+	(*ppSourceVoice)->src.flushList = NULL;
+	(*ppSourceVoice)->src.bufferLock = FAudio_PlatformCreateMutex();
+	LOG_MUTEX_CREATE(audio, (*ppSourceVoice)->src.bufferLock)
+
+	if ((*ppSourceVoice)->src.format->wFormatTag == FAUDIO_FORMAT_EXTENSIBLE)
+	{
+		FAudioWaveFormatExtensible *fmtex = (FAudioWaveFormatExtensible*) (*ppSourceVoice)->src.format;
+
+		#define COMPARE_GUID(type) \
+			(FAudio_memcmp( \
+				&fmtex->SubFormat, \
+				&DATAFORMAT_SUBTYPE_##type, \
+				sizeof(FAudioGUID) \
+			) == 0)
+		if (COMPARE_GUID(PCM))
+		{
+			#define DECODER(bit) \
+				if (fmtex->Format.wBitsPerSample == bit) \
+				{ \
+					(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodePCM##bit; \
+				}
+			DECODER(16)
+			else DECODER(8)
+			else DECODER(24)
+			else DECODER(32)
+			else
+			{
+				LOG_ERROR(
+					audio,
+					"Unrecognized wBitsPerSample: %d",
+					fmtex->Format.wBitsPerSample
+				)
+				FAudio_assert(0 && "Unrecognized wBitsPerSample!");
+			}
+			#undef DECODER
+		}
+		else if (COMPARE_GUID(IEEE_FLOAT))
+		{
+			/* FIXME: Weird behavior!
+			 * Prototype creates a source with the IEEE_FLOAT tag,
+			 * but it's actually PCM16. It seems to prioritize
+			 * wBitsPerSample over the format tag. Not sure if we
+			 * should fold this section into the section above...?
+			 * -flibit
+			 */
+			if (fmtex->Format.wBitsPerSample == 16)
+			{
+				(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodePCM16;
+			}
+			else
+			{
+				(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodePCM32F;
+			}
+		}
+		else if (	COMPARE_GUID(WMAUDIO2) ||
+				COMPARE_GUID(WMAUDIO3) ||
+				COMPARE_GUID(WMAUDIO_LOSSLESS)	)
+		{
+#ifdef HAVE_WMADEC
+			if (FAudio_WMADEC_init(*ppSourceVoice, fmtex->SubFormat.Data1) != 0)
+			{
+				(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
+			}
+#else
+			FAudio_assert(0 && "xWMA is not supported!");
+			(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
+#endif /* HAVE_WMADEC */
+		}
+		else
+		{
+			FAudio_assert(0 && "Unsupported WAVEFORMATEXTENSIBLE subtype!");
+		}
+		#undef COMPARE_GUID
+	}
+	else if ((*ppSourceVoice)->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
+	{
+#ifdef HAVE_WMADEC
+		if (FAudio_WMADEC_init(*ppSourceVoice, FAUDIO_FORMAT_XMAUDIO2) != 0)
+		{
+			(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
+		}
+#else
+		FAudio_assert(0 && "XMA2 is not supported!");
+		(*ppSourceVoice)->src.decode = FAudio_INTERNAL_DecodeWMAERROR;
+#endif /* HAVE_WMADEC */
+	}
+	else if ((*ppSourceVoice)->src.format->wFormatTag == FAUDIO_FORMAT_MSADPCM)
+	{
+		(*ppSourceVoice)->src.decode = ((*ppSourceVoice)->src.format->nChannels == 2) ?
+			FAudio_INTERNAL_DecodeStereoMSADPCM :
+			FAudio_INTERNAL_DecodeMonoMSADPCM;
+	}
+	else
+	{
+		FAudio_assert(0 && "Unsupported format tag!");
+	}
+
+	if ((*ppSourceVoice)->src.format->nChannels == 1)
+	{
+		(*ppSourceVoice)->src.resample = FAudio_INTERNAL_ResampleMono;
+	}
+	else if ((*ppSourceVoice)->src.format->nChannels == 2)
+	{
+		(*ppSourceVoice)->src.resample = FAudio_INTERNAL_ResampleStereo;
+	}
+	else
+	{
+		(*ppSourceVoice)->src.resample = FAudio_INTERNAL_ResampleGeneric;
+	}
+
+	(*ppSourceVoice)->src.curBufferOffset = 0;
+
+	/* Sends/Effects */
+	FAudio_INTERNAL_VoiceOutputFrequency(*ppSourceVoice, pSendList);
+	FAudioVoice_SetEffectChain(*ppSourceVoice, pEffectChain);
+
+	/* Default Levels */
+	(*ppSourceVoice)->volume = 1.0f;
+	(*ppSourceVoice)->channelVolume = (float*) audio->pMalloc(
+		sizeof(float) * (*ppSourceVoice)->outputChannels
+	);
+	for (i = 0; i < (*ppSourceVoice)->outputChannels; i += 1)
+	{
+		(*ppSourceVoice)->channelVolume[i] = 1.0f;
+	}
+
+	FAudioVoice_SetOutputVoices(*ppSourceVoice, pSendList);
+
+	/* Filters */
+	if (Flags & FAUDIO_VOICE_USEFILTER)
+	{
+		(*ppSourceVoice)->filterState = (FAudioFilterState*) audio->pMalloc(
+			sizeof(FAudioFilterState) * (*ppSourceVoice)->src.format->nChannels
+		);
+		FAudio_zero(
+			(*ppSourceVoice)->filterState,
+			sizeof(FAudioFilterState) * (*ppSourceVoice)->src.format->nChannels
+		);
+	}
+
+	/* Sample Storage */
+	(*ppSourceVoice)->src.decodeSamples = (uint32_t) (FAudio_ceil(
+		(double) audio->updateSize *
+		(double) MaxFrequencyRatio *
+		(double) (*ppSourceVoice)->src.format->nSamplesPerSec /
+		(double) audio->master->master.inputSampleRate
+	)) + EXTRA_DECODE_PADDING * (*ppSourceVoice)->src.format->nChannels;
+	FAudio_INTERNAL_ResizeDecodeCache(
+		audio,
+		((*ppSourceVoice)->src.decodeSamples + EXTRA_DECODE_PADDING) * (*ppSourceVoice)->src.format->nChannels
+	);
+
+	LOG_INFO(audio, "-> %p", (void*) (*ppSourceVoice))
+
+	/* Add to list, finally. */
+	LinkedList_PrependEntry(
+		&audio->sources,
+		*ppSourceVoice,
+		audio->sourceLock,
+		audio->pMalloc
+	);
+	FAudio_AddRef(audio);
+
+#ifdef FAUDIO_DUMP_VOICES
+	FAudio_DUMPVOICE_Init(*ppSourceVoice);
+#endif /* FAUDIO_DUMP_VOICES */
+
+	LOG_API_EXIT(audio)
+	return 0;
+}
+
+uint32_t FAudio_CreateSubmixVoice(
+	FAudio *audio,
+	FAudioSubmixVoice **ppSubmixVoice,
+	uint32_t InputChannels,
+	uint32_t InputSampleRate,
+	uint32_t Flags,
+	uint32_t ProcessingStage,
+	const FAudioVoiceSends *pSendList,
+	const FAudioEffectChain *pEffectChain
+) {
+	uint32_t i;
+
+	LOG_API_ENTER(audio)
+
+	*ppSubmixVoice = (FAudioSubmixVoice*) audio->pMalloc(sizeof(FAudioVoice));
+	FAudio_zero(*ppSubmixVoice, sizeof(FAudioSubmixVoice));
+	(*ppSubmixVoice)->audio = audio;
+	(*ppSubmixVoice)->type = FAUDIO_VOICE_SUBMIX;
+	(*ppSubmixVoice)->flags = Flags;
+	(*ppSubmixVoice)->filter.Type = FAUDIO_DEFAULT_FILTER_TYPE;
+	(*ppSubmixVoice)->filter.Frequency = FAUDIO_DEFAULT_FILTER_FREQUENCY;
+	(*ppSubmixVoice)->filter.OneOverQ = FAUDIO_DEFAULT_FILTER_ONEOVERQ;
+	(*ppSubmixVoice)->sendLock = FAudio_PlatformCreateMutex();
+	LOG_MUTEX_CREATE(audio, (*ppSubmixVoice)->sendLock)
+	(*ppSubmixVoice)->effectLock = FAudio_PlatformCreateMutex();
+	LOG_MUTEX_CREATE(audio, (*ppSubmixVoice)->effectLock)
+	(*ppSubmixVoice)->filterLock = FAudio_PlatformCreateMutex();
+	LOG_MUTEX_CREATE(audio, (*ppSubmixVoice)->filterLock)
+	(*ppSubmixVoice)->volumeLock = FAudio_PlatformCreateMutex();
+	LOG_MUTEX_CREATE(audio, (*ppSubmixVoice)->volumeLock)
+
+	/* Submix Properties */
+	(*ppSubmixVoice)->mix.inputChannels = InputChannels;
+	(*ppSubmixVoice)->mix.inputSampleRate = InputSampleRate;
+	(*ppSubmixVoice)->mix.processingStage = ProcessingStage;
+
+	/* Resampler */
+	if (InputChannels == 1)
+	{
+		(*ppSubmixVoice)->mix.resample = FAudio_INTERNAL_ResampleMono;
+	}
+	else if (InputChannels == 2)
+	{
+		(*ppSubmixVoice)->mix.resample = FAudio_INTERNAL_ResampleStereo;
+	}
+	else
+	{
+		(*ppSubmixVoice)->mix.resample = FAudio_INTERNAL_ResampleGeneric;
+	}
+
+	/* Sample Storage */
+	(*ppSubmixVoice)->mix.inputSamples = ((uint32_t) FAudio_ceil(
+		audio->updateSize *
+		(double) InputSampleRate /
+		(double) audio->master->master.inputSampleRate
+	) + EXTRA_DECODE_PADDING) * InputChannels;
+	(*ppSubmixVoice)->mix.inputCache = (float*) audio->pMalloc(
+		sizeof(float) * (*ppSubmixVoice)->mix.inputSamples
+	);
+	FAudio_zero( /* Zero this now, for the first update */
+		(*ppSubmixVoice)->mix.inputCache,
+		sizeof(float) * (*ppSubmixVoice)->mix.inputSamples
+	);
+
+	/* Sends/Effects */
+	FAudio_INTERNAL_VoiceOutputFrequency(*ppSubmixVoice, pSendList);
+	FAudioVoice_SetEffectChain(*ppSubmixVoice, pEffectChain);
+
+	/* Default Levels */
+	(*ppSubmixVoice)->volume = 1.0f;
+	(*ppSubmixVoice)->channelVolume = (float*) audio->pMalloc(
+		sizeof(float) * (*ppSubmixVoice)->outputChannels
+	);
+	for (i = 0; i < (*ppSubmixVoice)->outputChannels; i += 1)
+	{
+		(*ppSubmixVoice)->channelVolume[i] = 1.0f;
+	}
+
+	FAudioVoice_SetOutputVoices(*ppSubmixVoice, pSendList);
+
+	/* Filters */
+	if (Flags & FAUDIO_VOICE_USEFILTER)
+	{
+		(*ppSubmixVoice)->filterState = (FAudioFilterState*) audio->pMalloc(
+			sizeof(FAudioFilterState) * InputChannels
+		);
+		FAudio_zero(
+			(*ppSubmixVoice)->filterState,
+			sizeof(FAudioFilterState) * InputChannels
+		);
+	}
+
+	/* Add to list, finally. */
+	FAudio_INTERNAL_InsertSubmixSorted(
+		&audio->submixes,
+		*ppSubmixVoice,
+		audio->submixLock,
+		audio->pMalloc
+	);
+	FAudio_AddRef(audio);
+
+	LOG_API_EXIT(audio)
+	return 0;
+}
+
+uint32_t FAudio_CreateMasteringVoice(
+	FAudio *audio,
+	FAudioMasteringVoice **ppMasteringVoice,
+	uint32_t InputChannels,
+	uint32_t InputSampleRate,
+	uint32_t Flags,
+	uint32_t DeviceIndex,
+	const FAudioEffectChain *pEffectChain
+) {
+	FAudioDeviceDetails details;
+
+	LOG_API_ENTER(audio)
+
+	/* For now we only support one allocated master voice at a time */
+	FAudio_assert(audio->master == NULL);
+
+	if (FAudio_GetDeviceDetails(audio, DeviceIndex, &details) != 0)
+	{
+		return FAUDIO_E_INVALID_CALL;
+	}
+
+	*ppMasteringVoice = (FAudioMasteringVoice*) audio->pMalloc(sizeof(FAudioVoice));
+	FAudio_zero(*ppMasteringVoice, sizeof(FAudioMasteringVoice));
+	(*ppMasteringVoice)->audio = audio;
+	(*ppMasteringVoice)->type = FAUDIO_VOICE_MASTER;
+	(*ppMasteringVoice)->flags = Flags;
+	(*ppMasteringVoice)->effectLock = FAudio_PlatformCreateMutex();
+	LOG_MUTEX_CREATE(audio, (*ppMasteringVoice)->effectLock)
+	(*ppMasteringVoice)->volumeLock = FAudio_PlatformCreateMutex();
+	LOG_MUTEX_CREATE(audio, (*ppMasteringVoice)->volumeLock)
+
+	/* Default Levels */
+	(*ppMasteringVoice)->volume = 1.0f;
+
+	/* Master Properties */
+	(*ppMasteringVoice)->master.inputChannels = (InputChannels == FAUDIO_DEFAULT_CHANNELS) ?
+		details.OutputFormat.Format.nChannels :
+		InputChannels;
+	(*ppMasteringVoice)->master.inputSampleRate = (InputSampleRate == FAUDIO_DEFAULT_SAMPLERATE) ?
+		details.OutputFormat.Format.nSamplesPerSec :
+		InputSampleRate;
+
+	/* Sends/Effects */
+	FAudio_zero(&(*ppMasteringVoice)->sends, sizeof(FAudioVoiceSends));
+	FAudioVoice_SetEffectChain(*ppMasteringVoice, pEffectChain);
+
+	/* This is now safe enough to assign */
+	audio->master = *ppMasteringVoice;
+
+	/* Build the device format.
+	 * The most unintuitive part of this is the use of outputChannels
+	 * instead of master.inputChannels. Bizarrely, the effect chain can
+	 * dictate the _actual_ output channel count, and when the channel count
+	 * mismatches, we have to add a staging buffer for effects to process on
+	 * before ultimately copying the final result to the device. ARGH.
+	 */
+	WriteWaveFormatExtensible(
+		&audio->mixFormat,
+		audio->master->outputChannels,
+		audio->master->master.inputSampleRate,
+		&DATAFORMAT_SUBTYPE_IEEE_FLOAT
+	);
+
+	/* Platform Device */
+	FAudio_AddRef(audio);
+	FAudio_PlatformInit(
+		audio,
+		audio->initFlags,
+		DeviceIndex,
+		&audio->mixFormat,
+		&audio->updateSize,
+		&audio->platform
+	);
+	if (audio->platform == NULL)
+	{
+		FAudioVoice_DestroyVoice(*ppMasteringVoice);
+		*ppMasteringVoice = NULL;
+
+		/* Not the best code, but it's probably true? */
+		return FAUDIO_E_DEVICE_INVALIDATED;
+	}
+	audio->master->outputChannels = audio->mixFormat.Format.nChannels;
+	audio->master->master.inputSampleRate = audio->mixFormat.Format.nSamplesPerSec;
+
+	/* Effect Chain Cache */
+	if ((*ppMasteringVoice)->master.inputChannels != (*ppMasteringVoice)->outputChannels)
+	{
+		(*ppMasteringVoice)->master.effectCache = (float*) audio->pMalloc(
+			sizeof(float) *
+			audio->updateSize *
+			(*ppMasteringVoice)->master.inputChannels
+		);
+	}
+
+	LOG_API_EXIT(audio)
+	return 0;
+}
+
+uint32_t FAudio_CreateMasteringVoice8(
+	FAudio *audio,
+	FAudioMasteringVoice **ppMasteringVoice,
+	uint32_t InputChannels,
+	uint32_t InputSampleRate,
+	uint32_t Flags,
+	uint16_t *szDeviceId,
+	const FAudioEffectChain *pEffectChain,
+	FAudioStreamCategory StreamCategory
+) {
+	uint32_t DeviceIndex, retval;
+
+	LOG_API_ENTER(audio)
+
+	/* Eventually, we'll want the old CreateMastering to call the new one.
+	 * That will depend on us being able to use DeviceID though.
+	 * For now, use our little ID hack to turn szDeviceId into DeviceIndex.
+	 * -flibit
+	 */
+	if (szDeviceId == NULL || szDeviceId[0] == 0)
+	{
+		DeviceIndex = 0;
+	}
+	else
+	{
+		DeviceIndex = szDeviceId[0] - L'0';
+		if (DeviceIndex > FAudio_PlatformGetDeviceCount())
+		{
+			DeviceIndex = 0;
+		}
+	}
+
+	/* Note that StreamCategory is ignored! */
+	retval = FAudio_CreateMasteringVoice(
+		audio,
+		ppMasteringVoice,
+		InputChannels,
+		InputSampleRate,
+		Flags,
+		DeviceIndex,
+		pEffectChain
+	);
+
+	LOG_API_EXIT(audio)
+	return retval;
+}
+
+void FAudio_SetEngineProcedureEXT(
+	FAudio *audio,
+	FAudioEngineProcedureEXT clientEngineProc,
+	void *user
+) {
+	LOG_API_ENTER(audio)
+	audio->pClientEngineProc = clientEngineProc;
+	audio->clientEngineUser = user;
+	LOG_API_EXIT(audio)
+}
+
+uint32_t FAudio_StartEngine(FAudio *audio)
+{
+	LOG_API_ENTER(audio)
+	audio->active = 1;
+	LOG_API_EXIT(audio)
+	return 0;
+}
+
+void FAudio_StopEngine(FAudio *audio)
+{
+	LOG_API_ENTER(audio)
+	audio->active = 0;
+	FAudio_OPERATIONSET_CommitAll(audio);
+	FAudio_OPERATIONSET_Execute(audio);
+	LOG_API_EXIT(audio)
+}
+
+uint32_t FAudio_CommitOperationSet(FAudio *audio, uint32_t OperationSet)
+{
+	LOG_API_ENTER(audio)
+	if (OperationSet == FAUDIO_COMMIT_ALL)
+	{
+		FAudio_OPERATIONSET_CommitAll(audio);
+	}
+	else
+	{
+		FAudio_OPERATIONSET_Commit(audio, OperationSet);
+	}
+	LOG_API_EXIT(audio)
+	return 0;
+}
+
+uint32_t FAudio_CommitChanges(FAudio *audio)
+{
+	FAudio_Log(
+		"IF YOU CAN READ THIS, YOUR PROGRAM IS ABOUT TO BREAK!"
+		"\n\nEither you or somebody else is using FAudio_CommitChanges,"
+		"\nwhen they should be using FAudio_CommitOperationSet instead."
+		"\n\nIf your program calls this, move to CommitOperationSet."
+		"\n\nIf somebody else is calling this, find out who it is and"
+		"\nfile a bug report with them ASAP."
+	);
+
+	/* Seriously, this is like the worst possible thing short of no-oping.
+	 * For the love-a Pete, just migrate, do it, what is wrong with you
+	 */
+	return FAudio_CommitOperationSet(audio, FAUDIO_COMMIT_ALL);
+}
+
+void FAudio_GetPerformanceData(
+	FAudio *audio,
+	FAudioPerformanceData *pPerfData
+) {
+	LinkedList *list;
+	FAudioSourceVoice *source;
+
+	LOG_API_ENTER(audio)
+
+	FAudio_zero(pPerfData, sizeof(FAudioPerformanceData));
+
+	FAudio_PlatformLockMutex(audio->sourceLock);
+	LOG_MUTEX_LOCK(audio, audio->sourceLock)
+	list = audio->sources;
+	while (list != NULL)
+	{
+		source = (FAudioSourceVoice*) list->entry;
+		pPerfData->TotalSourceVoiceCount += 1;
+		if (source->src.active)
+		{
+			pPerfData->ActiveSourceVoiceCount += 1;
+		}
+		list = list->next;
+	}
+	FAudio_PlatformUnlockMutex(audio->sourceLock);
+	LOG_MUTEX_UNLOCK(audio, audio->sourceLock)
+
+	FAudio_PlatformLockMutex(audio->submixLock);
+	LOG_MUTEX_LOCK(audio, audio->submixLock)
+	list = audio->submixes;
+	while (list != NULL)
+	{
+		pPerfData->ActiveSubmixVoiceCount += 1;
+		list = list->next;
+	}
+	FAudio_PlatformUnlockMutex(audio->submixLock);
+	LOG_MUTEX_UNLOCK(audio, audio->submixLock)
+
+	if (audio->master != NULL)
+	{
+		/* estimate, should use real latency from platform */
+		pPerfData->CurrentLatencyInSamples = 2 * audio->updateSize;
+	}
+
+	LOG_API_EXIT(audio)
+}
+
+void FAudio_SetDebugConfiguration(
+	FAudio *audio,
+	FAudioDebugConfiguration *pDebugConfiguration,
+	void* pReserved
+) {
+#ifndef FAUDIO_DISABLE_DEBUGCONFIGURATION
+	char *env;
+
+	LOG_API_ENTER(audio)
+
+	FAudio_memcpy(
+		&audio->debug,
+		pDebugConfiguration,
+		sizeof(FAudioDebugConfiguration)
+	);
+
+	env = FAudio_getenv("FAUDIO_LOG_EVERYTHING");
+	if (env != NULL && *env == '1')
+	{
+		audio->debug.TraceMask = (
+			FAUDIO_LOG_ERRORS |
+			FAUDIO_LOG_WARNINGS |
+			FAUDIO_LOG_INFO |
+			FAUDIO_LOG_DETAIL |
+			FAUDIO_LOG_API_CALLS |
+			FAUDIO_LOG_FUNC_CALLS |
+			FAUDIO_LOG_TIMING |
+			FAUDIO_LOG_LOCKS |
+			FAUDIO_LOG_MEMORY |
+			FAUDIO_LOG_STREAMING
+		);
+		audio->debug.LogThreadID = 1;
+		audio->debug.LogFunctionName = 1;
+		audio->debug.LogTiming = 1;
+	}
+
+	#define CHECK_ENV(type) \
+		env = FAudio_getenv("FAUDIO_LOG_" #type); \
+		if (env != NULL) \
+		{ \
+			if (*env == '1') \
+			{ \
+				audio->debug.TraceMask |= FAUDIO_LOG_##type; \
+			} \
+			else \
+			{ \
+				audio->debug.TraceMask &= ~FAUDIO_LOG_##type; \
+			} \
+		}
+	CHECK_ENV(ERRORS)
+	CHECK_ENV(WARNINGS)
+	CHECK_ENV(INFO)
+	CHECK_ENV(DETAIL)
+	CHECK_ENV(API_CALLS)
+	CHECK_ENV(FUNC_CALLS)
+	CHECK_ENV(TIMING)
+	CHECK_ENV(LOCKS)
+	CHECK_ENV(MEMORY)
+	CHECK_ENV(STREAMING)
+	#undef CHECK_ENV
+	#define CHECK_ENV(envvar, boolvar) \
+		env = FAudio_getenv("FAUDIO_LOG_LOG" #envvar); \
+		if (env != NULL) \
+		{ \
+			audio->debug.Log##boolvar = (*env == '1'); \
+		}
+	CHECK_ENV(THREADID, ThreadID)
+	CHECK_ENV(FILELINE, Fileline)
+	CHECK_ENV(FUNCTIONNAME, FunctionName)
+	CHECK_ENV(TIMING, Timing)
+	#undef CHECK_ENV
+
+	LOG_API_EXIT(audio)
+#endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */
+}
+
+void FAudio_GetProcessingQuantum(
+	FAudio *audio,
+	uint32_t *quantumNumerator,
+	uint32_t *quantumDenominator
+) {
+	FAudio_assert(audio->master != NULL);
+	if (quantumNumerator != NULL)
+	{
+		*quantumNumerator = audio->updateSize;
+	}
+	if (quantumDenominator != NULL)
+	{
+		*quantumDenominator = audio->master->master.inputSampleRate;
+	}
+}
+
+/* FAudioVoice Interface */
+
+static void FAudio_RecalcMixMatrix(FAudioVoice *voice, uint32_t sendIndex)
+{
+	uint32_t oChan, s, d;
+	FAudioVoice *out = voice->sends.pSends[sendIndex].pOutputVoice;
+	float volume, *matrix = voice->mixCoefficients[sendIndex];
+
+	if (voice->type == FAUDIO_VOICE_SUBMIX)
+	{
+		volume = 1.f;
+	}
+	else
+	{
+		volume = voice->volume;
+	}
+
+	if (out->type == FAUDIO_VOICE_MASTER)
+	{
+		oChan = out->master.inputChannels;
+	}
+	else
+	{
+		oChan = out->mix.inputChannels;
+	}
+
+	for (d = 0; d < oChan; d += 1)
+	{
+		for (s = 0; s < voice->outputChannels; s += 1)
+		{
+			matrix[d * voice->outputChannels + s] = volume *
+				voice->channelVolume[s] *
+				voice->sendCoefficients[sendIndex][d * voice->outputChannels + s];
+		}
+	}
+}
+
+void FAudioVoice_GetVoiceDetails(
+	FAudioVoice *voice,
+	FAudioVoiceDetails *pVoiceDetails
+) {
+	LOG_API_ENTER(voice->audio)
+
+	pVoiceDetails->CreationFlags = voice->flags;
+	pVoiceDetails->ActiveFlags = voice->flags;
+	if (voice->type == FAUDIO_VOICE_SOURCE)
+	{
+		pVoiceDetails->InputChannels = voice->src.format->nChannels;
+		pVoiceDetails->InputSampleRate = voice->src.format->nSamplesPerSec;
+	}
+	else if (voice->type == FAUDIO_VOICE_SUBMIX)
+	{
+		pVoiceDetails->InputChannels = voice->mix.inputChannels;
+		pVoiceDetails->InputSampleRate = voice->mix.inputSampleRate;
+	}
+	else if (voice->type == FAUDIO_VOICE_MASTER)
+	{
+		pVoiceDetails->InputChannels = voice->master.inputChannels;
+		pVoiceDetails->InputSampleRate = voice->master.inputSampleRate;
+	}
+	else
+	{
+		FAudio_assert(0 && "Unknown voice type!");
+	}
+
+	LOG_API_EXIT(voice->audio)
+}
+
+uint32_t FAudioVoice_SetOutputVoices(
+	FAudioVoice *voice,
+	const FAudioVoiceSends *pSendList
+) {
+	uint32_t i;
+	uint32_t outChannels;
+	FAudioVoiceSends defaultSends;
+	FAudioSendDescriptor defaultSend;
+
+	LOG_API_ENTER(voice->audio)
+
+	if (voice->type == FAUDIO_VOICE_MASTER)
+	{
+		LOG_API_EXIT(voice->audio)
+		return FAUDIO_E_INVALID_CALL;
+	}
+
+	FAudio_PlatformLockMutex(voice->sendLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+
+	if (FAudio_INTERNAL_VoiceOutputFrequency(voice, pSendList) != 0)
+	{
+		LOG_ERROR(
+			voice->audio,
+			"%s",
+			"Changing the sample rate while an effect chain is attached is invalid!"
+		)
+		FAudio_PlatformUnlockMutex(voice->sendLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+		LOG_API_EXIT(voice->audio)
+		return FAUDIO_E_INVALID_CALL;
+	}
+
+	FAudio_PlatformLockMutex(voice->volumeLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
+
+	/* FIXME: This is lazy... */
+	for (i = 0; i < voice->sends.SendCount; i += 1)
+	{
+		voice->audio->pFree(voice->sendCoefficients[i]);
+	}
+	if (voice->sendCoefficients != NULL)
+	{
+		voice->audio->pFree(voice->sendCoefficients);
+	}
+	for (i = 0; i < voice->sends.SendCount; i += 1)
+	{
+		voice->audio->pFree(voice->mixCoefficients[i]);
+	}
+	if (voice->mixCoefficients != NULL)
+	{
+		voice->audio->pFree(voice->mixCoefficients);
+	}
+	if (voice->sendMix != NULL)
+	{
+		voice->audio->pFree(voice->sendMix);
+	}
+	if (voice->sendFilter != NULL)
+	{
+		voice->audio->pFree(voice->sendFilter);
+		voice->sendFilter = NULL;
+	}
+	if (voice->sendFilterState != NULL)
+	{
+		for (i = 0; i < voice->sends.SendCount; i += 1)
+		{
+			if (voice->sendFilterState[i] != NULL)
+			{
+				voice->audio->pFree(voice->sendFilterState[i]);
+			}
+		}
+		voice->audio->pFree(voice->sendFilterState);
+		voice->sendFilterState = NULL;
+	}
+	if (voice->sends.pSends != NULL)
+	{
+		voice->audio->pFree(voice->sends.pSends);
+	}
+
+	if (pSendList == NULL)
+	{
+		/* Default to the mastering voice as output */
+		defaultSend.Flags = 0;
+		defaultSend.pOutputVoice = voice->audio->master;
+		defaultSends.SendCount = 1;
+		defaultSends.pSends = &defaultSend;
+		pSendList = &defaultSends;
+	}
+	else if (pSendList->SendCount == 0)
+	{
+		/* No sends? Nothing to do... */
+		voice->sendCoefficients = NULL;
+		voice->mixCoefficients = NULL;
+		voice->sendMix = NULL;
+		FAudio_zero(&voice->sends, sizeof(FAudioVoiceSends));
+
+		FAudio_PlatformUnlockMutex(voice->volumeLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
+		FAudio_PlatformUnlockMutex(voice->sendLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+	/* Copy send list */
+	voice->sends.SendCount = pSendList->SendCount;
+	voice->sends.pSends = (FAudioSendDescriptor*) voice->audio->pMalloc(
+		pSendList->SendCount * sizeof(FAudioSendDescriptor)
+	);
+	FAudio_memcpy(
+		voice->sends.pSends,
+		pSendList->pSends,
+		pSendList->SendCount * sizeof(FAudioSendDescriptor)
+	);
+
+	/* Allocate/Reset default output matrix, mixer function, filters */
+	voice->sendCoefficients = (float**) voice->audio->pMalloc(
+		sizeof(float*) * pSendList->SendCount
+	);
+	voice->mixCoefficients = (float**) voice->audio->pMalloc(
+		sizeof(float*) * pSendList->SendCount
+	);
+	voice->sendMix = (FAudioMixCallback*) voice->audio->pMalloc(
+		sizeof(FAudioMixCallback) * pSendList->SendCount
+	);
+
+	for (i = 0; i < pSendList->SendCount; i += 1)
+	{
+		if (pSendList->pSends[i].pOutputVoice->type == FAUDIO_VOICE_MASTER)
+		{
+			outChannels = pSendList->pSends[i].pOutputVoice->master.inputChannels;
+		}
+		else
+		{
+			outChannels = pSendList->pSends[i].pOutputVoice->mix.inputChannels;
+		}
+		voice->sendCoefficients[i] = (float*) voice->audio->pMalloc(
+			sizeof(float) * voice->outputChannels * outChannels
+		);
+		voice->mixCoefficients[i] = (float*) voice->audio->pMalloc(
+			sizeof(float) * voice->outputChannels * outChannels
+		);
+
+		FAudio_assert(voice->outputChannels > 0 && voice->outputChannels < 9);
+		FAudio_assert(outChannels > 0 && outChannels < 9);
+		FAudio_memcpy(
+			voice->sendCoefficients[i],
+			FAUDIO_INTERNAL_MATRIX_DEFAULTS[voice->outputChannels - 1][outChannels - 1],
+			voice->outputChannels * outChannels * sizeof(float)
+		);
+		FAudio_RecalcMixMatrix(voice, i);
+
+		if (voice->outputChannels == 1)
+		{
+			if (outChannels == 1)
+			{
+				voice->sendMix[i] = FAudio_INTERNAL_Mix_1in_1out_Scalar;
+			}
+			else if (outChannels == 2)
+			{
+				voice->sendMix[i] = FAudio_INTERNAL_Mix_1in_2out_Scalar;
+			}
+			else if (outChannels == 6)
+			{
+				voice->sendMix[i] = FAudio_INTERNAL_Mix_1in_6out_Scalar;
+			}
+			else if (outChannels == 8)
+			{
+				voice->sendMix[i] = FAudio_INTERNAL_Mix_1in_8out_Scalar;
+			}
+			else
+			{
+				voice->sendMix[i] = FAudio_INTERNAL_Mix_Generic;
+			}
+		}
+		else if (voice->outputChannels == 2)
+		{
+			if (outChannels == 1)
+			{
+				voice->sendMix[i] = FAudio_INTERNAL_Mix_2in_1out_Scalar;
+			}
+			else if (outChannels == 2)
+			{
+				voice->sendMix[i] = FAudio_INTERNAL_Mix_2in_2out_Scalar;
+			}
+			else if (outChannels == 6)
+			{
+				voice->sendMix[i] = FAudio_INTERNAL_Mix_2in_6out_Scalar;
+			}
+			else if (outChannels == 8)
+			{
+				voice->sendMix[i] = FAudio_INTERNAL_Mix_2in_8out_Scalar;
+			}
+			else
+			{
+				voice->sendMix[i] = FAudio_INTERNAL_Mix_Generic;
+			}
+		}
+		else
+		{
+			voice->sendMix[i] = FAudio_INTERNAL_Mix_Generic;
+		}
+
+		if (pSendList->pSends[i].Flags & FAUDIO_SEND_USEFILTER)
+		{
+			/* Allocate the whole send filter array if needed... */
+			if (voice->sendFilter == NULL)
+			{
+				voice->sendFilter = (FAudioFilterParameters*) voice->audio->pMalloc(
+					sizeof(FAudioFilterParameters) * pSendList->SendCount
+				);
+			}
+			if (voice->sendFilterState == NULL)
+			{
+				voice->sendFilterState = (FAudioFilterState**) voice->audio->pMalloc(
+					sizeof(FAudioFilterState*) * pSendList->SendCount
+				);
+				FAudio_zero(
+					voice->sendFilterState,
+					sizeof(FAudioFilterState*) * pSendList->SendCount
+				);
+			}
+
+			/* ... then fill in this send's filter data */
+			voice->sendFilter[i].Type = FAUDIO_DEFAULT_FILTER_TYPE;
+			voice->sendFilter[i].Frequency = FAUDIO_DEFAULT_FILTER_FREQUENCY;
+			voice->sendFilter[i].OneOverQ = FAUDIO_DEFAULT_FILTER_ONEOVERQ;
+			voice->sendFilterState[i] = (FAudioFilterState*) voice->audio->pMalloc(
+				sizeof(FAudioFilterState) * outChannels
+			);
+			FAudio_zero(
+				voice->sendFilterState[i],
+				sizeof(FAudioFilterState) * outChannels
+			);
+		}
+	}
+
+	FAudio_PlatformUnlockMutex(voice->volumeLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
+
+	FAudio_PlatformUnlockMutex(voice->sendLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+uint32_t FAudioVoice_SetEffectChain(
+	FAudioVoice *voice,
+	const FAudioEffectChain *pEffectChain
+) {
+	uint32_t i;
+	FAPO *fapo;
+	uint32_t channelCount;
+	FAudioVoiceDetails voiceDetails;
+	FAPORegistrationProperties *pProps;
+	FAudioWaveFormatExtensible srcFmt, dstFmt;
+	FAPOLockForProcessBufferParameters srcLockParams, dstLockParams;
+
+	LOG_API_ENTER(voice->audio)
+
+	FAudioVoice_GetVoiceDetails(voice, &voiceDetails);
+
+	/* SetEffectChain must not change the number of output channels once the voice has been created */
+	if (pEffectChain == NULL && voice->outputChannels != 0)
+	{
+		/* cannot remove an effect chain that changes the number of channels */
+		if (voice->outputChannels != voiceDetails.InputChannels)
+		{
+			LOG_ERROR(
+				voice->audio,
+				"%s",
+				"Cannot remove effect chain that changes the number of channels"
+			)
+			FAudio_assert(0 && "Cannot remove effect chain that changes the number of channels");
+			LOG_API_EXIT(voice->audio)
+			return FAUDIO_E_INVALID_CALL;
+		}
+	}
+
+	if (pEffectChain != NULL && voice->outputChannels != 0)
+	{
+		uint32_t lst = pEffectChain->EffectCount - 1;
+
+		/* new effect chain must have same number of output channels */
+		if (voice->outputChannels != pEffectChain->pEffectDescriptors[lst].OutputChannels)
+		{
+			LOG_ERROR(
+				voice->audio,
+				"%s",
+				"New effect chain must have same number of output channels as the old chain"
+			)
+			FAudio_assert(0 && "New effect chain must have same number of output channels as the old chain");
+			LOG_API_EXIT(voice->audio)
+			return FAUDIO_E_INVALID_CALL;
+		}
+	}
+
+	FAudio_PlatformLockMutex(voice->effectLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
+
+	if (pEffectChain == NULL)
+	{
+		FAudio_INTERNAL_FreeEffectChain(voice);
+		FAudio_zero(&voice->effects, sizeof(voice->effects));
+		voice->outputChannels = voiceDetails.InputChannels;
+	}
+	else
+	{
+		/* Validate incoming chain before changing the current chain */
+
+		/* These are always the same, so just write them now. */
+		srcLockParams.pFormat = &srcFmt.Format;
+		dstLockParams.pFormat = &dstFmt.Format;
+		if (voice->type == FAUDIO_VOICE_SOURCE)
+		{
+			srcLockParams.MaxFrameCount = voice->src.resampleSamples;
+			dstLockParams.MaxFrameCount = voice->src.resampleSamples;
+		}
+		else if (voice->type == FAUDIO_VOICE_SUBMIX)
+		{
+			srcLockParams.MaxFrameCount = voice->mix.outputSamples;
+			dstLockParams.MaxFrameCount = voice->mix.outputSamples;
+		}
+		else if (voice->type == FAUDIO_VOICE_MASTER)
+		{
+			srcLockParams.MaxFrameCount = voice->audio->updateSize;
+			dstLockParams.MaxFrameCount = voice->audio->updateSize;
+		}
+
+		/* The first source is the voice input data... */
+		srcFmt.Format.wBitsPerSample = 32;
+		srcFmt.Format.wFormatTag = FAUDIO_FORMAT_EXTENSIBLE;
+		srcFmt.Format.nChannels = voiceDetails.InputChannels;
+		srcFmt.Format.nSamplesPerSec = voiceDetails.InputSampleRate;
+		srcFmt.Format.nBlockAlign = srcFmt.Format.nChannels * (srcFmt.Format.wBitsPerSample / 8);
+		srcFmt.Format.nAvgBytesPerSec = srcFmt.Format.nSamplesPerSec * srcFmt.Format.nBlockAlign;
+		srcFmt.Format.cbSize = sizeof(FAudioWaveFormatExtensible) - sizeof(FAudioWaveFormatEx);
+		srcFmt.Samples.wValidBitsPerSample = srcFmt.Format.wBitsPerSample;
+		srcFmt.dwChannelMask = 0;
+		FAudio_memcpy(&srcFmt.SubFormat, &DATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(FAudioGUID));
+		FAudio_memcpy(&dstFmt, &srcFmt, sizeof(srcFmt));
+
+		for (i = 0; i < pEffectChain->EffectCount; i += 1)
+		{
+			fapo = pEffectChain->pEffectDescriptors[i].pEffect;
+
+			/* ... then we get this effect's format... */
+			dstFmt.Format.nChannels = pEffectChain->pEffectDescriptors[i].OutputChannels;
+			dstFmt.Format.nBlockAlign = dstFmt.Format.nChannels * (dstFmt.Format.wBitsPerSample / 8);
+			dstFmt.Format.nAvgBytesPerSec = dstFmt.Format.nSamplesPerSec * dstFmt.Format.nBlockAlign;
+
+			/* FIXME: This error needs to be found _before_ we start
+			 * shredding the voice's state. This function is highly
+			 * destructive so any errors need to be found at the
+			 * beginning, not in the middle! We can't undo this!
+			 * -flibit
+			 */
+			if (fapo->LockForProcess(fapo, 1, &srcLockParams, 1, &dstLockParams))
+			{
+				LOG_ERROR(
+					voice->audio,
+					"%s",
+					"Effect output format not supported"
+				)
+				FAudio_assert(0 && "Effect output format not supported");
+				FAudio_PlatformUnlockMutex(voice->effectLock);
+				LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
+				LOG_API_EXIT(voice->audio)
+				return FAUDIO_E_UNSUPPORTED_FORMAT;
+			}
+
+			/* Okay, now this effect is the source and the next
+			 * effect will be the destination. Repeat until no
+			 * effects left.
+			 */
+			FAudio_memcpy(&srcFmt, &dstFmt, sizeof(srcFmt));
+		}
+
+		FAudio_INTERNAL_FreeEffectChain(voice);
+		FAudio_INTERNAL_AllocEffectChain(
+			voice,
+			pEffectChain
+		);
+
+		/* check if in-place processing is supported */
+		channelCount = voiceDetails.InputChannels;
+		for (i = 0; i < voice->effects.count; i += 1)
+		{
+			fapo = voice->effects.desc[i].pEffect;
+			if (fapo->GetRegistrationProperties(fapo, &pProps) == 0)
+			{
+				voice->effects.inPlaceProcessing[i] = (pProps->Flags & FAPO_FLAG_INPLACE_SUPPORTED) == FAPO_FLAG_INPLACE_SUPPORTED;
+				voice->effects.inPlaceProcessing[i] &= (channelCount == voice->effects.desc[i].OutputChannels);
+				channelCount = voice->effects.desc[i].OutputChannels;
+
+				/* Fails if in-place processing is mandatory and
+				 * the chain forces us to do otherwise...
+				 */
+				FAudio_assert(
+					!(pProps->Flags & FAPO_FLAG_INPLACE_REQUIRED) ||
+					voice->effects.inPlaceProcessing[i]
+				);
+
+				voice->audio->pFree(pProps);
+			}
+		}
+		voice->outputChannels = channelCount;
+	}
+
+	FAudio_PlatformUnlockMutex(voice->effectLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+uint32_t FAudioVoice_EnableEffect(
+	FAudioVoice *voice,
+	uint32_t EffectIndex,
+	uint32_t OperationSet
+) {
+	LOG_API_ENTER(voice->audio)
+
+	if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
+	{
+		FAudio_OPERATIONSET_QueueEnableEffect(
+			voice,
+			EffectIndex,
+			OperationSet
+		);
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+	FAudio_PlatformLockMutex(voice->effectLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
+	voice->effects.desc[EffectIndex].InitialState = 1;
+	FAudio_PlatformUnlockMutex(voice->effectLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+uint32_t FAudioVoice_DisableEffect(
+	FAudioVoice *voice,
+	uint32_t EffectIndex,
+	uint32_t OperationSet
+) {
+	LOG_API_ENTER(voice->audio)
+
+	if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
+	{
+		FAudio_OPERATIONSET_QueueDisableEffect(
+			voice,
+			EffectIndex,
+			OperationSet
+		);
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+	FAudio_PlatformLockMutex(voice->effectLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
+	voice->effects.desc[EffectIndex].InitialState = 0;
+	FAudio_PlatformUnlockMutex(voice->effectLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+void FAudioVoice_GetEffectState(
+	FAudioVoice *voice,
+	uint32_t EffectIndex,
+	int32_t *pEnabled
+) {
+	LOG_API_ENTER(voice->audio)
+	FAudio_PlatformLockMutex(voice->effectLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
+	*pEnabled = voice->effects.desc[EffectIndex].InitialState;
+	FAudio_PlatformUnlockMutex(voice->effectLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
+	LOG_API_EXIT(voice->audio)
+}
+
+uint32_t FAudioVoice_SetEffectParameters(
+	FAudioVoice *voice,
+	uint32_t EffectIndex,
+	const void *pParameters,
+	uint32_t ParametersByteSize,
+	uint32_t OperationSet
+) {
+	LOG_API_ENTER(voice->audio)
+
+	if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
+	{
+		FAudio_OPERATIONSET_QueueSetEffectParameters(
+			voice,
+			EffectIndex,
+			pParameters,
+			ParametersByteSize,
+			OperationSet
+		);
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+	if (voice->effects.parameters[EffectIndex] == NULL)
+	{
+		voice->effects.parameters[EffectIndex] = voice->audio->pMalloc(
+			ParametersByteSize
+		);
+		voice->effects.parameterSizes[EffectIndex] = ParametersByteSize;
+	}
+	FAudio_PlatformLockMutex(voice->effectLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
+	if (voice->effects.parameterSizes[EffectIndex] < ParametersByteSize)
+	{
+		voice->effects.parameters[EffectIndex] = voice->audio->pRealloc(
+			voice->effects.parameters[EffectIndex],
+			ParametersByteSize
+		);
+		voice->effects.parameterSizes[EffectIndex] = ParametersByteSize;
+	}
+	FAudio_memcpy(
+		voice->effects.parameters[EffectIndex],
+		pParameters,
+		ParametersByteSize
+	);
+	voice->effects.parameterUpdates[EffectIndex] = 1;
+	FAudio_PlatformUnlockMutex(voice->effectLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+uint32_t FAudioVoice_GetEffectParameters(
+	FAudioVoice *voice,
+	uint32_t EffectIndex,
+	void *pParameters,
+	uint32_t ParametersByteSize
+) {
+	FAPO *fapo;
+	LOG_API_ENTER(voice->audio)
+	FAudio_PlatformLockMutex(voice->effectLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
+	fapo = voice->effects.desc[EffectIndex].pEffect;
+	fapo->GetParameters(fapo, pParameters, ParametersByteSize);
+	FAudio_PlatformUnlockMutex(voice->effectLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+uint32_t FAudioVoice_SetFilterParameters(
+	FAudioVoice *voice,
+	const FAudioFilterParameters *pParameters,
+	uint32_t OperationSet
+) {
+	LOG_API_ENTER(voice->audio)
+
+	if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
+	{
+		FAudio_OPERATIONSET_QueueSetFilterParameters(
+			voice,
+			pParameters,
+			OperationSet
+		);
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+	/* MSDN: "This method is usable only on source and submix voices and
+	 * has no effect on mastering voices."
+	 */
+	if (voice->type == FAUDIO_VOICE_MASTER)
+	{
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+	if (!(voice->flags & FAUDIO_VOICE_USEFILTER))
+	{
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+	FAudio_PlatformLockMutex(voice->filterLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
+	FAudio_memcpy(
+		&voice->filter,
+		pParameters,
+		sizeof(FAudioFilterParameters)
+	);
+	FAudio_PlatformUnlockMutex(voice->filterLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->filterLock)
+
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+void FAudioVoice_GetFilterParameters(
+	FAudioVoice *voice,
+	FAudioFilterParameters *pParameters
+) {
+	LOG_API_ENTER(voice->audio)
+
+	/* MSDN: "This method is usable only on source and submix voices and
+	 * has no effect on mastering voices."
+	 */
+	if (voice->type == FAUDIO_VOICE_MASTER)
+	{
+		LOG_API_EXIT(voice->audio)
+		return;
+	}
+
+	if (!(voice->flags & FAUDIO_VOICE_USEFILTER))
+	{
+		LOG_API_EXIT(voice->audio)
+		return;
+	}
+
+	FAudio_PlatformLockMutex(voice->filterLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
+	FAudio_memcpy(
+		pParameters,
+		&voice->filter,
+		sizeof(FAudioFilterParameters)
+	);
+	FAudio_PlatformUnlockMutex(voice->filterLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->filterLock)
+	LOG_API_EXIT(voice->audio)
+}
+
+uint32_t FAudioVoice_SetOutputFilterParameters(
+	FAudioVoice *voice,
+	FAudioVoice *pDestinationVoice,
+	const FAudioFilterParameters *pParameters,
+	uint32_t OperationSet
+) {
+	uint32_t i;
+	LOG_API_ENTER(voice->audio)
+
+	if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
+	{
+		FAudio_OPERATIONSET_QueueSetOutputFilterParameters(
+			voice,
+			pDestinationVoice,
+			pParameters,
+			OperationSet
+		);
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+	/* MSDN: "This method is usable only on source and submix voices and
+	 * has no effect on mastering voices."
+	 */
+	if (voice->type == FAUDIO_VOICE_MASTER)
+	{
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+	FAudio_PlatformLockMutex(voice->sendLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+
+	/* Find the send index */
+	if (pDestinationVoice == NULL && voice->sends.SendCount == 1)
+	{
+		pDestinationVoice = voice->sends.pSends[0].pOutputVoice;
+	}
+	for (i = 0; i < voice->sends.SendCount; i += 1)
+	{
+		if (pDestinationVoice == voice->sends.pSends[i].pOutputVoice)
+		{
+			break;
+		}
+	}
+	if (i >= voice->sends.SendCount)
+	{
+		LOG_ERROR(
+			voice->audio,
+			"Destination not attached to source: %p %p",
+			(void*) voice,
+			(void*) pDestinationVoice
+		)
+		FAudio_PlatformUnlockMutex(voice->sendLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+		LOG_API_EXIT(voice->audio)
+		return FAUDIO_E_INVALID_CALL;
+	}
+
+	if (!(voice->sends.pSends[i].Flags & FAUDIO_SEND_USEFILTER))
+	{
+		FAudio_PlatformUnlockMutex(voice->sendLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+	/* Set the filter parameters, finally. */
+	FAudio_memcpy(
+		&voice->sendFilter[i],
+		pParameters,
+		sizeof(FAudioFilterParameters)
+	);
+
+	FAudio_PlatformUnlockMutex(voice->sendLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+void FAudioVoice_GetOutputFilterParameters(
+	FAudioVoice *voice,
+	FAudioVoice *pDestinationVoice,
+	FAudioFilterParameters *pParameters
+) {
+	uint32_t i;
+
+	LOG_API_ENTER(voice->audio)
+
+	/* MSDN: "This method is usable only on source and submix voices and
+	 * has no effect on mastering voices."
+	 */
+	if (voice->type == FAUDIO_VOICE_MASTER)
+	{
+		LOG_API_EXIT(voice->audio)
+		return;
+	}
+
+	FAudio_PlatformLockMutex(voice->sendLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+
+	/* Find the send index */
+	if (pDestinationVoice == NULL && voice->sends.SendCount == 1)
+	{
+		pDestinationVoice = voice->sends.pSends[0].pOutputVoice;
+	}
+	for (i = 0; i < voice->sends.SendCount; i += 1)
+	{
+		if (pDestinationVoice == voice->sends.pSends[i].pOutputVoice)
+		{
+			break;
+		}
+	}
+	if (i >= voice->sends.SendCount)
+	{
+		LOG_ERROR(
+			voice->audio,
+			"Destination not attached to source: %p %p",
+			(void*) voice,
+			(void*) pDestinationVoice
+		)
+		FAudio_PlatformUnlockMutex(voice->sendLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+		LOG_API_EXIT(voice->audio)
+		return;
+	}
+
+	if (!(voice->sends.pSends[i].Flags & FAUDIO_SEND_USEFILTER))
+	{
+		FAudio_PlatformUnlockMutex(voice->sendLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+		LOG_API_EXIT(voice->audio)
+		return;
+	}
+
+	/* Set the filter parameters, finally. */
+	FAudio_memcpy(
+		pParameters,
+		&voice->sendFilter[i],
+		sizeof(FAudioFilterParameters)
+	);
+
+	FAudio_PlatformUnlockMutex(voice->sendLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+	LOG_API_EXIT(voice->audio)
+}
+
+uint32_t FAudioVoice_SetVolume(
+	FAudioVoice *voice,
+	float Volume,
+	uint32_t OperationSet
+) {
+	uint32_t i;
+
+	LOG_API_ENTER(voice->audio)
+
+	if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
+	{
+		FAudio_OPERATIONSET_QueueSetVolume(
+			voice,
+			Volume,
+			OperationSet
+		);
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+	FAudio_PlatformLockMutex(voice->sendLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+
+	FAudio_PlatformLockMutex(voice->volumeLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
+
+	voice->volume = FAudio_clamp(
+		Volume,
+		-FAUDIO_MAX_VOLUME_LEVEL,
+		FAUDIO_MAX_VOLUME_LEVEL
+	);
+
+	for (i = 0; i < voice->sends.SendCount; i += 1)
+	{
+		FAudio_RecalcMixMatrix(voice, i);
+	}
+
+	FAudio_PlatformUnlockMutex(voice->volumeLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
+
+	FAudio_PlatformUnlockMutex(voice->sendLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+void FAudioVoice_GetVolume(
+	FAudioVoice *voice,
+	float *pVolume
+) {
+	LOG_API_ENTER(voice->audio)
+	*pVolume = voice->volume;
+	LOG_API_EXIT(voice->audio)
+}
+
+uint32_t FAudioVoice_SetChannelVolumes(
+	FAudioVoice *voice,
+	uint32_t Channels,
+	const float *pVolumes,
+	uint32_t OperationSet
+) {
+	uint32_t i;
+
+	LOG_API_ENTER(voice->audio)
+
+	if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
+	{
+		FAudio_OPERATIONSET_QueueSetChannelVolumes(
+			voice,
+			Channels,
+			pVolumes,
+			OperationSet
+		);
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+	if (pVolumes == NULL)
+	{
+		LOG_API_EXIT(voice->audio)
+		return FAUDIO_E_INVALID_CALL;
+	}
+
+	if (voice->type == FAUDIO_VOICE_MASTER)
+	{
+		LOG_API_EXIT(voice->audio)
+		return FAUDIO_E_INVALID_CALL;
+	}
+
+	if (voice->audio->version > 7 && Channels != voice->outputChannels)
+	{
+		LOG_API_EXIT(voice->audio)
+		return FAUDIO_E_INVALID_CALL;
+	}
+
+	FAudio_PlatformLockMutex(voice->sendLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+
+	FAudio_PlatformLockMutex(voice->volumeLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
+
+	FAudio_memcpy(
+		voice->channelVolume,
+		pVolumes,
+		sizeof(float) * Channels
+	);
+
+	for (i = 0; i < voice->sends.SendCount; i += 1)
+	{
+		FAudio_RecalcMixMatrix(voice, i);
+	}
+
+	FAudio_PlatformUnlockMutex(voice->volumeLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
+
+	FAudio_PlatformUnlockMutex(voice->sendLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+void FAudioVoice_GetChannelVolumes(
+	FAudioVoice *voice,
+	uint32_t Channels,
+	float *pVolumes
+) {
+	LOG_API_ENTER(voice->audio)
+	FAudio_PlatformLockMutex(voice->volumeLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
+	FAudio_memcpy(
+		pVolumes,
+		voice->channelVolume,
+		sizeof(float) * Channels
+	);
+	FAudio_PlatformUnlockMutex(voice->volumeLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
+	LOG_API_EXIT(voice->audio)
+}
+
+uint32_t FAudioVoice_SetOutputMatrix(
+	FAudioVoice *voice,
+	FAudioVoice *pDestinationVoice,
+	uint32_t SourceChannels,
+	uint32_t DestinationChannels,
+	const float *pLevelMatrix,
+	uint32_t OperationSet
+) {
+	uint32_t i, result = 0;
+	LOG_API_ENTER(voice->audio)
+
+	if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
+	{
+		FAudio_OPERATIONSET_QueueSetOutputMatrix(
+			voice,
+			pDestinationVoice,
+			SourceChannels,
+			DestinationChannels,
+			pLevelMatrix,
+			OperationSet
+		);
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+	FAudio_PlatformLockMutex(voice->sendLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+
+	/* Find the send index */
+	if (pDestinationVoice == NULL && voice->sends.SendCount == 1)
+	{
+		pDestinationVoice = voice->sends.pSends[0].pOutputVoice;
+	}
+	FAudio_assert(pDestinationVoice != NULL);
+	for (i = 0; i < voice->sends.SendCount; i += 1)
+	{
+		if (pDestinationVoice == voice->sends.pSends[i].pOutputVoice)
+		{
+			break;
+		}
+	}
+	if (i >= voice->sends.SendCount)
+	{
+		LOG_ERROR(
+			voice->audio,
+			"Destination not attached to source: %p %p",
+			(void*) voice,
+			(void*) pDestinationVoice
+		)
+		result = FAUDIO_E_INVALID_CALL;
+		goto end;
+	}
+
+	/* Verify the Source/Destination channel count */
+	if (SourceChannels != voice->outputChannels)
+	{
+		LOG_ERROR(
+			voice->audio,
+			"SourceChannels not equal to voice channel count: %p %d %d",
+			(void*) voice,
+			SourceChannels,
+			voice->outputChannels
+		)
+		result = FAUDIO_E_INVALID_CALL;
+		goto end;
+	}
+
+	if (pDestinationVoice->type == FAUDIO_VOICE_MASTER)
+	{
+		if (DestinationChannels != pDestinationVoice->master.inputChannels)
+		{
+			LOG_ERROR(
+				voice->audio,
+				"DestinationChannels not equal to master channel count: %p %d %d",
+				(void*) pDestinationVoice,
+				DestinationChannels,
+				pDestinationVoice->master.inputChannels
+			)
+			result = FAUDIO_E_INVALID_CALL;
+			goto end;
+		}
+	}
+	else
+	{
+		if (DestinationChannels != pDestinationVoice->mix.inputChannels)
+		{
+			LOG_ERROR(
+				voice->audio,
+				"DestinationChannels not equal to submix channel count: %p %d %d",
+				(void*) pDestinationVoice,
+				DestinationChannels,
+				pDestinationVoice->mix.inputChannels
+			)
+			result = FAUDIO_E_INVALID_CALL;
+			goto end;
+		}
+	}
+
+	/* Set the matrix values, finally */
+	FAudio_PlatformLockMutex(voice->volumeLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
+
+	FAudio_memcpy(
+		voice->sendCoefficients[i],
+		pLevelMatrix,
+		sizeof(float) * SourceChannels * DestinationChannels
+	);
+
+	FAudio_RecalcMixMatrix(voice, i);
+
+	FAudio_PlatformUnlockMutex(voice->volumeLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
+
+end:
+	FAudio_PlatformUnlockMutex(voice->sendLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+	LOG_API_EXIT(voice->audio)
+	return result;
+}
+
+void FAudioVoice_GetOutputMatrix(
+	FAudioVoice *voice,
+	FAudioVoice *pDestinationVoice,
+	uint32_t SourceChannels,
+	uint32_t DestinationChannels,
+	float *pLevelMatrix
+) {
+	uint32_t i;
+
+	LOG_API_ENTER(voice->audio)
+	FAudio_PlatformLockMutex(voice->sendLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+
+	/* Find the send index */
+	for (i = 0; i < voice->sends.SendCount; i += 1)
+	{
+		if (pDestinationVoice == voice->sends.pSends[i].pOutputVoice)
+		{
+			break;
+		}
+	}
+	if (i >= voice->sends.SendCount)
+	{
+		LOG_ERROR(
+			voice->audio,
+			"Destination not attached to source: %p %p",
+			(void*) voice,
+			(void*) pDestinationVoice
+		)
+		FAudio_PlatformUnlockMutex(voice->sendLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+		LOG_API_EXIT(voice->audio)
+		return;
+	}
+
+	/* Verify the Source/Destination channel count */
+	if (voice->type == FAUDIO_VOICE_SOURCE)
+	{
+		FAudio_assert(SourceChannels == voice->src.format->nChannels);
+	}
+	else
+	{
+		FAudio_assert(SourceChannels == voice->mix.inputChannels);
+	}
+	if (pDestinationVoice->type == FAUDIO_VOICE_MASTER)
+	{
+		FAudio_assert(DestinationChannels == pDestinationVoice->master.inputChannels);
+	}
+	else
+	{
+		FAudio_assert(DestinationChannels == pDestinationVoice->mix.inputChannels);
+	}
+
+	/* Get the matrix values, finally */
+	FAudio_memcpy(
+		pLevelMatrix,
+		voice->sendCoefficients[i],
+		sizeof(float) * SourceChannels * DestinationChannels
+	);
+
+	FAudio_PlatformUnlockMutex(voice->sendLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+	LOG_API_EXIT(voice->audio)
+}
+
+void FAudioVoice_DestroyVoice(FAudioVoice *voice)
+{
+	uint32_t i;
+	LOG_API_ENTER(voice->audio)
+
+	/* TODO: Check for dependencies and remove from audio graph first! */
+	FAudio_OPERATIONSET_ClearAllForVoice(voice);
+
+	if (voice->type == FAUDIO_VOICE_SOURCE)
+	{
+		FAudioBufferEntry *entry, *next;
+
+#ifdef FAUDIO_DUMP_VOICES
+		FAudio_DUMPVOICE_Finalize((FAudioSourceVoice*) voice);
+#endif /* FAUDIO_DUMP_VOICES */
+
+		FAudio_PlatformLockMutex(voice->audio->sourceLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+		while (voice == voice->audio->processingSource)
+		{
+			FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
+			LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
+			FAudio_PlatformLockMutex(voice->audio->sourceLock);
+			LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+		}
+		LinkedList_RemoveEntry(
+			&voice->audio->sources,
+			voice,
+			voice->audio->sourceLock,
+			voice->audio->pFree
+		);
+		FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
+
+		entry = voice->src.bufferList;
+		while (entry != NULL)
+		{
+			next = entry->next;
+			voice->audio->pFree(entry);
+			entry = next;
+		}
+
+		entry = voice->src.flushList;
+		while (entry != NULL)
+		{
+			next = entry->next;
+			voice->audio->pFree(entry);
+			entry = next;
+		}
+
+		voice->audio->pFree(voice->src.format);
+		LOG_MUTEX_DESTROY(voice->audio, voice->src.bufferLock)
+		FAudio_PlatformDestroyMutex(voice->src.bufferLock);
+#ifdef HAVE_WMADEC
+		if (voice->src.wmadec)
+		{
+			FAudio_WMADEC_free(voice);
+		}
+#endif /* HAVE_WMADEC */
+	}
+	else if (voice->type == FAUDIO_VOICE_SUBMIX)
+	{
+		/* Remove submix from list */
+		LinkedList_RemoveEntry(
+			&voice->audio->submixes,
+			voice,
+			voice->audio->submixLock,
+			voice->audio->pFree
+		);
+
+		/* Delete submix data */
+		voice->audio->pFree(voice->mix.inputCache);
+	}
+	else if (voice->type == FAUDIO_VOICE_MASTER)
+	{
+		if (voice->audio->platform != NULL)
+		{
+			FAudio_PlatformQuit(voice->audio->platform);
+			voice->audio->platform = NULL;
+		}
+		if (voice->master.effectCache != NULL)
+		{
+			voice->audio->pFree(voice->master.effectCache);
+		}
+		voice->audio->master = NULL;
+	}
+
+	if (voice->sendLock != NULL)
+	{
+		FAudio_PlatformLockMutex(voice->sendLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+		for (i = 0; i < voice->sends.SendCount; i += 1)
+		{
+			voice->audio->pFree(voice->sendCoefficients[i]);
+		}
+		if (voice->sendCoefficients != NULL)
+		{
+			voice->audio->pFree(voice->sendCoefficients);
+		}
+		for (i = 0; i < voice->sends.SendCount; i += 1)
+		{
+			voice->audio->pFree(voice->mixCoefficients[i]);
+		}
+		if (voice->mixCoefficients != NULL)
+		{
+			voice->audio->pFree(voice->mixCoefficients);
+		}
+		if (voice->sendMix != NULL)
+		{
+			voice->audio->pFree(voice->sendMix);
+		}
+		if (voice->sendFilter != NULL)
+		{
+			voice->audio->pFree(voice->sendFilter);
+		}
+		if (voice->sendFilterState != NULL)
+		{
+			for (i = 0; i < voice->sends.SendCount; i += 1)
+			{
+				if (voice->sendFilterState[i] != NULL)
+				{
+					voice->audio->pFree(voice->sendFilterState[i]);
+				}
+			}
+			voice->audio->pFree(voice->sendFilterState);
+		}
+		if (voice->sends.pSends != NULL)
+		{
+			voice->audio->pFree(voice->sends.pSends);
+		}
+		FAudio_PlatformUnlockMutex(voice->sendLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+		LOG_MUTEX_DESTROY(voice->audio, voice->sendLock)
+		FAudio_PlatformDestroyMutex(voice->sendLock);
+	}
+
+	if (voice->effectLock != NULL)
+	{
+		FAudio_PlatformLockMutex(voice->effectLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
+		FAudio_INTERNAL_FreeEffectChain(voice);
+		FAudio_PlatformUnlockMutex(voice->effectLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
+		LOG_MUTEX_DESTROY(voice->audio, voice->effectLock)
+		FAudio_PlatformDestroyMutex(voice->effectLock);
+	}
+
+	if (voice->filterLock != NULL)
+	{
+		FAudio_PlatformLockMutex(voice->filterLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
+		if (voice->filterState != NULL)
+		{
+			voice->audio->pFree(voice->filterState);
+		}
+		FAudio_PlatformUnlockMutex(voice->filterLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->filterLock)
+		LOG_MUTEX_DESTROY(voice->audio, voice->filterLock)
+		FAudio_PlatformDestroyMutex(voice->filterLock);
+	}
+
+	if (voice->volumeLock != NULL)
+	{
+		FAudio_PlatformLockMutex(voice->volumeLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
+		if (voice->channelVolume != NULL)
+		{
+			voice->audio->pFree(voice->channelVolume);
+		}
+		FAudio_PlatformUnlockMutex(voice->volumeLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
+		LOG_MUTEX_DESTROY(voice->audio, voice->volumeLock)
+		FAudio_PlatformDestroyMutex(voice->volumeLock);
+	}
+
+	LOG_API_EXIT(voice->audio)
+	FAudio_Release(voice->audio);
+	voice->audio->pFree(voice);
+}
+
+/* FAudioSourceVoice Interface */
+
+uint32_t FAudioSourceVoice_Start(
+	FAudioSourceVoice *voice,
+	uint32_t Flags,
+	uint32_t OperationSet
+) {
+	LOG_API_ENTER(voice->audio)
+
+	if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
+	{
+		FAudio_OPERATIONSET_QueueStart(
+			voice,
+			Flags,
+			OperationSet
+		);
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+
+	FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
+
+	FAudio_assert(Flags == 0);
+	voice->src.active = 1;
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+uint32_t FAudioSourceVoice_Stop(
+	FAudioSourceVoice *voice,
+	uint32_t Flags,
+	uint32_t OperationSet
+) {
+	LOG_API_ENTER(voice->audio)
+
+	if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
+	{
+		FAudio_OPERATIONSET_QueueStop(
+			voice,
+			Flags,
+			OperationSet
+		);
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+	FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
+
+	if (Flags & FAUDIO_PLAY_TAILS)
+	{
+		voice->src.active = 2;
+	}
+	else
+	{
+		voice->src.active = 0;
+	}
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+uint32_t FAudioSourceVoice_SubmitSourceBuffer(
+	FAudioSourceVoice *voice,
+	const FAudioBuffer *pBuffer,
+	const FAudioBufferWMA *pBufferWMA
+) {
+	uint32_t adpcmMask, *adpcmByteCount;
+	uint32_t playBegin, playLength, loopBegin, loopLength;
+	FAudioBufferEntry *entry, *list;
+
+	LOG_API_ENTER(voice->audio)
+	LOG_INFO(
+		voice->audio,
+		"%p: {Flags: 0x%x, AudioBytes: %u, pAudioData: %p, Play: %u + %u, Loop: %u + %u x %u}",
+		(void*) voice,
+		pBuffer->Flags,
+		pBuffer->AudioBytes,
+		(const void*) pBuffer->pAudioData,
+		pBuffer->PlayBegin,
+		pBuffer->PlayLength,
+		pBuffer->LoopBegin,
+		pBuffer->LoopLength,
+		pBuffer->LoopCount
+	)
+
+	FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
+#ifdef HAVE_WMADEC
+	FAudio_assert(	(voice->src.wmadec != NULL && (pBufferWMA != NULL || voice->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)) ||
+			(voice->src.wmadec == NULL && (pBufferWMA == NULL && voice->src.format->wFormatTag != FAUDIO_FORMAT_XMAUDIO2))	);
+#endif /* HAVE_WMADEC */
+
+	/* Start off with whatever they just sent us... */
+	playBegin = pBuffer->PlayBegin;
+	playLength = pBuffer->PlayLength;
+	loopBegin = pBuffer->LoopBegin;
+	loopLength = pBuffer->LoopLength;
+
+	/* "LoopBegin/LoopLength must be zero if LoopCount is 0" */
+	if (pBuffer->LoopCount == 0 && (loopBegin > 0 || loopLength > 0))
+	{
+		LOG_API_EXIT(voice->audio)
+		return FAUDIO_E_INVALID_CALL;
+	}
+
+	/* PlayLength Default */
+	if (playLength == 0)
+	{
+		if (voice->src.format->wFormatTag == FAUDIO_FORMAT_MSADPCM)
+		{
+			FAudioADPCMWaveFormat *fmtex = (FAudioADPCMWaveFormat*) voice->src.format;
+			playLength = (
+				pBuffer->AudioBytes /
+				fmtex->wfx.nBlockAlign *
+				fmtex->wSamplesPerBlock
+			) - playBegin;
+		}
+		else if (voice->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
+		{
+			FAudioXMA2WaveFormat *fmtex = (FAudioXMA2WaveFormat*) voice->src.format;
+			playLength = fmtex->dwSamplesEncoded - playBegin;
+		}
+		else if (pBufferWMA != NULL)
+		{
+			playLength = (
+				pBufferWMA->pDecodedPacketCumulativeBytes[pBufferWMA->PacketCount - 1] /
+				(voice->src.format->nChannels * voice->src.format->wBitsPerSample / 8)
+			) - playBegin;
+		}
+		else
+		{
+			playLength = (
+				pBuffer->AudioBytes /
+				voice->src.format->nBlockAlign
+			) - playBegin;
+		}
+	}
+
+	if (pBuffer->LoopCount > 0 && pBufferWMA == NULL && voice->src.format->wFormatTag != FAUDIO_FORMAT_XMAUDIO2)
+	{
+		/* "The value of LoopBegin must be less than PlayBegin + PlayLength" */
+		if (loopBegin >= (playBegin + playLength))
+		{
+			LOG_API_EXIT(voice->audio)
+			return FAUDIO_E_INVALID_CALL;
+		}
+
+		/* LoopLength Default */
+		if (loopLength == 0)
+		{
+			loopLength = playBegin + playLength - loopBegin;
+		}
+
+		/* "The value of LoopBegin + LoopLength must be greater than PlayBegin
+		 * and less than PlayBegin + PlayLength"
+		 */
+		if (	voice->audio->version > 7 && (
+			(loopBegin + loopLength) <= playBegin ||
+			(loopBegin + loopLength) > (playBegin + playLength))	)
+		{
+			LOG_API_EXIT(voice->audio)
+			return FAUDIO_E_INVALID_CALL;
+		}
+	}
+
+	/* For ADPCM, round down to the nearest sample block size */
+	if (voice->src.format->wFormatTag == FAUDIO_FORMAT_MSADPCM)
+	{
+		adpcmMask = ((FAudioADPCMWaveFormat*) voice->src.format)->wSamplesPerBlock;
+		playBegin -= playBegin % adpcmMask;
+		playLength -= playLength % adpcmMask;
+		loopBegin -= loopBegin % adpcmMask;
+		loopLength -= loopLength % adpcmMask;
+
+		/* This is basically a const_cast... */
+		adpcmByteCount = (uint32_t*) &pBuffer->AudioBytes;
+		*adpcmByteCount = (
+			pBuffer->AudioBytes / voice->src.format->nBlockAlign
+		) * voice->src.format->nBlockAlign;
+	}
+	else if (pBufferWMA != NULL || voice->src.format->wFormatTag == FAUDIO_FORMAT_XMAUDIO2)
+	{
+		/* WMA only supports looping the whole buffer */
+		loopBegin = 0;
+		loopLength = playBegin + playLength;
+	}
+
+	/* Allocate, now that we have valid input */
+	entry = (FAudioBufferEntry*) voice->audio->pMalloc(sizeof(FAudioBufferEntry));
+	FAudio_memcpy(&entry->buffer, pBuffer, sizeof(FAudioBuffer));
+	entry->buffer.PlayBegin = playBegin;
+	entry->buffer.PlayLength = playLength;
+	entry->buffer.LoopBegin = loopBegin;
+	entry->buffer.LoopLength = loopLength;
+	if (pBufferWMA != NULL)
+	{
+		FAudio_memcpy(&entry->bufferWMA, pBufferWMA, sizeof(FAudioBufferWMA));
+	}
+	entry->next = NULL;
+
+	if (	voice->audio->version <= 7 && (
+		entry->buffer.LoopCount > 0 &&
+		entry->buffer.LoopBegin + entry->buffer.LoopLength <= entry->buffer.PlayBegin))
+	{
+		entry->buffer.LoopCount = 0;
+	}
+
+#ifdef FAUDIO_DUMP_VOICES
+	/* dumping current buffer, append into "data" section */
+	if (pBuffer->pAudioData != NULL && playLength > 0)
+	{
+		FAudio_DUMPVOICE_WriteBuffer(voice, pBuffer, pBufferWMA, playBegin, playLength);
+	}
+#endif /* FAUDIO_DUMP_VOICES */
+
+	/* Submit! */
+	FAudio_PlatformLockMutex(voice->src.bufferLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
+	if (voice->src.bufferList == NULL)
+	{
+		voice->src.bufferList = entry;
+		voice->src.curBufferOffset = entry->buffer.PlayBegin;
+		voice->src.newBuffer = 1;
+	}
+	else
+	{
+		list = voice->src.bufferList;
+		while (list->next != NULL)
+		{
+			list = list->next;
+		}
+		list->next = entry;
+
+		/* For some bizarre reason we get scenarios where a buffer is freed, only to
+		 * have the allocator give us the exact same address and somehow get a single
+		 * buffer referencing itself. I don't even know.
+		 */
+		FAudio_assert(list != entry);
+	}
+	LOG_INFO(
+		voice->audio,
+		"%p: appended buffer %p",
+		(void*) voice,
+		(void*) &entry->buffer
+	)
+	FAudio_PlatformUnlockMutex(voice->src.bufferLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+uint32_t FAudioSourceVoice_FlushSourceBuffers(
+	FAudioSourceVoice *voice
+) {
+	FAudioBufferEntry *entry, *latest;
+
+	LOG_API_ENTER(voice->audio)
+	FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
+
+	FAudio_PlatformLockMutex(voice->src.bufferLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
+
+	/* If the source is playing, don't flush the active buffer */
+	entry = voice->src.bufferList;
+	if ((voice->src.active == 1) && entry != NULL && !voice->src.newBuffer)
+	{
+		entry = entry->next;
+		voice->src.bufferList->next = NULL;
+	}
+	else
+	{
+		voice->src.curBufferOffset = 0;
+		voice->src.bufferList = NULL;
+		voice->src.newBuffer = 0;
+	}
+
+	/* Move them to the pending flush list */
+	if (entry != NULL)
+	{
+		if (voice->src.flushList == NULL)
+		{
+			voice->src.flushList = entry;
+		}
+		else
+		{
+			latest = voice->src.flushList;
+			while (latest->next != NULL)
+			{
+				latest = latest->next;
+			}
+			latest->next = entry;
+		}
+	}
+
+	FAudio_PlatformUnlockMutex(voice->src.bufferLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+uint32_t FAudioSourceVoice_Discontinuity(
+	FAudioSourceVoice *voice
+) {
+	FAudioBufferEntry *buf;
+
+	LOG_API_ENTER(voice->audio)
+	FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
+
+	FAudio_PlatformLockMutex(voice->src.bufferLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
+
+	if (voice->src.bufferList != NULL)
+	{
+		for (buf = voice->src.bufferList; buf->next != NULL; buf = buf->next);
+		buf->buffer.Flags |= FAUDIO_END_OF_STREAM;
+	}
+
+	FAudio_PlatformUnlockMutex(voice->src.bufferLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+uint32_t FAudioSourceVoice_ExitLoop(
+	FAudioSourceVoice *voice,
+	uint32_t OperationSet
+) {
+	LOG_API_ENTER(voice->audio)
+
+	if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
+	{
+		FAudio_OPERATIONSET_QueueExitLoop(
+			voice,
+			OperationSet
+		);
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+	FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
+
+	FAudio_PlatformLockMutex(voice->src.bufferLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
+
+	if (voice->src.bufferList != NULL)
+	{
+		voice->src.bufferList->buffer.LoopCount = 0;
+	}
+
+	FAudio_PlatformUnlockMutex(voice->src.bufferLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+void FAudioSourceVoice_GetState(
+	FAudioSourceVoice *voice,
+	FAudioVoiceState *pVoiceState,
+	uint32_t Flags
+) {
+	FAudioBufferEntry *entry;
+
+	LOG_API_ENTER(voice->audio)
+	FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
+
+	FAudio_PlatformLockMutex(voice->src.bufferLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
+
+	if (!(Flags & FAUDIO_VOICE_NOSAMPLESPLAYED))
+	{
+		pVoiceState->SamplesPlayed = voice->src.totalSamples;
+	}
+
+	pVoiceState->BuffersQueued = 0;
+	pVoiceState->pCurrentBufferContext = NULL;
+	if (voice->src.bufferList != NULL)
+	{
+		entry = voice->src.bufferList;
+		if (!voice->src.newBuffer)
+		{
+			pVoiceState->pCurrentBufferContext = entry->buffer.pContext;
+		}
+		do
+		{
+			pVoiceState->BuffersQueued += 1;
+			entry = entry->next;
+		} while (entry != NULL);
+	}
+
+	/* Pending flushed buffers also count */
+	entry = voice->src.flushList;
+	while (entry != NULL)
+	{
+		pVoiceState->BuffersQueued += 1;
+		entry = entry->next;
+	}
+
+	LOG_INFO(
+		voice->audio,
+		"-> {pCurrentBufferContext: %p, BuffersQueued: %u, SamplesPlayed: %"FAudio_PRIu64"}",
+		pVoiceState->pCurrentBufferContext, pVoiceState->BuffersQueued,
+		pVoiceState->SamplesPlayed
+	)
+
+	FAudio_PlatformUnlockMutex(voice->src.bufferLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
+	LOG_API_EXIT(voice->audio)
+}
+
+uint32_t FAudioSourceVoice_SetFrequencyRatio(
+	FAudioSourceVoice *voice,
+	float Ratio,
+	uint32_t OperationSet
+) {
+	LOG_API_ENTER(voice->audio)
+
+	if (OperationSet != FAUDIO_COMMIT_NOW && voice->audio->active)
+	{
+		FAudio_OPERATIONSET_QueueSetFrequencyRatio(
+			voice,
+			Ratio,
+			OperationSet
+		);
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+	FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
+
+	if (voice->flags & FAUDIO_VOICE_NOPITCH)
+	{
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+
+	voice->src.freqRatio = FAudio_clamp(
+		Ratio,
+		FAUDIO_MIN_FREQ_RATIO,
+		voice->src.maxFreqRatio
+	);
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+void FAudioSourceVoice_GetFrequencyRatio(
+	FAudioSourceVoice *voice,
+	float *pRatio
+) {
+	LOG_API_ENTER(voice->audio)
+	FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
+
+	*pRatio = voice->src.freqRatio;
+	LOG_API_EXIT(voice->audio)
+}
+
+uint32_t FAudioSourceVoice_SetSourceSampleRate(
+	FAudioSourceVoice *voice,
+	uint32_t NewSourceSampleRate
+) {
+	uint32_t outSampleRate;
+	uint32_t newDecodeSamples, newResampleSamples;
+
+	LOG_API_ENTER(voice->audio)
+	FAudio_assert(voice->type == FAUDIO_VOICE_SOURCE);
+	FAudio_assert(	NewSourceSampleRate >= FAUDIO_MIN_SAMPLE_RATE &&
+			NewSourceSampleRate <= FAUDIO_MAX_SAMPLE_RATE	);
+
+	FAudio_PlatformLockMutex(voice->src.bufferLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
+	if (	voice->audio->version > 7 &&
+		voice->src.bufferList != NULL	)
+	{
+		FAudio_PlatformUnlockMutex(voice->src.bufferLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
+		LOG_API_EXIT(voice->audio)
+		return FAUDIO_E_INVALID_CALL;
+	}
+	FAudio_PlatformUnlockMutex(voice->src.bufferLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
+
+	voice->src.format->nSamplesPerSec = NewSourceSampleRate;
+
+	/* Resize decode cache */
+	newDecodeSamples = (uint32_t) FAudio_ceil(
+		voice->audio->updateSize *
+		(double) voice->src.maxFreqRatio *
+		(double) NewSourceSampleRate /
+		(double) voice->audio->master->master.inputSampleRate
+	) + EXTRA_DECODE_PADDING * voice->src.format->nChannels;
+	FAudio_INTERNAL_ResizeDecodeCache(
+		voice->audio,
+		(newDecodeSamples + EXTRA_DECODE_PADDING) * voice->src.format->nChannels
+	);
+	voice->src.decodeSamples = newDecodeSamples;
+
+	FAudio_PlatformLockMutex(voice->sendLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+
+	if (voice->sends.SendCount == 0)
+	{
+		FAudio_PlatformUnlockMutex(voice->sendLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+		LOG_API_EXIT(voice->audio)
+		return 0;
+	}
+	outSampleRate = voice->sends.pSends[0].pOutputVoice->type == FAUDIO_VOICE_MASTER ?
+		voice->sends.pSends[0].pOutputVoice->master.inputSampleRate :
+		voice->sends.pSends[0].pOutputVoice->mix.inputSampleRate;
+
+	newResampleSamples = (uint32_t) (FAudio_ceil(
+		(double) voice->audio->updateSize *
+		(double) outSampleRate /
+		(double) voice->audio->master->master.inputSampleRate
+	));
+	voice->src.resampleSamples = newResampleSamples;
+
+	FAudio_PlatformUnlockMutex(voice->sendLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+/* FAudioMasteringVoice Interface */
+
+FAUDIOAPI uint32_t FAudioMasteringVoice_GetChannelMask(
+	FAudioMasteringVoice *voice,
+	uint32_t *pChannelMask
+) {
+	LOG_API_ENTER(voice->audio)
+	FAudio_assert(voice->type == FAUDIO_VOICE_MASTER);
+	FAudio_assert(pChannelMask != NULL);
+
+	*pChannelMask = voice->audio->mixFormat.dwChannelMask;
+	LOG_API_EXIT(voice->audio)
+	return 0;
+}
+
+#ifdef FAUDIO_DUMP_VOICES
+
+static inline FAudioIOStreamOut *DumpVoices_fopen(
+	const FAudioSourceVoice *voice,
+	const FAudioWaveFormatEx *format,
+	const char *mode,
+	const char *ext
+) {
+	char loc[64];
+	uint16_t format_tag = format->wFormatTag;
+	uint16_t format_ex_tag = 0;
+	if (format->wFormatTag == FAUDIO_FORMAT_EXTENSIBLE)
+	{
+		/* get the GUID of the extended subformat */
+		const FAudioWaveFormatExtensible *format_ex =
+				(const FAudioWaveFormatExtensible*) format;
+		format_ex_tag = (uint16_t) (format_ex->SubFormat.Data1);
+	}
+	FAudio_snprintf(
+		loc,
+		sizeof(loc),
+		"FA_fmt_0x%04X_0x%04X_0x%016lX%s.wav",
+		format_tag,
+		format_ex_tag,
+		(uint64_t) voice,
+		ext
+	);
+	FAudioIOStreamOut *fileOut = FAudio_fopen_out(loc, mode);
+	return fileOut;
+}
+
+static inline void DumpVoices_finalize_section(
+	const FAudioSourceVoice *voice,
+	const FAudioWaveFormatEx *format,
+	const char *section /* one of "data" or "dpds" */
+) {
+	/* data file only contains the real data bytes */
+	FAudioIOStreamOut *io_data = DumpVoices_fopen(voice, format, "rb", section);
+	if (!io_data)
+	{
+		return;
+	}
+	FAudio_PlatformLockMutex((FAudioMutex) io_data->lock);
+	size_t file_size_data = io_data->size(io_data->data);
+	if (file_size_data == 0)
+	{
+		/* nothing to do */
+		/* close data file */
+		FAudio_PlatformUnlockMutex((FAudioMutex) io_data->lock);
+		FAudio_close_out(io_data);
+		return;
+	}
+
+	/* we got some data: append data section to main file */
+	FAudioIOStreamOut *io = DumpVoices_fopen(voice, format, "ab", "");
+	if (!io)
+	{
+		/* close data file */
+		FAudio_PlatformUnlockMutex((FAudioMutex) io_data->lock);
+		FAudio_close_out(io_data);
+		return;
+	}
+
+	/* data sub-chunk - 8 bytes + data */
+	/* SubChunk2ID - 4 --> "data" or "dpds" */
+	io->write(io->data, section, 4, 1);
+	/* Subchunk2Size - 4 */
+	uint32_t chunk_size = (uint32_t)file_size_data;
+	io->write(io->data, &chunk_size, 4, 1);
+	/* data */
+	/* fill in data bytes */
+	uint8_t buffer[1024*1024];
+	size_t count;
+	while((count = io_data->read(io_data->data, (void*) buffer, 1, 1024*1024)) > 0)
+	{
+		io->write(io->data, (void*) buffer, 1, count);
+	}
+
+	/* close data file */
+	FAudio_PlatformUnlockMutex((FAudioMutex) io_data->lock);
+	FAudio_close_out(io_data);
+	/* close main file */
+	FAudio_PlatformUnlockMutex((FAudioMutex) io->lock);
+	FAudio_close_out(io);
+}
+
+static void FAudio_DUMPVOICE_Init(const FAudioSourceVoice *voice)
+{
+	const FAudioWaveFormatEx *format = voice->src.format;
+
+	FAudioIOStreamOut *io = DumpVoices_fopen(voice, format, "wb", "");
+	if (!io)
+	{
+		return;
+	}
+	FAudio_PlatformLockMutex((FAudioMutex) io->lock);
+	/* another GREAT ressource
+	 * https://wiki.multimedia.cx/index.php/Microsoft_xWMA
+	 */
+
+
+	/* wave file format taken from
+	 * http://soundfile.sapp.org/doc/WaveFormat
+	 * https://sites.google.com/site/musicgapi/technical-documents/wav-file-format
+	 * |52 49|46 46|52 4A|02 00|
+	 * |c1 sz|af|nc|sp rt|bt rt|
+	 * |ba|bs|da ta|c2 sz|
+
+	 * | R  I  F  F  |chunk size  |W  A  V  E  |f  m  t     |
+	 *                19026
+	 * | 52 49 46 46  52 4A 02 00  57 41 56 45  66 6D 74 20 | RIFFRJ..WAVEfmt
+
+	 * | subchnk size|fmt  |nChan |samplerate  |byte rate   |
+	 * | 50          | 2   |2     |11025       |11289       |
+	 * | 32 00 00 00  02 00 02 00  11 2B 00 00  19 2C 00 00 | 2........+...,..
+
+	 * |blkaln|bps   |efmt |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|
+	 * | 512  |4     |32   |500   |7    |256   |0    |512   |
+	 * | 512  |4     |32   |459252      |256         |
+	 * | 00 02|04 00  20 00 F4 01  07 00 00 01  00 00 00 02 | .... .ô.........
+
+	 * | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
+	 * |
+	 * | 00 FF 00 00  00 00 C0 00  40 00 F0 00  00 00 CC 01 | .ÿ....À.@.ð...Ì.
+
+	 * | XXXXXXXXXXXXXXXXXX|d  a   t  a |chunk size  |XXXXX |
+	 * |                   |            |18944       |      |
+	 * | 30 FF 88 01  18 FF 64 61  74 61 00 4A  02 00 00 00 | 0ÿ...ÿdata.J....
+	 */
+
+	uint16_t cbSize = format->cbSize;
+	const char *formatFourcc = "WAVE";
+	uint16_t wFormatTag = format->wFormatTag;
+	/* special handling for WMAUDIO2 */
+	if (wFormatTag == FAUDIO_FORMAT_EXTENSIBLE && cbSize >= 22)
+	{
+		const FAudioWaveFormatExtensible *format_ex =
+				(const FAudioWaveFormatExtensible*) format;
+		uint16_t format_ex_tag = (uint16_t) (format_ex->SubFormat.Data1);
+		if (format_ex_tag == FAUDIO_FORMAT_WMAUDIO2)
+		{
+			cbSize = 0;
+			formatFourcc = "XWMA";
+			wFormatTag = FAUDIO_FORMAT_WMAUDIO2;
+		}
+	}
+
+	{ /* RIFF chunk descriptor - 12 byte */
+		/* ChunkID - 4 */
+		io->write(io->data, "RIFF", 4, 1);
+		/* ChunkSize - 4 */
+		uint32_t filesize = 0; /* the real file size is written in finalize step */
+		io->write(io->data, &filesize, 4, 1);
+		/* Format - 4 */
+		io->write(io->data, formatFourcc, 4, 1);
+	}
+	{ /* fmt sub-chunk 24 */
+		/* Subchunk1ID - 4 */
+		io->write(io->data, "fmt ", 4, 1);
+		/* Subchunk1Size - 4 */
+		/* 18 byte for WAVEFORMATEX and cbSize for WAVEFORMATEXTENDED */
+		uint32_t chunk_data_size = 18 + (uint32_t) cbSize;
+		io->write(io->data, &chunk_data_size, 4, 1);
+		/* AudioFormat - 2 */
+		io->write(io->data, &wFormatTag, 2, 1);
+		/* NumChannels - 2 */
+		io->write(io->data, &format->nChannels, 2, 1);
+		/* SampleRate - 4 */
+		io->write(io->data, &format->nSamplesPerSec, 4, 1);
+		/* ByteRate - 4 */
+		/* SampleRate * NumChannels * BitsPerSample/8 */
+		io->write(io->data, &format->nAvgBytesPerSec, 4, 1);
+		/* BlockAlign - 2 */
+		/* NumChannels * BitsPerSample/8 */
+		io->write(io->data, &format->nBlockAlign, 2, 1);
+		/* BitsPerSample - 2 */
+		io->write(io->data, &format->wBitsPerSample, 2, 1);
+	}
+	/* in case of extensible audio format write the additional data to the file */
+	{
+		/* always write the cbSize */
+		io->write(io->data, &cbSize, 2, 1);
+
+		if (cbSize >= 22)
+		{
+			/* we have a WAVEFORMATEXTENSIBLE struct to write */
+			const FAudioWaveFormatExtensible *format_ex =
+					(const FAudioWaveFormatExtensible*) format;
+			io->write(io->data, &format_ex->Samples.wValidBitsPerSample, 2, 1);
+			io->write(io->data, &format_ex->dwChannelMask,   4, 1);
+			/* write FAudioGUID */
+			io->write(io->data, &format_ex->SubFormat.Data1, 4, 1);
+			io->write(io->data, &format_ex->SubFormat.Data2, 2, 1);
+			io->write(io->data, &format_ex->SubFormat.Data3, 2, 1);
+			io->write(io->data, &format_ex->SubFormat.Data4, 1, 8);
+		}
+		if (format->cbSize > 22)
+		{
+			/* fill up the remaining cbSize bytes with zeros */
+			uint8_t zero = 0;
+			for (uint16_t i=23; i<=format->cbSize; i++)
+			{
+				io->write(io->data, &zero, 1, 1);
+			}
+		}
+	}
+	{ /* dpds sub-chunk - optional - 8 bytes + bufferWMA uint32_t samples */
+		/* create file to hold the bufferWMA samples */
+		FAudioIOStreamOut *io_dpds = DumpVoices_fopen(voice, format, "wb", "dpds");
+		FAudio_close_out(io_dpds);
+		/* io_dpds file will be filled by SubmitBuffer */
+	}
+	{ /* data sub-chunk - 8 bytes + data */
+		/* create file to hold the data samples */
+		FAudioIOStreamOut *io_data = DumpVoices_fopen(voice, format, "wb", "data");
+		FAudio_close_out(io_data);
+		/* io_data file will be filled by SubmitBuffer */
+	}
+	FAudio_PlatformUnlockMutex((FAudioMutex) io->lock);
+	FAudio_close_out(io);
+}
+
+static void FAudio_DUMPVOICE_Finalize(const FAudioSourceVoice *voice)
+{
+	const FAudioWaveFormatEx *format = voice->src.format;
+
+	/* add dpds subchunk - optional */
+	DumpVoices_finalize_section(voice, format, "dpds");
+	/* add data subchunk */
+	DumpVoices_finalize_section(voice, format, "data");
+
+	/* open main file to update filesize */
+	FAudioIOStreamOut *io = DumpVoices_fopen(voice, format, "r+b", "");
+	if (!io)
+	{
+		return;
+	}
+	FAudio_PlatformLockMutex((FAudioMutex) io->lock);
+	size_t file_size = io->size(io->data);
+	if (file_size >= 44)
+	{
+		/* update filesize */
+		uint32_t chunk_size = (uint32_t)(file_size - 8);
+		io->seek(io->data, 4, FAUDIO_SEEK_SET);
+		io->write(io->data, &chunk_size, 4, 1);
+	}
+	FAudio_PlatformUnlockMutex((FAudioMutex) io->lock);
+	FAudio_close_out(io);
+}
+
+static void FAudio_DUMPVOICE_WriteBuffer(
+	const FAudioSourceVoice *voice,
+	const FAudioBuffer *pBuffer,
+	const FAudioBufferWMA *pBufferWMA,
+	const uint32_t playBegin,
+	const uint32_t playLength
+) {
+	FAudioIOStreamOut *io_data = DumpVoices_fopen(voice, voice->src.format, "ab", "data");
+	if (io_data == NULL)
+	{
+		return;
+	}
+
+	FAudio_PlatformLockMutex((FAudioMutex) io_data->lock);
+	if (pBufferWMA != NULL)
+	{
+		/* dump encoded buffer contents */
+		if (pBufferWMA->PacketCount > 0)
+		{
+			FAudioIOStreamOut *io_dpds = DumpVoices_fopen(voice, voice->src.format, "ab", "dpds");
+			if (io_dpds)
+			{
+				FAudio_PlatformLockMutex((FAudioMutex) io_dpds->lock);
+				/* write to dpds file */
+				io_dpds->write(io_dpds->data, pBufferWMA->pDecodedPacketCumulativeBytes, sizeof(uint32_t), pBufferWMA->PacketCount);
+				FAudio_PlatformUnlockMutex((FAudioMutex) io_dpds->lock);
+				FAudio_close_out(io_dpds);
+			}
+			/* write buffer contents to data file */
+			io_data->write(io_data->data, pBuffer->pAudioData, sizeof(uint8_t), pBuffer->AudioBytes);
+		}
+	}
+	else
+	{
+		/* dump unencoded buffer contents */
+		uint16_t bytesPerFrame = (voice->src.format->nChannels * voice->src.format->wBitsPerSample / 8);
+		FAudio_assert(bytesPerFrame > 0);
+		const void *pAudioDataBegin = pBuffer->pAudioData + playBegin*bytesPerFrame;
+		io_data->write(io_data->data, pAudioDataBegin, bytesPerFrame, playLength);
+	}
+	FAudio_PlatformUnlockMutex((FAudioMutex) io_data->lock);
+	FAudio_close_out(io_data);
+}
+
+#endif /* FAUDIO_DUMP_VOICES */
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FAudioFX_reverb.c b/libs/faudio/src/FAudioFX_reverb.c
new file mode 100644
index 00000000000..4d43fa4e76c
--- /dev/null
+++ b/libs/faudio/src/FAudioFX_reverb.c
@@ -0,0 +1,1969 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "FAudioFX.h"
+#include "FAudio_internal.h"
+
+/* #define DISABLE_SUBNORMALS */
+#ifdef DISABLE_SUBNORMALS
+#include <math.h> /* ONLY USE THIS FOR fpclassify/_fpclass! */
+
+/* VS2010 doesn't define fpclassify (which is C99), so here it is. */
+#if defined(_MSC_VER) && !defined(fpclassify)
+#define IS_SUBNORMAL(a) (_fpclass(a) & (_FPCLASS_ND | _FPCLASS_PD))
+#else
+#define IS_SUBNORMAL(a) (fpclassify(a) == FP_SUBNORMAL)
+#endif
+#endif /* DISABLE_SUBNORMALS */
+
+/* Utility Functions */
+
+static inline float DbGainToFactor(float gain)
+{
+	return (float) FAudio_pow(10, gain / 20.0f);
+}
+
+static inline uint32_t MsToSamples(float msec, int32_t sampleRate)
+{
+	return (uint32_t) ((sampleRate * msec) / 1000.0f);
+}
+
+#ifndef DISABLE_SUBNORMALS
+#define Undenormalize(a) ((a))
+#else /* DISABLE_SUBNORMALS */
+static inline float Undenormalize(float sample_in)
+{
+	if (IS_SUBNORMAL(sample_in))
+	{
+		return 0.0f;
+	}
+	return sample_in;
+}
+#endif /* DISABLE_SUBNORMALS */
+
+/* Component - Delay */
+
+#define DSP_DELAY_MAX_DELAY_MS 300
+
+typedef struct DspDelay
+{
+	int32_t	 sampleRate;
+	uint32_t capacity;	/* In samples */
+	uint32_t delay;		/* In samples */
+	uint32_t read_idx;
+	uint32_t write_idx;
+	float *buffer;
+} DspDelay;
+
+static inline void DspDelay_Initialize(
+	DspDelay *filter,
+	int32_t sampleRate,
+	float delay_ms,
+	FAudioMallocFunc pMalloc
+) {
+	FAudio_assert(delay_ms >= 0 && delay_ms <= DSP_DELAY_MAX_DELAY_MS);
+
+	filter->sampleRate = sampleRate;
+	filter->capacity = MsToSamples(DSP_DELAY_MAX_DELAY_MS, sampleRate);
+	filter->delay = MsToSamples(delay_ms, sampleRate);
+	filter->read_idx = 0;
+	filter->write_idx = filter->delay;
+	filter->buffer = (float*) pMalloc(filter->capacity * sizeof(float));
+	FAudio_zero(filter->buffer, filter->capacity * sizeof(float));
+}
+
+static inline void DspDelay_Change(DspDelay *filter, float delay_ms)
+{
+	FAudio_assert(delay_ms >= 0 && delay_ms <= DSP_DELAY_MAX_DELAY_MS);
+
+	/* Length */
+	filter->delay = MsToSamples(delay_ms, filter->sampleRate);
+	filter->read_idx = (filter->write_idx - filter->delay + filter->capacity) % filter->capacity;
+}
+
+static inline float DspDelay_Read(DspDelay *filter)
+{
+	float delay_out;
+
+	FAudio_assert(filter->read_idx < filter->capacity);
+
+	delay_out = filter->buffer[filter->read_idx];
+	filter->read_idx = (filter->read_idx + 1) % filter->capacity;
+	return delay_out;
+}
+
+static inline void DspDelay_Write(DspDelay *filter, float sample)
+{
+	FAudio_assert(filter->write_idx < filter->capacity);
+
+	filter->buffer[filter->write_idx] = sample;
+	filter->write_idx = (filter->write_idx + 1) % filter->capacity;
+}
+
+static inline float DspDelay_Process(DspDelay *filter, float sample_in)
+{
+	float delay_out = DspDelay_Read(filter);
+	DspDelay_Write(filter, sample_in);
+	return delay_out;
+}
+
+static inline float DspDelay_Tap(DspDelay *filter, uint32_t delay)
+{
+	FAudio_assert(delay <= filter->delay);
+	return filter->buffer[(filter->write_idx - delay + filter->capacity) % filter->capacity];
+}
+
+static inline void DspDelay_Reset(DspDelay *filter)
+{
+	filter->read_idx = 0;
+	filter->write_idx = filter->delay;
+	FAudio_zero(filter->buffer, filter->capacity * sizeof(float));
+}
+
+static inline void DspDelay_Destroy(DspDelay *filter, FAudioFreeFunc pFree)
+{
+	pFree(filter->buffer);
+}
+
+static inline float DspComb_FeedbackFromRT60(DspDelay *delay, float rt60_ms)
+{
+	float exponent = (
+		(-3.0f * delay->delay * 1000.0f) /
+		(delay->sampleRate * rt60_ms)
+	);
+	return (float) FAudio_pow(10.0f, exponent);
+}
+
+/* Component - Bi-Quad Filter */
+
+typedef enum DspBiQuadType
+{
+	DSP_BIQUAD_LOWSHELVING,
+	DSP_BIQUAD_HIGHSHELVING
+} DspBiQuadType;
+
+typedef struct DspBiQuad
+{
+	int32_t sampleRate;
+	float a0, a1, a2;
+	float b1, b2;
+	float c0, d0;
+	float delay0, delay1;
+} DspBiQuad;
+
+static inline void DspBiQuad_Change(
+	DspBiQuad *filter,
+	DspBiQuadType type,
+	float frequency,
+	float q,
+	float gain
+) {
+	const float TWOPI = 6.283185307179586476925286766559005;
+	float theta_c = (TWOPI * frequency) / (float) filter->sampleRate;
+	float mu = DbGainToFactor(gain);
+	float beta = (type == DSP_BIQUAD_LOWSHELVING) ?
+		4.0f / (1 + mu) :
+		(1 + mu) / 4.0f;
+	float delta = beta * (float) FAudio_tan(theta_c * 0.5f);
+	float gamma = (1 - delta) / (1 + delta);
+
+	if (type == DSP_BIQUAD_LOWSHELVING)
+	{
+		filter->a0 = (1 - gamma) * 0.5f;
+		filter->a1 = filter->a0;
+	}
+	else
+	{
+		filter->a0 = (1 + gamma) * 0.5f;
+		filter->a1 = -filter->a0;
+	}
+
+	filter->a2 = 0.0f;
+	filter->b1 = -gamma;
+	filter->b2 = 0.0f;
+	filter->c0 = mu - 1.0f;
+	filter->d0 = 1.0f;
+}
+
+static inline void DspBiQuad_Initialize(
+	DspBiQuad *filter,
+	int32_t sampleRate,
+	DspBiQuadType type,
+	float frequency,	/* Corner frequency */
+	float q,		/* Only used by low/high-pass filters */
+	float gain		/* Only used by low/high-shelving filters */
+) {
+	filter->sampleRate = sampleRate;
+	filter->delay0 = 0.0f;
+	filter->delay1 = 0.0f;
+	DspBiQuad_Change(filter, type, frequency, q, gain);
+}
+
+static inline float DspBiQuad_Process(DspBiQuad *filter, float sample_in)
+{
+	/* Direct Form II Transposed:
+	 * - Less delay registers than Direct Form I
+	 * - More numerically stable than Direct Form II
+	 */
+	float result = (filter->a0 * sample_in) + filter->delay0;
+	filter->delay0 = (filter->a1 * sample_in) - (filter->b1 * result) + filter->delay1;
+	filter->delay1 = (filter->a2 * sample_in) - (filter->b2 * result);
+
+	return Undenormalize(
+		(result * filter->c0) +
+		(sample_in * filter->d0)
+	);
+}
+
+static inline void DspBiQuad_Reset(DspBiQuad *filter)
+{
+	filter->delay0 = 0.0f;
+	filter->delay1 = 0.0f;
+}
+
+static inline void DspBiQuad_Destroy(DspBiQuad *filter)
+{
+}
+
+/* Component - Comb Filter with Integrated Low/High Shelving Filters */
+
+typedef struct DspCombShelving
+{
+	DspDelay comb_delay;
+	float comb_feedback_gain;
+
+	DspBiQuad low_shelving;
+	DspBiQuad high_shelving;
+} DspCombShelving;
+
+static inline void DspCombShelving_Initialize(
+	DspCombShelving *filter,
+	int32_t sampleRate,
+	float delay_ms,
+	float rt60_ms,
+	float low_frequency,
+	float low_gain,
+	float high_frequency,
+	float high_gain,
+	FAudioMallocFunc pMalloc
+) {
+	DspDelay_Initialize(&filter->comb_delay, sampleRate, delay_ms, pMalloc);
+	filter->comb_feedback_gain = DspComb_FeedbackFromRT60(
+		&filter->comb_delay,
+		rt60_ms
+	);
+
+	DspBiQuad_Initialize(
+		&filter->low_shelving,
+		sampleRate,
+		DSP_BIQUAD_LOWSHELVING,
+		low_frequency,
+		0.0f,
+		low_gain
+	);
+	DspBiQuad_Initialize(
+		&filter->high_shelving,
+		sampleRate,
+		DSP_BIQUAD_HIGHSHELVING,
+		high_frequency,
+		0.0f,
+		high_gain
+	);
+}
+
+static inline float DspCombShelving_Process(
+	DspCombShelving *filter,
+	float sample_in
+) {
+	float delay_out, feedback, to_buf;
+
+	delay_out = DspDelay_Read(&filter->comb_delay);
+
+	/* Apply shelving filters */
+	feedback = DspBiQuad_Process(&filter->high_shelving, delay_out);
+	feedback = DspBiQuad_Process(&filter->low_shelving, feedback);
+
+	/* Apply comb filter */
+	to_buf = Undenormalize(sample_in + (filter->comb_feedback_gain * feedback));
+	DspDelay_Write(&filter->comb_delay, to_buf);
+
+	return delay_out;
+}
+
+static inline void DspCombShelving_Reset(DspCombShelving *filter)
+{
+	DspDelay_Reset(&filter->comb_delay);
+	DspBiQuad_Reset(&filter->low_shelving);
+	DspBiQuad_Reset(&filter->high_shelving);
+}
+
+static inline void DspCombShelving_Destroy(
+	DspCombShelving *filter,
+	FAudioFreeFunc pFree
+) {
+	DspDelay_Destroy(&filter->comb_delay, pFree);
+	DspBiQuad_Destroy(&filter->low_shelving);
+	DspBiQuad_Destroy(&filter->high_shelving);
+}
+
+/* Component - Delaying All-Pass Filter */
+
+typedef struct DspAllPass
+{
+	DspDelay delay;
+	float feedback_gain;
+} DspAllPass;
+
+static inline void DspAllPass_Initialize(
+	DspAllPass *filter,
+	int32_t sampleRate,
+	float delay_ms,
+	float gain,
+	FAudioMallocFunc pMalloc
+) {
+	DspDelay_Initialize(&filter->delay, sampleRate, delay_ms, pMalloc);
+	filter->feedback_gain = gain;
+}
+
+static inline void DspAllPass_Change(DspAllPass *filter, float delay_ms, float gain)
+{
+	DspDelay_Change(&filter->delay, delay_ms);
+	filter->feedback_gain = gain;
+}
+
+static inline float DspAllPass_Process(DspAllPass *filter, float sample_in)
+{
+	float delay_out, to_buf;
+
+	delay_out = DspDelay_Read(&filter->delay);
+
+	to_buf = Undenormalize(sample_in + (filter->feedback_gain * delay_out));
+	DspDelay_Write(&filter->delay, to_buf);
+
+	return Undenormalize(delay_out - (filter->feedback_gain * to_buf));
+}
+
+static inline void DspAllPass_Reset(DspAllPass *filter)
+{
+	DspDelay_Reset(&filter->delay);
+}
+
+static inline void DspAllPass_Destroy(DspAllPass *filter, FAudioFreeFunc pFree)
+{
+	DspDelay_Destroy(&filter->delay, pFree);
+}
+
+/*
+Reverb network - loosely based on the reverberator from
+"Designing Audio Effect Plug-Ins in C++" by Will Pirkle and
+the classic classic Schroeder-Moorer reverberator with modifications
+to fit the XAudio2FX parameters.
+
+                                                                         
+    In    +--------+   +----+      +------------+      +-----+           
+  ----|--->PreDelay---->APF1---+--->Sub LeftCh  |----->|     |   Left Out
+      |   +--------+   +----+  |   +------------+      | Wet |-------->  
+      |                        |   +------------+      |     |           
+      |                        |---|Sub RightCh |----->| Dry |           
+      |                            +------------+      |     |  Right Out
+      |                                                | Mix |-------->  
+      +----------------------------------------------->|     |           
+                                                       +-----+           
+ Sub routine per channel :                                                                    
+                                                                         
+     In  +-----+      +-----+ * cg                                       
+   ---+->|Delay|--+---|Comb1|------+                                     
+      |  +-----+  |   +-----+      |                                     
+      |           |                |                                     
+      |           |   +-----+ * cg |                                     
+      |           +--->Comb2|------+                                     
+      |           |   +-----+      |     +-----+                         
+      |           |                +---->| SUM |--------+                
+      |           |   +-----+ * cg |     +-----+        |                
+      |           +--->...  |------+                    |                
+      | * g0      |   +-----+      |                    |                
+      |           |                |                    |                
+      |           +--->-----+ * cg |                    |                
+      |               |Comb8|------+                    |                
+      |               +-----+                           |                
+      v                                                 |                
+   +-----+  g1   +----+   +----+   +----+   +----+      |                
+   | SUM |<------|APF4|<--|APF3|<--|APF2|<--|APF1|<-----+                
+   +-----+       +----+   +----+   +----+   +----+                       
+      |                                                                  
+      |                                                                  
+      |            +-------------+          Out                          
+      +----------->|RoomFilter   |------------------------>              
+                   +-------------+                                       
+                                                                         
+
+Parameters:
+
+float WetDryMix;		0 - 100 (0 = fully dry, 100 = fully wet)
+uint32_t ReflectionsDelay;	0 - 300 ms
+uint8_t ReverbDelay;		0 - 85 ms
+uint8_t RearDelay;		0 - 5 ms
+uint8_t PositionLeft;		0 - 30
+uint8_t PositionRight;		0 - 30
+uint8_t PositionMatrixLeft;	0 - 30
+uint8_t PositionMatrixRight;	0 - 30
+uint8_t EarlyDiffusion;		0 - 15
+uint8_t LateDiffusion;		0 - 15
+uint8_t LowEQGain;		0 - 12 (formula dB = LowEQGain - 8)
+uint8_t LowEQCutoff;		0 - 9  (formula Hz = 50 + (LowEQCutoff * 50))
+uint8_t HighEQGain;		0 - 8  (formula dB = HighEQGain - 8)
+uint8_t HighEQCutoff;		0 - 14 (formula Hz = 1000 + (HighEqCutoff * 500))
+float RoomFilterFreq;		20 - 20000Hz
+float RoomFilterMain;		-100 - 0dB
+float RoomFilterHF;		-100 - 0dB
+float ReflectionsGain;		-100 - 20dB
+float ReverbGain;		-100 - 20dB
+float DecayTime;		0.1 - .... ms
+float Density;			0 - 100 %
+float RoomSize;			1 - 100 feet (NOT USED YET)
+
+*/
+
+#define REVERB_COUNT_COMB	8
+#define REVERB_COUNT_APF_IN	1
+#define REVERB_COUNT_APF_OUT	4
+
+static float COMB_DELAYS[REVERB_COUNT_COMB] =
+{
+	25.31f,
+	26.94f,
+	28.96f,
+	30.75f,
+	32.24f,
+	33.80f,
+	35.31f,
+	36.67f
+};
+
+static float APF_IN_DELAYS[REVERB_COUNT_APF_IN] =
+{
+	13.28f,
+/*	28.13f */
+};
+
+static float APF_OUT_DELAYS[REVERB_COUNT_APF_OUT] =
+{
+	5.10f,
+	12.61f,
+	10.0f,
+	7.73f
+};
+
+typedef enum FAudio_ChannelPositionFlags
+{
+	Position_Left = 0x1,
+	Position_Right = 0x2,
+	Position_Center = 0x4,
+	Position_Rear = 0x8,
+} FAudio_ChannelPositionFlags;
+
+static FAudio_ChannelPositionFlags FAudio_GetChannelPositionFlags(int32_t total_channels, int32_t channel)
+{
+	switch (total_channels)
+	{
+		case 1:
+			return Position_Center;
+
+		case 2:
+			return (channel == 0) ? Position_Left : Position_Right;
+
+		case 4:
+			switch (channel)
+			{
+				case 0:
+					return Position_Left;
+				case 1:
+					return Position_Right;
+				case 2:
+					return Position_Left | Position_Rear;
+				case 3:
+					return Position_Right | Position_Rear;
+			}
+
+		case 5:
+			switch (channel)
+			{
+				case 0:
+					return Position_Left;
+				case 1:
+					return Position_Right;
+				case 2:
+					return Position_Center;
+				case 3:
+					return Position_Left | Position_Rear;
+				case 4:
+					return Position_Right | Position_Rear;
+			}
+
+		default:
+			FAudio_assert(0 && "Unsupported channel count");
+			break;
+	}
+
+	/* shouldn't happen, but default to left speaker */
+	return Position_Left;
+}
+
+float FAudio_GetStereoSpreadDelayMS(int32_t total_channels, int32_t channel)
+{
+	FAudio_ChannelPositionFlags flags = FAudio_GetChannelPositionFlags(total_channels, channel);
+	return (flags & Position_Right) ? 0.5216f : 0.0f;
+}
+
+typedef struct DspReverbChannel
+{
+	DspDelay reverb_delay;
+	DspCombShelving	lpf_comb[REVERB_COUNT_COMB];
+	DspAllPass apf_out[REVERB_COUNT_APF_OUT];
+	DspBiQuad room_high_shelf;
+	float early_gain;
+	float gain;
+} DspReverbChannel;
+
+typedef struct DspReverb
+{
+	DspDelay early_delay;
+	DspAllPass apf_in[REVERB_COUNT_APF_IN];
+	
+	int32_t in_channels;
+	int32_t out_channels;
+	int32_t reverb_channels;
+	DspReverbChannel channel[5];
+
+	float early_gain;
+	float reverb_gain;
+	float room_gain;
+	float wet_ratio;
+	float dry_ratio;
+} DspReverb;
+
+static inline void DspReverb_Create(
+	DspReverb *reverb,
+	int32_t sampleRate,
+	int32_t in_channels,
+	int32_t out_channels,
+	FAudioMallocFunc pMalloc
+) {
+	int32_t i, c;
+
+	FAudio_assert(in_channels == 1 || in_channels == 2 || in_channels == 6);
+	FAudio_assert(out_channels == 1 || out_channels == 2 || out_channels == 6);
+
+	FAudio_zero(reverb, sizeof(DspReverb));
+	DspDelay_Initialize(&reverb->early_delay, sampleRate, 10, pMalloc);
+
+	for (i = 0; i < REVERB_COUNT_APF_IN; i += 1)
+	{
+		DspAllPass_Initialize(
+			&reverb->apf_in[i],
+			sampleRate,
+			APF_IN_DELAYS[i],
+			0.5f,
+			pMalloc
+		);
+	}
+
+	if (out_channels == 6)
+	{
+		reverb->reverb_channels = (in_channels == 6) ? 5 : 4;
+	}
+	else
+	{
+		reverb->reverb_channels = out_channels;
+	}
+
+	for (c = 0; c < reverb->reverb_channels; c += 1)
+	{
+		DspDelay_Initialize(
+			&reverb->channel[c].reverb_delay,
+			sampleRate,
+			10,
+			pMalloc
+		);
+
+		for (i = 0; i < REVERB_COUNT_COMB; i += 1)
+		{
+			DspCombShelving_Initialize(
+				&reverb->channel[c].lpf_comb[i],
+				sampleRate,
+				COMB_DELAYS[i] + FAudio_GetStereoSpreadDelayMS(reverb->reverb_channels, c),
+				500,
+				500,
+				-6,
+				5000,
+				-6,
+				pMalloc
+			);
+		}
+
+		for (i = 0; i < REVERB_COUNT_APF_OUT; i += 1)
+		{
+			DspAllPass_Initialize(
+				&reverb->channel[c].apf_out[i],
+				sampleRate,
+				APF_OUT_DELAYS[i] + FAudio_GetStereoSpreadDelayMS(reverb->reverb_channels, c),
+				0.5f,
+				pMalloc
+			);
+		}
+
+		DspBiQuad_Initialize(
+			&reverb->channel[c].room_high_shelf,
+			sampleRate,
+			DSP_BIQUAD_HIGHSHELVING,
+			5000,
+			0,
+			-10
+		);
+		reverb->channel[c].gain = 1.0f;
+	}
+
+	reverb->early_gain = 1.0f;
+	reverb->reverb_gain = 1.0f;
+	reverb->dry_ratio = 0.0f;
+	reverb->wet_ratio = 1.0f;
+	reverb->in_channels = in_channels;
+	reverb->out_channels = out_channels;
+}
+
+static inline void DspReverb_Destroy(DspReverb *reverb, FAudioFreeFunc pFree)
+{
+	int32_t i, c;
+
+	DspDelay_Destroy(&reverb->early_delay, pFree);
+
+	for (i = 0; i < REVERB_COUNT_APF_IN; i += 1)
+	{
+		DspAllPass_Destroy(&reverb->apf_in[i], pFree);
+	}
+
+	for (c = 0; c < reverb->reverb_channels; c += 1)
+	{
+		DspDelay_Destroy(&reverb->channel[c].reverb_delay, pFree);
+
+		for (i = 0; i < REVERB_COUNT_COMB; i += 1)
+		{
+			DspCombShelving_Destroy(
+				&reverb->channel[c].lpf_comb[i],
+				pFree
+			);
+		}
+
+		DspBiQuad_Destroy(&reverb->channel[c].room_high_shelf);
+
+		for (i = 0; i < REVERB_COUNT_APF_OUT; i += 1)
+		{
+			DspAllPass_Destroy(
+				&reverb->channel[c].apf_out[i],
+				pFree
+			);
+		}
+	}
+}
+
+static inline void DspReverb_SetParameters(
+	DspReverb *reverb,
+	FAudioFXReverbParameters *params
+) {
+	float early_diffusion, late_diffusion;
+	DspCombShelving *comb;
+	int32_t i, c;
+
+	/* Pre-Delay */
+	DspDelay_Change(&reverb->early_delay, (float) params->ReflectionsDelay);
+
+	/* Early Reflections - Diffusion */
+	early_diffusion = 0.6f - ((params->EarlyDiffusion / 15.0f) * 0.2f);
+
+	for (i = 0; i < REVERB_COUNT_APF_IN; i += 1)
+	{
+		DspAllPass_Change(
+			&reverb->apf_in[i],
+			APF_IN_DELAYS[i],
+			early_diffusion
+		);
+	}
+
+	/* Reverberation */
+	for (c = 0; c < reverb->reverb_channels; c += 1)
+	{
+		float channel_delay =
+			(FAudio_GetChannelPositionFlags(reverb->reverb_channels, c) & Position_Rear) ?
+			params->RearDelay :
+			0.0f;
+
+		DspDelay_Change(
+			&reverb->channel[c].reverb_delay,
+			(float) params->ReverbDelay + channel_delay
+		);
+
+		for (i = 0; i < REVERB_COUNT_COMB; i += 1)
+		{
+			comb = &reverb->channel[c].lpf_comb[i];
+
+			/* Set decay time of comb filter */
+			DspDelay_Change(
+				&comb->comb_delay,
+				COMB_DELAYS[i] + FAudio_GetStereoSpreadDelayMS(reverb->reverb_channels, c)
+			);
+			comb->comb_feedback_gain = DspComb_FeedbackFromRT60(
+				&comb->comb_delay,
+				FAudio_max(params->DecayTime, FAUDIOFX_REVERB_MIN_DECAY_TIME) * 1000.0f
+			);
+
+			/* High/Low shelving */
+			DspBiQuad_Change(
+				&comb->low_shelving,
+				DSP_BIQUAD_LOWSHELVING,
+				50.0f + params->LowEQCutoff * 50.0f,
+				0.0f,
+				params->LowEQGain - 8.0f
+			);
+			DspBiQuad_Change(
+				&comb->high_shelving,
+				DSP_BIQUAD_HIGHSHELVING,
+				1000 + params->HighEQCutoff * 500.0f,
+				0.0f,
+				params->HighEQGain - 8.0f
+			);
+		}
+	}
+
+	/* Gain */
+	reverb->early_gain = DbGainToFactor(params->ReflectionsGain);
+	reverb->reverb_gain = DbGainToFactor(params->ReverbGain);
+	reverb->room_gain = DbGainToFactor(params->RoomFilterMain);
+
+	/* Late Diffusion */
+	late_diffusion = 0.6f - ((params->LateDiffusion / 15.0f) * 0.2f);
+
+	for (c = 0; c < reverb->reverb_channels; c += 1)
+	{
+		FAudio_ChannelPositionFlags position = FAudio_GetChannelPositionFlags(reverb->reverb_channels, c);
+		float gain;
+
+		for (i = 0; i < REVERB_COUNT_APF_OUT; i += 1)
+		{
+			DspAllPass_Change(
+				&reverb->channel[c].apf_out[i],
+				APF_OUT_DELAYS[i] + FAudio_GetStereoSpreadDelayMS(reverb->reverb_channels, c),
+				late_diffusion
+			);
+		}
+
+		DspBiQuad_Change(
+			&reverb->channel[c].room_high_shelf,
+			DSP_BIQUAD_HIGHSHELVING,
+			params->RoomFilterFreq,
+			0.0f,
+			params->RoomFilterMain + params->RoomFilterHF
+		);
+
+		if (position & Position_Left)
+		{
+			gain = params->PositionMatrixLeft;
+		}
+		else if (position & Position_Right)
+		{
+			gain = params->PositionMatrixRight;
+		}
+		else /*if (position & Position_Center) */
+		{
+			gain = (params->PositionMatrixLeft + params->PositionMatrixRight) / 2.0f;
+		}
+		reverb->channel[c].gain = 1.5f - (gain / 27.0f) * 0.5f;
+
+		if (position & Position_Rear)
+		{
+			/* Rear-channel Attenuation */
+			reverb->channel[c].gain *= 0.75f;
+		}
+
+		if (position & Position_Left)
+		{
+			gain = params->PositionLeft;
+		}
+		else if (position & Position_Right)
+		{
+			gain = params->PositionRight;
+		}
+		else /*if (position & Position_Center) */
+		{
+			gain = (params->PositionLeft + params->PositionRight) / 2.0f;
+		}
+		reverb->channel[c].early_gain = 1.2f - (gain / 6.0f) * 0.2f;
+		reverb->channel[c].early_gain = (
+			reverb->channel[c].early_gain *
+			reverb->early_gain
+		);
+	}
+
+	/* Wet/Dry Mix (100 = fully wet, 0 = fully dry) */
+	reverb->wet_ratio = params->WetDryMix / 100.0f;
+	reverb->dry_ratio = 1.0f - reverb->wet_ratio;
+}
+
+static inline void DspReverb_SetParameters9(
+	DspReverb *reverb,
+	FAudioFXReverbParameters9 *params
+) {
+	FAudioFXReverbParameters oldParams;
+	oldParams.WetDryMix = params->WetDryMix;
+	oldParams.ReflectionsDelay = params->ReflectionsDelay;
+	oldParams.ReverbDelay = params->ReverbDelay;
+	oldParams.RearDelay = params->RearDelay;
+	oldParams.PositionLeft = params->PositionLeft;
+	oldParams.PositionRight = params->PositionRight;
+	oldParams.PositionMatrixLeft = params->PositionMatrixLeft;
+	oldParams.PositionMatrixRight = params->PositionMatrixRight;
+	oldParams.EarlyDiffusion = params->EarlyDiffusion;
+	oldParams.LateDiffusion = params->LateDiffusion;
+	oldParams.LowEQGain = params->LowEQGain;
+	oldParams.LowEQCutoff = params->LowEQCutoff;
+	oldParams.HighEQGain = params->HighEQGain;
+	oldParams.HighEQCutoff = params->HighEQCutoff;
+	oldParams.RoomFilterFreq = params->RoomFilterFreq;
+	oldParams.RoomFilterMain = params->RoomFilterMain;
+	oldParams.RoomFilterHF = params->RoomFilterHF;
+	oldParams.ReflectionsGain = params->ReflectionsGain;
+	oldParams.ReverbGain = params->ReverbGain;
+	oldParams.DecayTime = params->DecayTime;
+	oldParams.Density = params->Density;
+	oldParams.RoomSize = params->RoomSize;
+	DspReverb_SetParameters(reverb, &oldParams);
+}
+
+static inline float DspReverb_INTERNAL_ProcessEarly(
+	DspReverb *reverb,
+	float sample_in
+) {
+	float early;
+	int32_t i;
+
+	/* Pre-Delay */
+	early = DspDelay_Process(&reverb->early_delay, sample_in);
+
+	/* Early Reflections */
+	for (i = 0; i < REVERB_COUNT_APF_IN; i += 1)
+	{
+		early = DspAllPass_Process(&reverb->apf_in[i], early);
+	}
+
+	return early;
+}
+
+static inline float DspReverb_INTERNAL_ProcessChannel(
+	DspReverb *reverb,
+	DspReverbChannel *channel,
+	float sample_in
+) {
+	float revdelay, early_late, sample_out;
+	int32_t i;
+
+	revdelay = DspDelay_Process(&channel->reverb_delay, sample_in);
+
+	sample_out = 0.0f;
+	for (i = 0; i < REVERB_COUNT_COMB; i += 1)
+	{
+		sample_out += DspCombShelving_Process(
+			&channel->lpf_comb[i],
+			revdelay
+		);
+	}
+	sample_out /= (float) REVERB_COUNT_COMB;
+
+	/* Output Diffusion */
+	for (i = 0; i < REVERB_COUNT_APF_OUT; i += 1)
+	{
+		sample_out = DspAllPass_Process(
+			&channel->apf_out[i],
+			sample_out
+		);
+	}
+
+	/* Combine early reflections and reverberation */
+	early_late = (
+		(sample_in * channel->early_gain) +
+		(sample_out * reverb->reverb_gain)
+	);
+
+	/* Room filter */
+	sample_out = DspBiQuad_Process(
+		&channel->room_high_shelf,
+		early_late * reverb->room_gain
+	);
+
+	/* PositionMatrixLeft/Right */
+	return sample_out * channel->gain;
+}
+
+/* Reverb Process Functions */
+
+static inline float DspReverb_INTERNAL_Process_1_to_1(
+	DspReverb *reverb,
+	float *restrict samples_in,
+	float *restrict samples_out,
+	size_t sample_count
+) {
+	const float *in_end = samples_in + sample_count;
+	float in, early, late, out;
+	float squared_sum = 0.0f;
+
+	while (samples_in < in_end)
+	{
+		/* Input */
+		in = *samples_in++;
+
+		/* Early Reflections */
+		early = DspReverb_INTERNAL_ProcessEarly(reverb, in);
+
+		/* Reverberation */
+		late = DspReverb_INTERNAL_ProcessChannel(
+			reverb,
+			&reverb->channel[0],
+			early
+		);
+
+		/* Wet/Dry Mix */
+		out = (late * reverb->wet_ratio) + (in * reverb->dry_ratio);
+		squared_sum += out * out;
+
+		/* Output */
+		*samples_out++ = out;
+	}
+
+	return squared_sum;
+}
+
+static inline float DspReverb_INTERNAL_Process_1_to_5p1(
+	DspReverb *reverb,
+	float *restrict samples_in,
+	float *restrict samples_out,
+	size_t sample_count
+) {
+	const float *in_end = samples_in + sample_count;
+	float in, in_ratio, early, late[4];
+	float squared_sum = 0.0f;
+	int32_t c;
+
+	while (samples_in < in_end)
+	{
+		/* Input */
+		in = *samples_in++;
+		in_ratio = in * reverb->dry_ratio;
+
+		/* Early Reflections */
+		early = DspReverb_INTERNAL_ProcessEarly(reverb, in);
+
+		/* Reverberation with Wet/Dry Mix */
+		for (c = 0; c < 4; c += 1)
+		{
+			late[c] = (DspReverb_INTERNAL_ProcessChannel(
+				reverb,
+				&reverb->channel[c],
+				early
+			) * reverb->wet_ratio) + in_ratio;
+			squared_sum += late[c] * late[c];
+		}
+
+		/* Output */
+		*samples_out++ = late[0];	/* Front Left */
+		*samples_out++ = late[1];	/* Front Right */
+		*samples_out++ = 0.0f;		/* Center */
+		*samples_out++ = 0.0f;		/* LFE */
+		*samples_out++ = late[2];	/* Rear Left */
+		*samples_out++ = late[3];	/* Rear Right */
+	}
+
+	return squared_sum;
+}
+
+static inline float DspReverb_INTERNAL_Process_2_to_2(
+	DspReverb *reverb,
+	float *restrict samples_in,
+	float *restrict samples_out,
+	size_t sample_count
+) {
+	const float *in_end = samples_in + sample_count;
+	float in, in_ratio, early, late[2];
+	float squared_sum = 0;
+
+	while (samples_in < in_end)
+	{
+		/* Input - Combine 2 channels into 1 */
+		in = (samples_in[0] + samples_in[1]) / 2.0f;
+		in_ratio = in * reverb->dry_ratio;
+		samples_in += 2;
+
+		/* Early Reflections */
+		early = DspReverb_INTERNAL_ProcessEarly(reverb, in);
+
+		/* Reverberation with Wet/Dry Mix */
+		late[0] = DspReverb_INTERNAL_ProcessChannel(
+			reverb,
+			&reverb->channel[0],
+			early
+		);
+		late[1] = (DspReverb_INTERNAL_ProcessChannel(
+			reverb,
+			&reverb->channel[1],
+			early
+		) * reverb->wet_ratio) + in_ratio;
+		squared_sum += (late[0] * late[0]) + (late[1] * late[1]);
+
+		/* Output */
+		*samples_out++ = late[0];
+		*samples_out++ = late[1];
+	}
+
+	return squared_sum;
+}
+
+static inline float DspReverb_INTERNAL_Process_2_to_5p1(
+	DspReverb *reverb,
+	float *restrict samples_in,
+	float *restrict samples_out,
+	size_t sample_count
+) {
+	const float *in_end = samples_in + sample_count;
+	float in, in_ratio, early, late[4];
+	float squared_sum = 0;
+	int32_t c;
+
+	while (samples_in < in_end)
+	{
+		/* Input - Combine 2 channels into 1 */
+		in = (samples_in[0] + samples_in[1]) / 2.0f;
+		in_ratio = in * reverb->dry_ratio;
+		samples_in += 2;
+
+		/* Early Reflections */
+		early = DspReverb_INTERNAL_ProcessEarly(reverb, in);
+
+		/* Reverberation with Wet/Dry Mix */
+		for (c = 0; c < 4; c += 1)
+		{
+			late[c] = (DspReverb_INTERNAL_ProcessChannel(
+				reverb,
+				&reverb->channel[c],
+				early
+			) * reverb->wet_ratio) + in_ratio;
+			squared_sum += late[c] * late[c];
+		}
+
+		/* Output */
+		*samples_out++ = late[0];	/* Front Left */
+		*samples_out++ = late[1];	/* Front Right */
+		*samples_out++ = 0.0f;		/* Center */
+		*samples_out++ = 0.0f;		/* LFE */
+		*samples_out++ = late[2];	/* Rear Left */
+		*samples_out++ = late[3];	/* Rear Right */
+	}
+
+	return squared_sum;
+}
+
+static inline float DspReverb_INTERNAL_Process_5p1_to_5p1(
+	DspReverb *reverb,
+	float *restrict samples_in,
+	float *restrict samples_out,
+	size_t sample_count
+) {
+	const float *in_end = samples_in + sample_count;
+	float in, in_ratio, early, late[5];
+	float squared_sum = 0;
+	int32_t c;
+
+	while (samples_in < in_end)
+	{
+		/* Input - Combine non-LFE channels into 1 */
+		in = (samples_in[0] + samples_in[1] + samples_in[2] +
+				samples_in[4] + samples_in[5]) / 5.0f;
+		in_ratio = in * reverb->dry_ratio;
+
+		/* Early Reflections */
+		early = DspReverb_INTERNAL_ProcessEarly(reverb, in);
+
+		/* Reverberation with Wet/Dry Mix */
+		for (c = 0; c < 5; c += 1)
+		{
+			late[c] = (DspReverb_INTERNAL_ProcessChannel(
+				reverb,
+				&reverb->channel[c],
+				early
+			) * reverb->wet_ratio) + in_ratio;
+			squared_sum += late[c] * late[c];
+		}
+
+		/* Output */
+		*samples_out++ = late[0];	/* Front Left */
+		*samples_out++ = late[1];	/* Front Right */
+		*samples_out++ = late[2];	/* Center */
+		*samples_out++ = samples_in[3];	/* LFE, pass through */
+		*samples_out++ = late[3];	/* Rear Left */
+		*samples_out++ = late[4];	/* Rear Right */
+
+		samples_in += 6;
+	}
+
+	return squared_sum;
+}
+
+#undef OUTPUT_SAMPLE
+
+/* Reverb FAPO Implementation */
+
+const FAudioGUID FAudioFX_CLSID_AudioReverb = /* 2.7 */
+{
+	0x6A93130E,
+	0xCB4E,
+	0x4CE1,
+	{
+		0xA9,
+		0xCF,
+		0xE7,
+		0x58,
+		0x80,
+		0x0B,
+		0xB1,
+		0x79
+	}
+};
+
+static FAPORegistrationProperties ReverbProperties =
+{
+	/* .clsid = */ {0},
+	/*.FriendlyName = */
+	{
+		'R', 'e', 'v', 'e', 'r', 'b', '\0'
+	},
+	/*.CopyrightInfo = */ {
+		'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')',
+		'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0'
+	},
+	/*.MajorVersion = */ 0,
+	/*.MinorVersion = */ 0,
+	/*.Flags = */ (
+		FAPO_FLAG_FRAMERATE_MUST_MATCH |
+		FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH |
+		FAPO_FLAG_BUFFERCOUNT_MUST_MATCH |
+		FAPO_FLAG_INPLACE_SUPPORTED
+	),
+	/*.MinInputBufferCount = */ 1,
+	/*.MaxInputBufferCount = */ 1,
+	/*.MinOutputBufferCount = */ 1,
+	/*.MaxOutputBufferCount = */ 1
+};
+
+typedef struct FAudioFXReverb
+{
+	FAPOBase base;
+
+	uint16_t inChannels;
+	uint16_t outChannels;
+	uint32_t sampleRate;
+	uint16_t inBlockAlign;
+	uint16_t outBlockAlign;
+
+	uint8_t apiVersion;
+	DspReverb reverb;
+} FAudioFXReverb;
+
+static inline int8_t IsFloatFormat(const FAudioWaveFormatEx *format)
+{
+	if (format->wFormatTag == FAUDIO_FORMAT_IEEE_FLOAT)
+	{
+		/* Plain ol' WaveFormatEx */
+		return 1;
+	}
+
+	if (format->wFormatTag == FAUDIO_FORMAT_EXTENSIBLE)
+	{
+		/* WaveFormatExtensible, match GUID */
+		#define MAKE_SUBFORMAT_GUID(guid, fmt) \
+			static FAudioGUID KSDATAFORMAT_SUBTYPE_##guid = \
+			{ \
+				(uint16_t) (fmt), 0x0000, 0x0010, \
+				{ \
+					0x80, 0x00, 0x00, 0xaa, \
+					0x00, 0x38, 0x9b, 0x71 \
+				} \
+			}
+		MAKE_SUBFORMAT_GUID(IEEE_FLOAT, 3);
+		#undef MAKE_SUBFORMAT_GUID
+
+		if (FAudio_memcmp(
+			&((FAudioWaveFormatExtensible*) format)->SubFormat,
+			&KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
+			sizeof(FAudioGUID)
+		) == 0) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+uint32_t FAudioFXReverb_IsInputFormatSupported(
+	FAPOBase *fapo,
+	const FAudioWaveFormatEx *pOutputFormat,
+	const FAudioWaveFormatEx *pRequestedInputFormat,
+	FAudioWaveFormatEx **ppSupportedInputFormat
+) {
+	uint32_t result = 0;
+
+#define SET_SUPPORTED_FIELD(field, value)	\
+	result = 1;	\
+	if (ppSupportedInputFormat && *ppSupportedInputFormat)	\
+	{	\
+		(*ppSupportedInputFormat)->field = (value);	\
+	}
+
+	/* Sample Rate */
+	if (pOutputFormat->nSamplesPerSec != pRequestedInputFormat->nSamplesPerSec)
+	{
+		SET_SUPPORTED_FIELD(nSamplesPerSec, pOutputFormat->nSamplesPerSec);
+	}
+
+	/* Data Type */
+	if (!IsFloatFormat(pRequestedInputFormat))
+	{
+		SET_SUPPORTED_FIELD(wFormatTag, FAUDIO_FORMAT_IEEE_FLOAT);
+	}
+
+	/* Input/Output Channel Count */
+	if (pOutputFormat->nChannels == 1 || pOutputFormat->nChannels == 2)
+	{
+		if (pRequestedInputFormat->nChannels != pOutputFormat->nChannels)
+		{
+			SET_SUPPORTED_FIELD(nChannels, pOutputFormat->nChannels);
+		}
+	}
+	else if (pOutputFormat->nChannels == 6)
+	{
+		if (	pRequestedInputFormat->nChannels != 1 &&
+			pRequestedInputFormat->nChannels != 2 &&
+			pRequestedInputFormat->nChannels != 6	)
+		{
+			SET_SUPPORTED_FIELD(nChannels, 1);
+		}
+	}
+	else
+	{
+		SET_SUPPORTED_FIELD(nChannels, 1);
+	}
+
+#undef SET_SUPPORTED_FIELD
+
+	return result;
+}
+
+
+uint32_t FAudioFXReverb_IsOutputFormatSupported(
+	FAPOBase *fapo,
+	const FAudioWaveFormatEx *pInputFormat,
+	const FAudioWaveFormatEx *pRequestedOutputFormat,
+	FAudioWaveFormatEx **ppSupportedOutputFormat
+) {
+	uint32_t result = 0;
+
+#define SET_SUPPORTED_FIELD(field, value)	\
+	result = 1;	\
+	if (ppSupportedOutputFormat && *ppSupportedOutputFormat)	\
+	{	\
+		(*ppSupportedOutputFormat)->field = (value);	\
+	}
+
+	/* Sample Rate */
+	if (pInputFormat->nSamplesPerSec != pRequestedOutputFormat->nSamplesPerSec)
+	{
+		SET_SUPPORTED_FIELD(nSamplesPerSec, pInputFormat->nSamplesPerSec);
+	}
+
+	/* Data Type */
+	if (!IsFloatFormat(pRequestedOutputFormat))
+	{
+		SET_SUPPORTED_FIELD(wFormatTag, FAUDIO_FORMAT_IEEE_FLOAT);
+	}
+
+	/* Input/Output Channel Count */
+	if (pInputFormat->nChannels == 1 || pInputFormat->nChannels == 2)
+	{
+		if (	pRequestedOutputFormat->nChannels != pInputFormat->nChannels &&
+			pRequestedOutputFormat->nChannels != 6)
+		{
+			SET_SUPPORTED_FIELD(nChannels, pInputFormat->nChannels);
+		}
+	}
+	else if (pInputFormat->nChannels == 6)
+	{
+		if (pRequestedOutputFormat->nChannels != 6)
+		{
+			SET_SUPPORTED_FIELD(nChannels, pInputFormat->nChannels);
+		}
+	}
+	else
+	{
+		SET_SUPPORTED_FIELD(nChannels, 1);
+	}
+
+#undef SET_SUPPORTED_FIELD
+
+	return result;
+}
+
+uint32_t FAudioFXReverb_Initialize(
+	FAudioFXReverb *fapo,
+	const void* pData,
+	uint32_t DataByteSize
+) {
+	#define INITPARAMS(offset) \
+		FAudio_memcpy( \
+			fapo->base.m_pParameterBlocks + DataByteSize * offset, \
+			pData, \
+			DataByteSize \
+		);
+	INITPARAMS(0)
+	INITPARAMS(1)
+	INITPARAMS(2)
+	#undef INITPARAMS
+	return 0;
+}
+
+uint32_t FAudioFXReverb_LockForProcess(
+	FAudioFXReverb *fapo,
+	uint32_t InputLockedParameterCount,
+	const FAPOLockForProcessBufferParameters *pInputLockedParameters,
+	uint32_t OutputLockedParameterCount,
+	const FAPOLockForProcessBufferParameters *pOutputLockedParameters
+) {
+	/* Reverb specific validation */
+	if (!IsFloatFormat(pInputLockedParameters->pFormat))
+	{
+		return FAPO_E_FORMAT_UNSUPPORTED;
+	}
+
+	if (	pInputLockedParameters->pFormat->nSamplesPerSec < FAUDIOFX_REVERB_MIN_FRAMERATE ||
+		pInputLockedParameters->pFormat->nSamplesPerSec > FAUDIOFX_REVERB_MAX_FRAMERATE	)
+	{
+		return FAPO_E_FORMAT_UNSUPPORTED;
+	}
+
+	if (!(	(pInputLockedParameters->pFormat->nChannels == 1 &&
+			(pOutputLockedParameters->pFormat->nChannels == 1 ||
+			 pOutputLockedParameters->pFormat->nChannels == 6)) ||
+		(pInputLockedParameters->pFormat->nChannels == 2 &&
+			(pOutputLockedParameters->pFormat->nChannels == 2 ||
+			 pOutputLockedParameters->pFormat->nChannels == 6)) ||
+		(pInputLockedParameters->pFormat->nChannels == 6 &&
+			pOutputLockedParameters->pFormat->nChannels == 6)))
+	{
+		return FAPO_E_FORMAT_UNSUPPORTED;
+	}
+
+	/* Save the things we care about */
+	fapo->inChannels = pInputLockedParameters->pFormat->nChannels;
+	fapo->outChannels = pOutputLockedParameters->pFormat->nChannels;
+	fapo->sampleRate = pOutputLockedParameters->pFormat->nSamplesPerSec;
+	fapo->inBlockAlign = pInputLockedParameters->pFormat->nBlockAlign;
+	fapo->outBlockAlign = pOutputLockedParameters->pFormat->nBlockAlign;
+
+	/* Create the network */
+	DspReverb_Create(
+		&fapo->reverb,
+		fapo->sampleRate,
+		fapo->inChannels,
+		fapo->outChannels,
+		fapo->base.pMalloc
+	);
+
+	/* Call	parent to do basic validation */
+	return FAPOBase_LockForProcess(
+		&fapo->base,
+		InputLockedParameterCount,
+		pInputLockedParameters,
+		OutputLockedParameterCount,
+		pOutputLockedParameters
+	);
+}
+
+static inline void FAudioFXReverb_CopyBuffer(
+	FAudioFXReverb *fapo,
+	float *restrict buffer_in,
+	float *restrict buffer_out,
+	size_t frames_in
+) {
+	/* In-place processing? */
+	if (buffer_in == buffer_out)
+	{
+		return;
+	}
+	
+	/* equal channel count */
+	if (fapo->inBlockAlign == fapo->outBlockAlign)
+	{
+		FAudio_memcpy(
+			buffer_out,
+			buffer_in,
+			fapo->inBlockAlign * frames_in
+		);
+		return;
+	}
+
+	/* 1 -> 5.1 */
+	if (fapo->inChannels == 1 && fapo->outChannels == 6)
+	{
+		const float *in_end = buffer_in + frames_in;
+		while (buffer_in < in_end)
+		{
+			*buffer_out++ = *buffer_in;
+			*buffer_out++ = *buffer_in++;
+			*buffer_out++ = 0.0f;
+			*buffer_out++ = 0.0f;
+			*buffer_out++ = 0.0f;
+			*buffer_out++ = 0.0f;
+		}
+		return;
+	}
+
+	/* 2 -> 5.1 */
+	if (fapo->inChannels == 2 && fapo->outChannels == 6)
+	{
+		const float *in_end = buffer_in + (frames_in * 2);
+		while (buffer_in < in_end)
+		{
+			*buffer_out++ = *buffer_in++;
+			*buffer_out++ = *buffer_in++;
+			*buffer_out++ = 0.0f;
+			*buffer_out++ = 0.0f;
+			*buffer_out++ = 0.0f;
+			*buffer_out++ = 0.0f;
+		}
+		return;
+	}
+
+	FAudio_assert(0 && "Unsupported channel combination");
+	FAudio_zero(buffer_out, fapo->outBlockAlign * frames_in);
+}
+
+void FAudioFXReverb_Process(
+	FAudioFXReverb *fapo,
+	uint32_t InputProcessParameterCount,
+	const FAPOProcessBufferParameters* pInputProcessParameters,
+	uint32_t OutputProcessParameterCount,
+	FAPOProcessBufferParameters* pOutputProcessParameters,
+	int32_t IsEnabled
+) {
+	FAudioFXReverbParameters *params;
+	uint8_t update_params = FAPOBase_ParametersChanged(&fapo->base);
+	float total;
+	
+	/* Handle disabled filter */
+	if (IsEnabled == 0)
+	{
+		pOutputProcessParameters->BufferFlags = pInputProcessParameters->BufferFlags;
+
+		if (pOutputProcessParameters->BufferFlags != FAPO_BUFFER_SILENT)
+		{
+			FAudioFXReverb_CopyBuffer(
+				fapo,
+				(float*) pInputProcessParameters->pBuffer,
+				(float*) pOutputProcessParameters->pBuffer,
+				pInputProcessParameters->ValidFrameCount
+			);
+		}
+
+		return;
+	}
+	
+	/* XAudio2 passes a 'silent' buffer when no input buffer is available to play the effect tail */
+	if (pInputProcessParameters->BufferFlags == FAPO_BUFFER_SILENT)
+	{
+		/* Make sure input data is usable. FIXME: Is this required? */
+		FAudio_zero(
+			pInputProcessParameters->pBuffer,
+			pInputProcessParameters->ValidFrameCount * fapo->inBlockAlign
+		);
+	}
+
+	params = (FAudioFXReverbParameters*) FAPOBase_BeginProcess(&fapo->base);
+
+	/* Update parameters  */
+	if (update_params)
+	{
+		if (fapo->apiVersion == 9)
+		{
+			DspReverb_SetParameters9(
+				&fapo->reverb,
+				(FAudioFXReverbParameters9*) params
+			);
+		}
+		else
+		{
+			DspReverb_SetParameters(&fapo->reverb, params);
+		}
+	}
+
+	/* Run reverb effect */
+	#define PROCESS(pin, pout) \
+		DspReverb_INTERNAL_Process_##pin##_to_##pout( \
+			&fapo->reverb, \
+			(float*) pInputProcessParameters->pBuffer, \
+			(float*) pOutputProcessParameters->pBuffer, \
+			pInputProcessParameters->ValidFrameCount * fapo->inChannels \
+		)
+	switch (fapo->reverb.out_channels)
+	{
+		case 1:
+			total = PROCESS(1, 1);
+			break;
+		case 2:
+			total = PROCESS(2, 2);
+			break;
+		default: /* 5.1 */
+			if (fapo->reverb.in_channels == 1)
+			{
+				total = PROCESS(1, 5p1);
+			}
+			else if (fapo->reverb.in_channels == 2)
+			{
+				total = PROCESS(2, 5p1);
+			}
+			else /* 5.1 */
+			{
+				total = PROCESS(5p1, 5p1);
+			}
+			break;
+	}
+	#undef PROCESS
+
+	/* Set BufferFlags to silent so PLAY_TAILS knows when to stop */
+	pOutputProcessParameters->BufferFlags = (total < 0.0000001f) ?
+		FAPO_BUFFER_SILENT :
+		FAPO_BUFFER_VALID;
+
+	FAPOBase_EndProcess(&fapo->base);
+}
+
+void FAudioFXReverb_Reset(FAudioFXReverb *fapo)
+{
+	int32_t i, c;
+	FAPOBase_Reset(&fapo->base);
+
+	/* Reset the cached state of the reverb filter */
+	DspDelay_Reset(&fapo->reverb.early_delay);
+
+	for (i = 0; i < REVERB_COUNT_APF_IN; i += 1)
+	{
+		DspAllPass_Reset(&fapo->reverb.apf_in[i]);
+	}
+
+	for (c = 0; c < fapo->reverb.reverb_channels; c += 1)
+	{
+		DspDelay_Reset(&fapo->reverb.channel[c].reverb_delay);
+
+		for (i = 0; i < REVERB_COUNT_COMB; i += 1)
+		{
+			DspCombShelving_Reset(&fapo->reverb.channel[c].lpf_comb[i]);
+		}
+
+		DspBiQuad_Reset(&fapo->reverb.channel[c].room_high_shelf);
+
+		for (i = 0; i < REVERB_COUNT_APF_OUT; i += 1)
+		{
+			DspAllPass_Reset(&fapo->reverb.channel[c].apf_out[i]);
+		}
+	}
+}
+
+void FAudioFXReverb_Free(void* fapo)
+{
+	FAudioFXReverb *reverb = (FAudioFXReverb*) fapo;
+	DspReverb_Destroy(&reverb->reverb, reverb->base.pFree);
+	reverb->base.pFree(reverb->base.m_pParameterBlocks);
+	reverb->base.pFree(fapo);
+}
+
+/* Public API (Version 7) */
+
+uint32_t FAudioCreateReverb(FAPO** ppApo, uint32_t Flags)
+{
+	return FAudioCreateReverbWithCustomAllocatorEXT(
+		ppApo,
+		Flags,
+		FAudio_malloc,
+		FAudio_free,
+		FAudio_realloc
+	);
+}
+
+uint32_t FAudioCreateReverbWithCustomAllocatorEXT(
+	FAPO** ppApo,
+	uint32_t Flags,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc
+) {
+	const FAudioFXReverbParameters fxdefault =
+	{
+		FAUDIOFX_REVERB_DEFAULT_WET_DRY_MIX,
+		FAUDIOFX_REVERB_DEFAULT_REFLECTIONS_DELAY,
+		FAUDIOFX_REVERB_DEFAULT_REVERB_DELAY,
+		FAUDIOFX_REVERB_DEFAULT_REAR_DELAY,
+		FAUDIOFX_REVERB_DEFAULT_POSITION,
+		FAUDIOFX_REVERB_DEFAULT_POSITION,
+		FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX,
+		FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX,
+		FAUDIOFX_REVERB_DEFAULT_EARLY_DIFFUSION,
+		FAUDIOFX_REVERB_DEFAULT_LATE_DIFFUSION,
+		FAUDIOFX_REVERB_DEFAULT_LOW_EQ_GAIN,
+		FAUDIOFX_REVERB_DEFAULT_LOW_EQ_CUTOFF,
+		FAUDIOFX_REVERB_DEFAULT_HIGH_EQ_GAIN,
+		FAUDIOFX_REVERB_DEFAULT_HIGH_EQ_CUTOFF,
+		FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_FREQ,
+		FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_MAIN,
+		FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_HF,
+		FAUDIOFX_REVERB_DEFAULT_REFLECTIONS_GAIN,
+		FAUDIOFX_REVERB_DEFAULT_REVERB_GAIN,
+		FAUDIOFX_REVERB_DEFAULT_DECAY_TIME,
+		FAUDIOFX_REVERB_DEFAULT_DENSITY,
+		FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE
+	};
+
+	/* Allocate... */
+	FAudioFXReverb *result = (FAudioFXReverb*) customMalloc(sizeof(FAudioFXReverb));
+	uint8_t *params = (uint8_t*) customMalloc(
+		sizeof(FAudioFXReverbParameters) * 3
+	);
+	result->apiVersion = 7;
+	#define INITPARAMS(offset) \
+		FAudio_memcpy( \
+			params + sizeof(FAudioFXReverbParameters) * offset, \
+			&fxdefault, \
+			sizeof(FAudioFXReverbParameters) \
+		);
+	INITPARAMS(0)
+	INITPARAMS(1)
+	INITPARAMS(2)
+	#undef INITPARAMS
+
+	/* Initialize... */
+	FAudio_memcpy(
+		&ReverbProperties.clsid,
+		&FAudioFX_CLSID_AudioReverb,
+		sizeof(FAudioGUID)
+	);
+	CreateFAPOBaseWithCustomAllocatorEXT(
+		&result->base,
+		&ReverbProperties,
+		params,
+		sizeof(FAudioFXReverbParameters),
+		0,
+		customMalloc,
+		customFree,
+		customRealloc
+	);
+
+	result->inChannels = 0;
+	result->outChannels = 0;
+	result->sampleRate = 0;
+	FAudio_zero(&result->reverb, sizeof(DspReverb));
+
+	/* Function table... */
+	#define ASSIGN_VT(name) \
+		result->base.base.name = (name##Func) FAudioFXReverb_##name;
+	ASSIGN_VT(LockForProcess);
+	ASSIGN_VT(IsInputFormatSupported);
+	ASSIGN_VT(IsOutputFormatSupported);
+	ASSIGN_VT(Initialize);
+	ASSIGN_VT(Reset);
+	ASSIGN_VT(Process);
+	result->base.Destructor = FAudioFXReverb_Free;
+	#undef ASSIGN_VT
+
+	/* Finally. */
+	*ppApo = &result->base.base;
+	return 0;
+}
+
+void ReverbConvertI3DL2ToNative(
+	const FAudioFXReverbI3DL2Parameters *pI3DL2,
+	FAudioFXReverbParameters *pNative
+) {
+	float reflectionsDelay;
+	float reverbDelay;
+
+	pNative->RearDelay = FAUDIOFX_REVERB_DEFAULT_REAR_DELAY;
+	pNative->PositionLeft = FAUDIOFX_REVERB_DEFAULT_POSITION;
+	pNative->PositionRight = FAUDIOFX_REVERB_DEFAULT_POSITION;
+	pNative->PositionMatrixLeft = FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
+	pNative->PositionMatrixRight = FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
+	pNative->RoomSize = FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE;
+	pNative->LowEQCutoff = 4;
+	pNative->HighEQCutoff = 6;
+
+	pNative->RoomFilterMain = (float) pI3DL2->Room / 100.0f;
+	pNative->RoomFilterHF = (float) pI3DL2->RoomHF / 100.0f;
+
+	if (pI3DL2->DecayHFRatio >= 1.0f)
+	{
+		int32_t index = (int32_t) (-4.0 * FAudio_log10(pI3DL2->DecayHFRatio));
+		if (index < -8)
+		{
+			index = -8;
+		}
+		pNative->LowEQGain = (uint8_t) ((index < 0) ? index + 8 : 8);
+		pNative->HighEQGain = 8;
+		pNative->DecayTime = pI3DL2->DecayTime * pI3DL2->DecayHFRatio;
+	}
+	else
+	{
+		int32_t index = (int32_t) (4.0 * FAudio_log10(pI3DL2->DecayHFRatio));
+		if (index < -8)
+		{
+			index = -8;
+		}
+		pNative->LowEQGain = 8;
+		pNative->HighEQGain = (uint8_t) ((index < 0) ? index + 8 : 8);
+		pNative->DecayTime = pI3DL2->DecayTime;
+	}
+
+	reflectionsDelay = pI3DL2->ReflectionsDelay * 1000.0f;
+	if (reflectionsDelay >= FAUDIOFX_REVERB_MAX_REFLECTIONS_DELAY)
+	{
+		reflectionsDelay = (float) (FAUDIOFX_REVERB_MAX_REFLECTIONS_DELAY - 1);
+	}
+	else if (reflectionsDelay <= 1)
+	{
+		reflectionsDelay = 1;
+	}
+	pNative->ReflectionsDelay = (uint32_t) reflectionsDelay;
+
+	reverbDelay = pI3DL2->ReverbDelay * 1000.0f;
+	if (reverbDelay >= FAUDIOFX_REVERB_MAX_REVERB_DELAY)
+	{
+		reverbDelay = (float) (FAUDIOFX_REVERB_MAX_REVERB_DELAY - 1);
+	}
+	pNative->ReverbDelay = (uint8_t) reverbDelay;
+
+	pNative->ReflectionsGain = pI3DL2->Reflections / 100.0f;
+	pNative->ReverbGain = pI3DL2->Reverb / 100.0f;
+	pNative->EarlyDiffusion = (uint8_t) (15.0f * pI3DL2->Diffusion / 100.0f);
+	pNative->LateDiffusion = pNative->EarlyDiffusion;
+	pNative->Density = pI3DL2->Density;
+	pNative->RoomFilterFreq = pI3DL2->HFReference;
+
+	pNative->WetDryMix = pI3DL2->WetDryMix;
+}
+
+/* Public API (Version 9) */
+
+uint32_t FAudioCreateReverb9(FAPO** ppApo, uint32_t Flags)
+{
+	return FAudioCreateReverb9WithCustomAllocatorEXT(
+		ppApo,
+		Flags,
+		FAudio_malloc,
+		FAudio_free,
+		FAudio_realloc
+	);
+}
+
+uint32_t FAudioCreateReverb9WithCustomAllocatorEXT(
+	FAPO** ppApo,
+	uint32_t Flags,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc
+) {
+	const FAudioFXReverbParameters9 fxdefault =
+	{
+		FAUDIOFX_REVERB_DEFAULT_WET_DRY_MIX,
+		FAUDIOFX_REVERB_DEFAULT_REFLECTIONS_DELAY,
+		FAUDIOFX_REVERB_DEFAULT_REVERB_DELAY,
+		FAUDIOFX_REVERB_DEFAULT_REAR_DELAY, /* FIXME: 7POINT1? */
+		FAUDIOFX_REVERB_DEFAULT_7POINT1_SIDE_DELAY,
+		FAUDIOFX_REVERB_DEFAULT_POSITION,
+		FAUDIOFX_REVERB_DEFAULT_POSITION,
+		FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX,
+		FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX,
+		FAUDIOFX_REVERB_DEFAULT_EARLY_DIFFUSION,
+		FAUDIOFX_REVERB_DEFAULT_LATE_DIFFUSION,
+		FAUDIOFX_REVERB_DEFAULT_LOW_EQ_GAIN,
+		FAUDIOFX_REVERB_DEFAULT_LOW_EQ_CUTOFF,
+		FAUDIOFX_REVERB_DEFAULT_HIGH_EQ_GAIN,
+		FAUDIOFX_REVERB_DEFAULT_HIGH_EQ_CUTOFF,
+		FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_FREQ,
+		FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_MAIN,
+		FAUDIOFX_REVERB_DEFAULT_ROOM_FILTER_HF,
+		FAUDIOFX_REVERB_DEFAULT_REFLECTIONS_GAIN,
+		FAUDIOFX_REVERB_DEFAULT_REVERB_GAIN,
+		FAUDIOFX_REVERB_DEFAULT_DECAY_TIME,
+		FAUDIOFX_REVERB_DEFAULT_DENSITY,
+		FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE
+	};
+
+	/* Allocate... */
+	FAudioFXReverb *result = (FAudioFXReverb*) customMalloc(sizeof(FAudioFXReverb));
+	uint8_t *params = (uint8_t*) customMalloc(
+		sizeof(FAudioFXReverbParameters9) * 3
+	);
+	result->apiVersion = 9;
+	#define INITPARAMS(offset) \
+		FAudio_memcpy( \
+			params + sizeof(FAudioFXReverbParameters9) * offset, \
+			&fxdefault, \
+			sizeof(FAudioFXReverbParameters9) \
+		);
+	INITPARAMS(0)
+	INITPARAMS(1)
+	INITPARAMS(2)
+	#undef INITPARAMS
+
+	/* Initialize... */
+	FAudio_memcpy(
+		&ReverbProperties.clsid,
+		&FAudioFX_CLSID_AudioReverb,
+		sizeof(FAudioGUID)
+	);
+	CreateFAPOBaseWithCustomAllocatorEXT(
+		&result->base,
+		&ReverbProperties,
+		params,
+		sizeof(FAudioFXReverbParameters9),
+		0,
+		customMalloc,
+		customFree,
+		customRealloc
+	);
+
+	result->inChannels = 0;
+	result->outChannels = 0;
+	result->sampleRate = 0;
+	FAudio_zero(&result->reverb, sizeof(DspReverb));
+
+	/* Function table... */
+	#define ASSIGN_VT(name) \
+		result->base.base.name = (name##Func) FAudioFXReverb_##name;
+	ASSIGN_VT(LockForProcess);
+	ASSIGN_VT(IsInputFormatSupported);
+	ASSIGN_VT(IsOutputFormatSupported);
+	ASSIGN_VT(Initialize);
+	ASSIGN_VT(Reset);
+	ASSIGN_VT(Process);
+	result->base.Destructor = FAudioFXReverb_Free;
+	#undef ASSIGN_VT
+
+	/* Finally. */
+	*ppApo = &result->base.base;
+	return 0;
+}
+
+void ReverbConvertI3DL2ToNative9(
+	const FAudioFXReverbI3DL2Parameters *pI3DL2,
+	FAudioFXReverbParameters9 *pNative,
+	int32_t sevenDotOneReverb
+) {
+	float reflectionsDelay;
+	float reverbDelay;
+
+	if (sevenDotOneReverb)
+	{
+		pNative->RearDelay = FAUDIOFX_REVERB_DEFAULT_7POINT1_REAR_DELAY;
+	}
+	else
+	{
+		pNative->RearDelay = FAUDIOFX_REVERB_DEFAULT_REAR_DELAY;
+	}
+	pNative->SideDelay = FAUDIOFX_REVERB_DEFAULT_7POINT1_SIDE_DELAY;
+	pNative->PositionLeft = FAUDIOFX_REVERB_DEFAULT_POSITION;
+	pNative->PositionRight = FAUDIOFX_REVERB_DEFAULT_POSITION;
+	pNative->PositionMatrixLeft = FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
+	pNative->PositionMatrixRight = FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
+	pNative->RoomSize = FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE;
+	pNative->LowEQCutoff = 4;
+	pNative->HighEQCutoff = 6;
+
+	pNative->RoomFilterMain = (float) pI3DL2->Room / 100.0f;
+	pNative->RoomFilterHF = (float) pI3DL2->RoomHF / 100.0f;
+
+	if (pI3DL2->DecayHFRatio >= 1.0f)
+	{
+		int32_t index = (int32_t) (-4.0 * FAudio_log10(pI3DL2->DecayHFRatio));
+		if (index < -8)
+		{
+			index = -8;
+		}
+		pNative->LowEQGain = (uint8_t) ((index < 0) ? index + 8 : 8);
+		pNative->HighEQGain = 8;
+		pNative->DecayTime = pI3DL2->DecayTime * pI3DL2->DecayHFRatio;
+	}
+	else
+	{
+		int32_t index = (int32_t) (4.0 * FAudio_log10(pI3DL2->DecayHFRatio));
+		if (index < -8)
+		{
+			index = -8;
+		}
+		pNative->LowEQGain = 8;
+		pNative->HighEQGain = (uint8_t) ((index < 0) ? index + 8 : 8);
+		pNative->DecayTime = pI3DL2->DecayTime;
+	}
+
+	reflectionsDelay = pI3DL2->ReflectionsDelay * 1000.0f;
+	if (reflectionsDelay >= FAUDIOFX_REVERB_MAX_REFLECTIONS_DELAY)
+	{
+		reflectionsDelay = (float) (FAUDIOFX_REVERB_MAX_REFLECTIONS_DELAY - 1);
+	}
+	else if (reflectionsDelay <= 1)
+	{
+		reflectionsDelay = 1;
+	}
+	pNative->ReflectionsDelay = (uint32_t) reflectionsDelay;
+
+	reverbDelay = pI3DL2->ReverbDelay * 1000.0f;
+	if (reverbDelay >= FAUDIOFX_REVERB_MAX_REVERB_DELAY)
+	{
+		reverbDelay = (float) (FAUDIOFX_REVERB_MAX_REVERB_DELAY - 1);
+	}
+	pNative->ReverbDelay = (uint8_t) reverbDelay;
+
+	pNative->ReflectionsGain = pI3DL2->Reflections / 100.0f;
+	pNative->ReverbGain = pI3DL2->Reverb / 100.0f;
+	pNative->EarlyDiffusion = (uint8_t) (15.0f * pI3DL2->Diffusion / 100.0f);
+	pNative->LateDiffusion = pNative->EarlyDiffusion;
+	pNative->Density = pI3DL2->Density;
+	pNative->RoomFilterFreq = pI3DL2->HFReference;
+
+	pNative->WetDryMix = pI3DL2->WetDryMix;
+}
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FAudioFX_volumemeter.c b/libs/faudio/src/FAudioFX_volumemeter.c
new file mode 100644
index 00000000000..ded77b9908e
--- /dev/null
+++ b/libs/faudio/src/FAudioFX_volumemeter.c
@@ -0,0 +1,281 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "FAudioFX.h"
+#include "FAudio_internal.h"
+
+/* Volume Meter FAPO Implementation */
+
+const FAudioGUID FAudioFX_CLSID_AudioVolumeMeter = /* 2.7 */
+{
+	0xCAC1105F,
+	0x619B,
+	0x4D04,
+	{
+		0x83,
+		0x1A,
+		0x44,
+		0xE1,
+		0xCB,
+		0xF1,
+		0x2D,
+		0x57
+	}
+};
+
+static FAPORegistrationProperties VolumeMeterProperties =
+{
+	/* .clsid = */ {0},
+	/* .FriendlyName = */
+	{
+		'V', 'o', 'l', 'u', 'm', 'e', 'M', 'e', 't', 'e', 'r', '\0'
+	},
+	/*.CopyrightInfo = */
+	{
+		'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', '(', 'c', ')',
+		'E', 't', 'h', 'a', 'n', ' ', 'L', 'e', 'e', '\0'
+	},
+	/*.MajorVersion = */ 0,
+	/*.MinorVersion = */ 0,
+	/*.Flags = */(
+		FAPO_FLAG_CHANNELS_MUST_MATCH |
+		FAPO_FLAG_FRAMERATE_MUST_MATCH |
+		FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH |
+		FAPO_FLAG_BUFFERCOUNT_MUST_MATCH |
+		FAPO_FLAG_INPLACE_SUPPORTED |
+		FAPO_FLAG_INPLACE_REQUIRED
+	),
+	/*.MinInputBufferCount = */ 1,
+	/*.MaxInputBufferCount = */  1,
+	/*.MinOutputBufferCount = */ 1,
+	/*.MaxOutputBufferCount =*/ 1
+};
+
+typedef struct FAudioFXVolumeMeter
+{
+	FAPOBase base;
+	uint16_t channels;
+} FAudioFXVolumeMeter;
+
+uint32_t FAudioFXVolumeMeter_LockForProcess(
+	FAudioFXVolumeMeter *fapo,
+	uint32_t InputLockedParameterCount,
+	const FAPOLockForProcessBufferParameters *pInputLockedParameters,
+	uint32_t OutputLockedParameterCount,
+	const FAPOLockForProcessBufferParameters *pOutputLockedParameters
+) {
+	FAudioFXVolumeMeterLevels *levels = (FAudioFXVolumeMeterLevels*)
+		fapo->base.m_pParameterBlocks;
+
+	/* Verify parameter counts... */
+	if (	InputLockedParameterCount < fapo->base.m_pRegistrationProperties->MinInputBufferCount ||
+		InputLockedParameterCount > fapo->base.m_pRegistrationProperties->MaxInputBufferCount ||
+		OutputLockedParameterCount < fapo->base.m_pRegistrationProperties->MinOutputBufferCount ||
+		OutputLockedParameterCount > fapo->base.m_pRegistrationProperties->MaxOutputBufferCount	)
+	{
+		return FAUDIO_E_INVALID_ARG;
+	}
+
+
+	/* Validate input/output formats */
+	#define VERIFY_FORMAT_FLAG(flag, prop) \
+		if (	(fapo->base.m_pRegistrationProperties->Flags & flag) && \
+			(pInputLockedParameters->pFormat->prop != pOutputLockedParameters->pFormat->prop)	) \
+		{ \
+			return FAUDIO_E_INVALID_ARG; \
+		}
+	VERIFY_FORMAT_FLAG(FAPO_FLAG_CHANNELS_MUST_MATCH, nChannels)
+	VERIFY_FORMAT_FLAG(FAPO_FLAG_FRAMERATE_MUST_MATCH, nSamplesPerSec)
+	VERIFY_FORMAT_FLAG(FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH, wBitsPerSample)
+	#undef VERIFY_FORMAT_FLAG
+	if (	(fapo->base.m_pRegistrationProperties->Flags & FAPO_FLAG_BUFFERCOUNT_MUST_MATCH) &&
+		(InputLockedParameterCount != OutputLockedParameterCount)	)
+	{
+		return FAUDIO_E_INVALID_ARG;
+	}
+
+	/* Allocate volume meter arrays */
+	fapo->channels = pInputLockedParameters->pFormat->nChannels;
+	levels[0].pPeakLevels = (float*) fapo->base.pMalloc(
+		fapo->channels * sizeof(float) * 6
+	);
+	FAudio_zero(levels[0].pPeakLevels, fapo->channels * sizeof(float) * 6);
+	levels[0].pRMSLevels = levels[0].pPeakLevels + fapo->channels;
+	levels[1].pPeakLevels = levels[0].pPeakLevels + (fapo->channels * 2);
+	levels[1].pRMSLevels = levels[0].pPeakLevels + (fapo->channels * 3);
+	levels[2].pPeakLevels = levels[0].pPeakLevels + (fapo->channels * 4);
+	levels[2].pRMSLevels = levels[0].pPeakLevels + (fapo->channels * 5);
+
+	fapo->base.m_fIsLocked = 1;
+	return 0;
+}
+
+void FAudioFXVolumeMeter_UnlockForProcess(FAudioFXVolumeMeter *fapo)
+{
+	FAudioFXVolumeMeterLevels *levels = (FAudioFXVolumeMeterLevels*)
+		fapo->base.m_pParameterBlocks;
+	fapo->base.pFree(levels[0].pPeakLevels);
+	fapo->base.m_fIsLocked = 0;
+}
+
+void FAudioFXVolumeMeter_Process(
+	FAudioFXVolumeMeter *fapo,
+	uint32_t InputProcessParameterCount,
+	const FAPOProcessBufferParameters* pInputProcessParameters,
+	uint32_t OutputProcessParameterCount,
+	FAPOProcessBufferParameters* pOutputProcessParameters,
+	int32_t IsEnabled
+) {
+	float peak;
+	float total;
+	float *buffer;
+	uint32_t i, j;
+	FAudioFXVolumeMeterLevels *levels = (FAudioFXVolumeMeterLevels*)
+		FAPOBase_BeginProcess(&fapo->base);
+
+	/* TODO: This could probably be SIMD-ified... */
+	for (i = 0; i < fapo->channels; i += 1)
+	{
+		peak = 0.0f;
+		total = 0.0f;
+		buffer = ((float*) pInputProcessParameters->pBuffer) + i;
+		for (j = 0; j < pInputProcessParameters->ValidFrameCount; j += 1, buffer += fapo->channels)
+		{
+			const float sampleAbs = FAudio_fabsf(*buffer);
+			if (sampleAbs > peak)
+			{
+				peak = sampleAbs;
+			}
+			total += (*buffer) * (*buffer);
+		}
+		levels->pPeakLevels[i] = peak;
+		levels->pRMSLevels[i] = FAudio_sqrtf(
+			total / pInputProcessParameters->ValidFrameCount
+		);
+	}
+
+	FAPOBase_EndProcess(&fapo->base);
+}
+
+void FAudioFXVolumeMeter_GetParameters(
+	FAudioFXVolumeMeter *fapo,
+	FAudioFXVolumeMeterLevels *pParameters,
+	uint32_t ParameterByteSize
+) {
+	FAudioFXVolumeMeterLevels *levels = (FAudioFXVolumeMeterLevels*)
+		fapo->base.m_pCurrentParameters;
+	FAudio_assert(ParameterByteSize == sizeof(FAudioFXVolumeMeterLevels));
+	FAudio_assert(pParameters->ChannelCount == fapo->channels);
+
+	/* Copy what's current as of the last Process */
+	if (pParameters->pPeakLevels != NULL)
+	{
+		FAudio_memcpy(
+			pParameters->pPeakLevels,
+			levels->pPeakLevels,
+			fapo->channels * sizeof(float)
+		);
+	}
+	if (pParameters->pRMSLevels != NULL)
+	{
+		FAudio_memcpy(
+			pParameters->pRMSLevels,
+			levels->pRMSLevels,
+			fapo->channels * sizeof(float)
+		);
+	}
+}
+
+void FAudioFXVolumeMeter_Free(void* fapo)
+{
+	FAudioFXVolumeMeter *volumemeter = (FAudioFXVolumeMeter*) fapo;
+	volumemeter->base.pFree(volumemeter->base.m_pParameterBlocks);
+	volumemeter->base.pFree(fapo);
+}
+
+/* Public API */
+
+uint32_t FAudioCreateVolumeMeter(FAPO** ppApo, uint32_t Flags)
+{
+	return FAudioCreateVolumeMeterWithCustomAllocatorEXT(
+		ppApo,
+		Flags,
+		FAudio_malloc,
+		FAudio_free,
+		FAudio_realloc
+	);
+}
+
+uint32_t FAudioCreateVolumeMeterWithCustomAllocatorEXT(
+	FAPO** ppApo,
+	uint32_t Flags,
+	FAudioMallocFunc customMalloc,
+	FAudioFreeFunc customFree,
+	FAudioReallocFunc customRealloc
+) {
+	/* Allocate... */
+	FAudioFXVolumeMeter *result = (FAudioFXVolumeMeter*) customMalloc(
+		sizeof(FAudioFXVolumeMeter)
+	);
+	uint8_t *params = (uint8_t*) customMalloc(
+		sizeof(FAudioFXVolumeMeterLevels) * 3
+	);
+	FAudio_zero(params, sizeof(FAudioFXVolumeMeterLevels) * 3);
+
+	/* Initialize... */
+	FAudio_memcpy(
+		&VolumeMeterProperties.clsid,
+		&FAudioFX_CLSID_AudioVolumeMeter,
+		sizeof(FAudioGUID)
+	);
+	CreateFAPOBaseWithCustomAllocatorEXT(
+		&result->base,
+		&VolumeMeterProperties,
+		params,
+		sizeof(FAudioFXVolumeMeterLevels),
+		1,
+		customMalloc,
+		customFree,
+		customRealloc
+	);
+
+	/* Function table... */
+	result->base.base.LockForProcess = (LockForProcessFunc)
+		FAudioFXVolumeMeter_LockForProcess;
+	result->base.base.UnlockForProcess = (UnlockForProcessFunc)
+		FAudioFXVolumeMeter_UnlockForProcess;
+	result->base.base.Process = (ProcessFunc)
+		FAudioFXVolumeMeter_Process;
+	result->base.base.GetParameters = (GetParametersFunc)
+		FAudioFXVolumeMeter_GetParameters;
+	result->base.Destructor = FAudioFXVolumeMeter_Free;
+
+	/* Finally. */
+	*ppApo = &result->base.base;
+	return 0;
+}
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FAudio_internal.c b/libs/faudio/src/FAudio_internal.c
new file mode 100644
index 00000000000..6ab0ce98bc2
--- /dev/null
+++ b/libs/faudio/src/FAudio_internal.c
@@ -0,0 +1,1967 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "FAudio_internal.h"
+
+#ifndef FAUDIO_DISABLE_DEBUGCONFIGURATION
+void WINAPIV FAudio_INTERNAL_debug(
+	FAudio *audio,
+	const char *file,
+	uint32_t line,
+	const char *func,
+	const char *fmt,
+	...
+) {
+	char output[1024];
+	char *out = output;
+	va_list va;
+	out[0] = '\0';
+
+	/* Logging extras */
+	if (audio->debug.LogThreadID)
+	{
+		out += FAudio_snprintf(
+			out,
+			sizeof(output) - (out - output),
+			"0x%" FAudio_PRIx64 " ",
+			FAudio_PlatformGetThreadID()
+		);
+	}
+	if (audio->debug.LogFileline)
+	{
+		out += FAudio_snprintf(
+			out,
+			sizeof(output) - (out - output),
+			"%s:%u ",
+			file,
+			line
+		);
+	}
+	if (audio->debug.LogFunctionName)
+	{
+		out += FAudio_snprintf(
+			out,
+			sizeof(output) - (out - output),
+			"%s ",
+			func
+		);
+	}
+	if (audio->debug.LogTiming)
+	{
+		out += FAudio_snprintf(
+			out,
+			sizeof(output) - (out - output),
+			"%dms ",
+			FAudio_timems()
+		);
+	}
+
+	/* The actual message... */
+	va_start(va, fmt);
+	FAudio_vsnprintf(
+		out,
+		sizeof(output) - (out - output),
+		fmt,
+		va
+	);
+	va_end(va);
+
+	/* Print, finally. */
+	FAudio_Log(output);
+}
+
+static const char *get_wformattag_string(const FAudioWaveFormatEx *fmt)
+{
+#define FMT_STRING(suffix) \
+	if (fmt->wFormatTag == FAUDIO_FORMAT_##suffix) \
+	{ \
+		return #suffix; \
+	}
+	FMT_STRING(PCM)
+	FMT_STRING(MSADPCM)
+	FMT_STRING(IEEE_FLOAT)
+	FMT_STRING(XMAUDIO2)
+	FMT_STRING(WMAUDIO2)
+	FMT_STRING(EXTENSIBLE)
+#undef FMT_STRING
+	return "UNKNOWN!";
+}
+
+static const char *get_subformat_string(const FAudioWaveFormatEx *fmt)
+{
+	const FAudioWaveFormatExtensible *fmtex = (const FAudioWaveFormatExtensible*) fmt;
+
+	if (fmt->wFormatTag != FAUDIO_FORMAT_EXTENSIBLE)
+	{
+		return "N/A";
+	}
+	if (!FAudio_memcmp(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(FAudioGUID)))
+	{
+		return "IEEE_FLOAT";
+	}
+	if (!FAudio_memcmp(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_PCM, sizeof(FAudioGUID)))
+	{
+		return "PCM";
+	}
+	return "UNKNOWN!";
+}
+
+void FAudio_INTERNAL_debug_fmt(
+	FAudio *audio,
+	const char *file,
+	uint32_t line,
+	const char *func,
+	const FAudioWaveFormatEx *fmt
+) {
+	FAudio_INTERNAL_debug(
+		audio,
+		file,
+		line,
+		func,
+		(
+			"{"
+			"wFormatTag: 0x%x %s, "
+			"nChannels: %u, "
+			"nSamplesPerSec: %u, "
+			"wBitsPerSample: %u, "
+			"nBlockAlign: %u, "
+			"SubFormat: %s"
+			"}"
+		),
+		fmt->wFormatTag,
+		get_wformattag_string(fmt),
+		fmt->nChannels,
+		fmt->nSamplesPerSec,
+		fmt->wBitsPerSample,
+		fmt->nBlockAlign,
+		get_subformat_string(fmt)
+	);
+}
+#endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */
+
+void LinkedList_AddEntry(
+	LinkedList **start,
+	void* toAdd,
+	FAudioMutex lock,
+	FAudioMallocFunc pMalloc
+) {
+	LinkedList *newEntry, *latest;
+	newEntry = (LinkedList*) pMalloc(sizeof(LinkedList));
+	newEntry->entry = toAdd;
+	newEntry->next = NULL;
+	FAudio_PlatformLockMutex(lock);
+	if (*start == NULL)
+	{
+		*start = newEntry;
+	}
+	else
+	{
+		latest = *start;
+		while (latest->next != NULL)
+		{
+			latest = latest->next;
+		}
+		latest->next = newEntry;
+	}
+	FAudio_PlatformUnlockMutex(lock);
+}
+
+void LinkedList_PrependEntry(
+	LinkedList **start,
+	void* toAdd,
+	FAudioMutex lock,
+	FAudioMallocFunc pMalloc
+) {
+	LinkedList *newEntry;
+	newEntry = (LinkedList*) pMalloc(sizeof(LinkedList));
+	newEntry->entry = toAdd;
+	FAudio_PlatformLockMutex(lock);
+	newEntry->next = *start;
+	*start = newEntry;
+	FAudio_PlatformUnlockMutex(lock);
+}
+
+void LinkedList_RemoveEntry(
+	LinkedList **start,
+	void* toRemove,
+	FAudioMutex lock,
+	FAudioFreeFunc pFree
+) {
+	LinkedList *latest, *prev;
+	latest = *start;
+	prev = latest;
+	FAudio_PlatformLockMutex(lock);
+	while (latest != NULL)
+	{
+		if (latest->entry == toRemove)
+		{
+			if (latest == prev) /* First in list */
+			{
+				*start = latest->next;
+			}
+			else
+			{
+				prev->next = latest->next;
+			}
+			pFree(latest);
+			FAudio_PlatformUnlockMutex(lock);
+			return;
+		}
+		prev = latest;
+		latest = latest->next;
+	}
+	FAudio_PlatformUnlockMutex(lock);
+	FAudio_assert(0 && "LinkedList element not found!");
+}
+
+void FAudio_INTERNAL_InsertSubmixSorted(
+	LinkedList **start,
+	FAudioSubmixVoice *toAdd,
+	FAudioMutex lock,
+	FAudioMallocFunc pMalloc
+) {
+	LinkedList *newEntry, *latest;
+	newEntry = (LinkedList*) pMalloc(sizeof(LinkedList));
+	newEntry->entry = toAdd;
+	newEntry->next = NULL;
+	FAudio_PlatformLockMutex(lock);
+	if (*start == NULL)
+	{
+		*start = newEntry;
+	}
+	else
+	{
+		latest = *start;
+
+		/* Special case if the new stage is lower than everyone else */
+		if (toAdd->mix.processingStage < ((FAudioSubmixVoice*) latest->entry)->mix.processingStage)
+		{
+			newEntry->next = latest;
+			*start = newEntry;
+		}
+		else
+		{
+			/* If we got here, we know that the new stage is
+			 * _at least_ as high as the first submix in the list.
+			 *
+			 * Each loop iteration checks to see if the new stage
+			 * is smaller than `latest->next`, meaning it fits
+			 * between `latest` and `latest->next`.
+			 */
+			while (latest->next != NULL)
+			{
+				if (toAdd->mix.processingStage < ((FAudioSubmixVoice *) latest->next->entry)->mix.processingStage)
+				{
+					newEntry->next = latest->next;
+					latest->next = newEntry;
+					break;
+				}
+				latest = latest->next;
+			}
+			/* If newEntry didn't get a `next` value, that means
+			 * it didn't fall in between any stages and `latest`
+			 * is the last entry in the list. Add it to the end!
+			 */
+			if (newEntry->next == NULL)
+			{
+				latest->next = newEntry;
+			}
+		}
+	}
+	FAudio_PlatformUnlockMutex(lock);
+}
+
+static uint32_t FAudio_INTERNAL_GetBytesRequested(
+	FAudioSourceVoice *voice,
+	uint32_t decoding
+) {
+	uint32_t end, result;
+	FAudioBuffer *buffer;
+	FAudioWaveFormatExtensible *fmt;
+	FAudioBufferEntry *list = voice->src.bufferList;
+
+	LOG_FUNC_ENTER(voice->audio)
+
+#ifdef HAVE_WMADEC
+	if (voice->src.wmadec != NULL)
+	{
+		/* Always 0, per the spec */
+		LOG_FUNC_EXIT(voice->audio)
+		return 0;
+	}
+#endif /* HAVE_WMADEC */
+	while (list != NULL && decoding > 0)
+	{
+		buffer = &list->buffer;
+		if (buffer->LoopCount > 0)
+		{
+			end = (
+				/* Current loop... */
+				((buffer->LoopBegin + buffer->LoopLength) - voice->src.curBufferOffset) +
+				/* Remaining loops... */
+				(buffer->LoopLength * buffer->LoopCount - 1) +
+				/* ... Final iteration */
+				buffer->PlayLength
+			);
+		}
+		else
+		{
+			end = (buffer->PlayBegin + buffer->PlayLength) - voice->src.curBufferOffset;
+		}
+		if (end > decoding)
+		{
+			decoding = 0;
+			break;
+		}
+		decoding -= end;
+		list = list->next;
+	}
+
+	/* Convert samples to bytes, factoring block alignment */
+	if (voice->src.format->wFormatTag == FAUDIO_FORMAT_MSADPCM)
+	{
+		fmt = (FAudioWaveFormatExtensible*) voice->src.format;
+		result = (
+			(decoding / fmt->Samples.wSamplesPerBlock) +
+			((decoding % fmt->Samples.wSamplesPerBlock) > 0)
+		) * voice->src.format->nBlockAlign;
+	}
+	else
+	{
+		result = decoding * voice->src.format->nBlockAlign;
+	}
+
+	LOG_FUNC_EXIT(voice->audio)
+	return result;
+}
+
+static void FAudio_INTERNAL_DecodeBuffers(
+	FAudioSourceVoice *voice,
+	uint64_t *toDecode
+) {
+	uint32_t end, endRead, decoding, decoded = 0;
+	FAudioBuffer *buffer = &voice->src.bufferList->buffer;
+	FAudioBufferEntry *toDelete;
+
+	LOG_FUNC_ENTER(voice->audio)
+
+	/* This should never go past the max ratio size */
+	FAudio_assert(*toDecode <= voice->src.decodeSamples);
+
+	while (decoded < *toDecode && buffer != NULL)
+	{
+		decoding = (uint32_t) *toDecode - decoded;
+
+		/* Start-of-buffer behavior */
+		if (voice->src.newBuffer)
+		{
+			voice->src.newBuffer = 0;
+			if (	voice->src.callback != NULL &&
+				voice->src.callback->OnBufferStart != NULL	)
+			{
+				FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
+				LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
+
+				voice->src.callback->OnBufferStart(
+					voice->src.callback,
+					buffer->pContext
+				);
+
+				FAudio_PlatformLockMutex(voice->audio->sourceLock);
+				LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+			}
+		}
+
+		/* Check for end-of-buffer */
+		end = (buffer->LoopCount > 0) ?
+			(buffer->LoopBegin + buffer->LoopLength) :
+			buffer->PlayBegin + buffer->PlayLength;
+		endRead = FAudio_min(
+			end - voice->src.curBufferOffset,
+			decoding
+		);
+
+		/* Decode... */
+		voice->src.decode(
+			voice,
+			buffer,
+			voice->audio->decodeCache + (
+				decoded * voice->src.format->nChannels
+			),
+			endRead
+		);
+
+		LOG_INFO(
+			voice->audio,
+			"Voice %p, buffer %p, decoded %u samples from [%u,%u)",
+			(void*) voice,
+			(void*) buffer,
+			endRead,
+			voice->src.curBufferOffset,
+			voice->src.curBufferOffset + endRead
+		)
+
+		decoded += endRead;
+		voice->src.curBufferOffset += endRead;
+		voice->src.totalSamples += endRead;
+
+		/* End-of-buffer behavior */
+		if (endRead < decoding)
+		{
+			if (buffer->LoopCount > 0)
+			{
+				voice->src.curBufferOffset = buffer->LoopBegin;
+				if (buffer->LoopCount < FAUDIO_LOOP_INFINITE)
+				{
+					buffer->LoopCount -= 1;
+				}
+				if (	voice->src.callback != NULL &&
+					voice->src.callback->OnLoopEnd != NULL	)
+				{
+					FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
+					LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
+
+					voice->src.callback->OnLoopEnd(
+						voice->src.callback,
+						buffer->pContext
+					);
+
+					FAudio_PlatformLockMutex(voice->audio->sourceLock);
+					LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+				}
+			}
+			else
+			{
+#ifdef HAVE_WMADEC
+				if (voice->src.wmadec != NULL)
+				{
+					FAudio_WMADEC_end_buffer(voice);
+				}
+#endif /* HAVE_WMADEC */
+				/* For EOS we can stop storing fraction offsets */
+				if (buffer->Flags & FAUDIO_END_OF_STREAM)
+				{
+					voice->src.curBufferOffsetDec = 0;
+					voice->src.totalSamples = 0;
+				}
+
+				LOG_INFO(
+					voice->audio,
+					"Voice %p, finished with buffer %p",
+					(void*) voice,
+					(void*) buffer
+				)
+
+				/* Change active buffer, delete finished buffer */
+				toDelete = voice->src.bufferList;
+				voice->src.bufferList = voice->src.bufferList->next;
+				if (voice->src.bufferList != NULL)
+				{
+					buffer = &voice->src.bufferList->buffer;
+					voice->src.curBufferOffset = buffer->PlayBegin;
+				}
+				else
+				{
+					buffer = NULL;
+
+					/* FIXME: I keep going past the buffer so fuck it */
+					FAudio_zero(
+						voice->audio->decodeCache + (
+							decoded *
+							voice->src.format->nChannels
+						),
+						sizeof(float) * (
+							(*toDecode - decoded) *
+							voice->src.format->nChannels
+						)
+					);
+				}
+
+				/* Callbacks */
+				if (voice->src.callback != NULL)
+				{
+					FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
+					LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
+
+					if (voice->src.callback->OnBufferEnd != NULL)
+					{
+						voice->src.callback->OnBufferEnd(
+							voice->src.callback,
+							toDelete->buffer.pContext
+						);
+					}
+					if (	toDelete->buffer.Flags & FAUDIO_END_OF_STREAM &&
+						voice->src.callback->OnStreamEnd != NULL	)
+					{
+						voice->src.callback->OnStreamEnd(
+							voice->src.callback
+						);
+					}
+
+					/* One last chance at redemption */
+					if (buffer == NULL && voice->src.bufferList != NULL)
+					{
+						buffer = &voice->src.bufferList->buffer;
+						voice->src.curBufferOffset = buffer->PlayBegin;
+					}
+
+					if (buffer != NULL && voice->src.callback->OnBufferStart != NULL)
+					{
+						voice->src.callback->OnBufferStart(
+							voice->src.callback,
+							buffer->pContext
+						);
+					}
+
+					FAudio_PlatformLockMutex(voice->audio->sourceLock);
+					LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+				}
+
+				voice->audio->pFree(toDelete);
+			}
+		}
+	}
+
+	/* ... FIXME: I keep going past the buffer so fuck it */
+	if (buffer)
+	{
+		end = (buffer->LoopCount > 0) ?
+			(buffer->LoopBegin + buffer->LoopLength) :
+			buffer->PlayBegin + buffer->PlayLength;
+		endRead = FAudio_min(
+			end - voice->src.curBufferOffset,
+			EXTRA_DECODE_PADDING
+		);
+
+		voice->src.decode(
+			voice,
+			buffer,
+			voice->audio->decodeCache + (
+				decoded * voice->src.format->nChannels
+			),
+			endRead
+		);
+		/* Do NOT increment curBufferOffset! */
+
+		if (endRead < EXTRA_DECODE_PADDING)
+		{
+			FAudio_zero(
+				voice->audio->decodeCache + (
+					decoded * voice->src.format->nChannels
+				),
+				sizeof(float) * (
+					(EXTRA_DECODE_PADDING - endRead) *
+					voice->src.format->nChannels
+				)
+			);
+		}
+	}
+	else
+	{
+		FAudio_zero(
+			voice->audio->decodeCache + (
+				decoded * voice->src.format->nChannels
+			),
+			sizeof(float) * (
+				EXTRA_DECODE_PADDING *
+				voice->src.format->nChannels
+			)
+		);
+	}
+
+	*toDecode = decoded;
+	LOG_FUNC_EXIT(voice->audio)
+}
+
+static inline void FAudio_INTERNAL_FilterVoice(
+	FAudio *audio,
+	const FAudioFilterParameters *filter,
+	FAudioFilterState *filterState,
+	float *samples,
+	uint32_t numSamples,
+	uint16_t numChannels
+) {
+	uint32_t j, ci;
+
+	LOG_FUNC_ENTER(audio)
+
+	/* Apply a digital state-variable filter to the voice.
+	 * The difference equations of the filter are:
+	 *
+	 * Yl(n) = F Yb(n - 1) + Yl(n - 1)
+	 * Yh(n) = x(n) - Yl(n) - OneOverQ Yb(n - 1)
+	 * Yb(n) = F Yh(n) + Yb(n - 1)
+	 * Yn(n) = Yl(n) + Yh(n)
+	 *
+	 * Please note that FAudioFilterParameters.Frequency is defined as:
+	 *
+	 * (2 * sin(pi * (desired filter cutoff frequency) / sampleRate))
+	 *
+	 * - @JohanSmet
+	 */
+
+	for (j = 0; j < numSamples; j += 1)
+	for (ci = 0; ci < numChannels; ci += 1)
+	{
+		filterState[ci][FAudioLowPassFilter] = filterState[ci][FAudioLowPassFilter] + (filter->Frequency * filterState[ci][FAudioBandPassFilter]);
+		filterState[ci][FAudioHighPassFilter] = samples[j * numChannels + ci] - filterState[ci][FAudioLowPassFilter] - (filter->OneOverQ * filterState[ci][FAudioBandPassFilter]);
+		filterState[ci][FAudioBandPassFilter] = (filter->Frequency * filterState[ci][FAudioHighPassFilter]) + filterState[ci][FAudioBandPassFilter];
+		filterState[ci][FAudioNotchFilter] = filterState[ci][FAudioHighPassFilter] + filterState[ci][FAudioLowPassFilter];
+		samples[j * numChannels + ci] = filterState[ci][filter->Type];
+	}
+
+	LOG_FUNC_EXIT(audio)
+}
+
+static void FAudio_INTERNAL_ResizeEffectChainCache(FAudio *audio, uint32_t samples)
+{
+	LOG_FUNC_ENTER(audio)
+	if (samples > audio->effectChainSamples)
+	{
+		audio->effectChainSamples = samples;
+		audio->effectChainCache = (float*) audio->pRealloc(
+			audio->effectChainCache,
+			sizeof(float) * audio->effectChainSamples
+		);
+	}
+	LOG_FUNC_EXIT(audio)
+}
+
+static inline float *FAudio_INTERNAL_ProcessEffectChain(
+	FAudioVoice *voice,
+	float *buffer,
+	uint32_t *samples
+) {
+	uint32_t i;
+	FAPO *fapo;
+	FAPOProcessBufferParameters srcParams, dstParams;
+
+	LOG_FUNC_ENTER(voice->audio)
+
+	/* Set up the buffer to be written into */
+	srcParams.pBuffer = buffer;
+	srcParams.BufferFlags = FAPO_BUFFER_SILENT;
+	srcParams.ValidFrameCount = *samples;
+	for (i = 0; i < srcParams.ValidFrameCount; i += 1)
+	{
+		if (buffer[i] != 0.0f) /* Arbitrary! */
+		{
+			srcParams.BufferFlags = FAPO_BUFFER_VALID;
+			break;
+		}
+	}
+
+	/* Initialize output parameters to something sane */
+	dstParams.pBuffer = srcParams.pBuffer;
+	dstParams.BufferFlags = FAPO_BUFFER_VALID;
+	dstParams.ValidFrameCount = srcParams.ValidFrameCount;
+
+	/* Update parameters, process! */
+	for (i = 0; i < voice->effects.count; i += 1)
+	{
+		fapo = voice->effects.desc[i].pEffect;
+
+		if (!voice->effects.inPlaceProcessing[i])
+		{
+			if (dstParams.pBuffer == buffer)
+			{
+				FAudio_INTERNAL_ResizeEffectChainCache(
+					voice->audio,
+					voice->effects.desc[i].OutputChannels * srcParams.ValidFrameCount
+				);
+				dstParams.pBuffer = voice->audio->effectChainCache;
+			}
+			else
+			{
+				/* FIXME: What if this is smaller because
+				 * inputChannels < desc[i].OutputChannels?
+				 */
+				dstParams.pBuffer = buffer;
+			}
+
+			FAudio_zero(
+				dstParams.pBuffer,
+				voice->effects.desc[i].OutputChannels * srcParams.ValidFrameCount * sizeof(float)
+			);
+		}
+
+		if (voice->effects.parameterUpdates[i])
+		{
+			fapo->SetParameters(
+				fapo,
+				voice->effects.parameters[i],
+				voice->effects.parameterSizes[i]
+			);
+			voice->effects.parameterUpdates[i] = 0;
+		}
+
+		fapo->Process(
+			fapo,
+			1,
+			&srcParams,
+			1,
+			&dstParams,
+			voice->effects.desc[i].InitialState
+		);
+
+		FAudio_memcpy(&srcParams, &dstParams, sizeof(dstParams));
+	}
+
+	*samples = dstParams.ValidFrameCount;
+
+	/* Save the output buffer-flags so the mixer-function can determine when it's save to stop processing the effect chain */
+	voice->effects.state = dstParams.BufferFlags;
+
+	LOG_FUNC_EXIT(voice->audio)
+	return (float*) dstParams.pBuffer;
+}
+
+static void FAudio_INTERNAL_ResizeResampleCache(FAudio *audio, uint32_t samples)
+{
+       LOG_FUNC_ENTER(audio)
+       if (samples > audio->resampleSamples)
+       {
+               audio->resampleSamples = samples;
+               audio->resampleCache = (float*) audio->pRealloc(
+                       audio->resampleCache,
+                       sizeof(float) * audio->resampleSamples
+               );
+       }
+       LOG_FUNC_EXIT(audio)
+}
+
+static void FAudio_INTERNAL_MixSource(FAudioSourceVoice *voice)
+{
+	/* Iterators */
+	uint32_t i;
+	/* Decode/Resample variables */
+	uint64_t toDecode;
+	uint64_t toResample;
+	/* Output mix variables */
+	float *stream;
+	uint32_t mixed;
+	uint32_t oChan;
+	FAudioVoice *out;
+	uint32_t outputRate;
+	double stepd;
+	float *finalSamples;
+
+	LOG_FUNC_ENTER(voice->audio)
+
+	FAudio_PlatformLockMutex(voice->sendLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+
+	/* Calculate the resample stepping value */
+	if (voice->src.resampleFreq != voice->src.freqRatio * voice->src.format->nSamplesPerSec)
+	{
+		out = (voice->sends.SendCount == 0) ?
+			voice->audio->master : /* Barf */
+			voice->sends.pSends->pOutputVoice;
+		outputRate = (out->type == FAUDIO_VOICE_MASTER) ?
+			out->master.inputSampleRate :
+			out->mix.inputSampleRate;
+		stepd = (
+			voice->src.freqRatio *
+			(double) voice->src.format->nSamplesPerSec /
+			(double) outputRate
+		);
+		voice->src.resampleStep = DOUBLE_TO_FIXED(stepd);
+		voice->src.resampleFreq = voice->src.freqRatio * voice->src.format->nSamplesPerSec;
+	}
+
+	if (voice->src.active == 2)
+	{
+		/* We're just playing tails, skip all buffer stuff */
+		FAudio_INTERNAL_ResizeResampleCache(
+				voice->audio,
+				voice->src.resampleSamples * voice->src.format->nChannels
+		);
+		mixed = voice->src.resampleSamples;
+		FAudio_zero(
+			voice->audio->resampleCache,
+			mixed * voice->src.format->nChannels * sizeof(float)
+		);
+		finalSamples = voice->audio->resampleCache;
+		goto sendwork;
+	}
+
+	/* Base decode size, int to fixed... */
+	toDecode = voice->src.resampleSamples * voice->src.resampleStep;
+	/* ... rounded up based on current offset... */
+	toDecode += voice->src.curBufferOffsetDec + FIXED_FRACTION_MASK;
+	/* ... fixed to int, truncating extra fraction from rounding. */
+	toDecode >>= FIXED_PRECISION;
+
+	/* First voice callback */
+	if (	voice->src.callback != NULL &&
+		voice->src.callback->OnVoiceProcessingPassStart != NULL	)
+	{
+		FAudio_PlatformUnlockMutex(voice->sendLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+
+		FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
+
+		voice->src.callback->OnVoiceProcessingPassStart(
+			voice->src.callback,
+			FAudio_INTERNAL_GetBytesRequested(voice, (uint32_t) toDecode)
+		);
+
+		FAudio_PlatformLockMutex(voice->audio->sourceLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+
+		FAudio_PlatformLockMutex(voice->sendLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+	}
+
+	FAudio_PlatformLockMutex(voice->src.bufferLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
+
+	/* Nothing to do? */
+	if (voice->src.bufferList == NULL)
+	{
+		FAudio_PlatformUnlockMutex(voice->src.bufferLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
+
+		if (voice->effects.count > 0 && voice->effects.state != FAPO_BUFFER_SILENT)
+		{
+			/* do not stop while the effect chain generates a non-silent buffer */
+			FAudio_INTERNAL_ResizeResampleCache(
+					voice->audio,
+					voice->src.resampleSamples * voice->src.format->nChannels
+			);
+			mixed = voice->src.resampleSamples;
+			FAudio_zero(
+				voice->audio->resampleCache,
+				mixed * voice->src.format->nChannels * sizeof(float)
+			);
+			finalSamples = voice->audio->resampleCache;
+			goto sendwork;
+		}
+
+		FAudio_PlatformUnlockMutex(voice->sendLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+
+		FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
+
+		if (	voice->src.callback != NULL &&
+			voice->src.callback->OnVoiceProcessingPassEnd != NULL)
+		{
+			voice->src.callback->OnVoiceProcessingPassEnd(
+				voice->src.callback
+			);
+		}
+
+		FAudio_PlatformLockMutex(voice->audio->sourceLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+
+		LOG_FUNC_EXIT(voice->audio)
+		return;
+	}
+
+	/* Decode... */
+	FAudio_INTERNAL_DecodeBuffers(voice, &toDecode);
+
+	/* Subtract any padding samples from the total, if applicable */
+	if (	voice->src.curBufferOffsetDec > 0 &&
+		voice->src.totalSamples > 0	)
+	{
+		voice->src.totalSamples -= 1;
+	}
+
+	/* Okay, we're done messing with client data */
+	if (	voice->src.callback != NULL &&
+		voice->src.callback->OnVoiceProcessingPassEnd != NULL)
+	{
+		FAudio_PlatformUnlockMutex(voice->src.bufferLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
+
+		FAudio_PlatformUnlockMutex(voice->sendLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+
+		FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
+
+		voice->src.callback->OnVoiceProcessingPassEnd(
+			voice->src.callback
+		);
+
+		FAudio_PlatformLockMutex(voice->audio->sourceLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+
+		FAudio_PlatformLockMutex(voice->sendLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+
+		FAudio_PlatformLockMutex(voice->src.bufferLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
+	}
+
+	/* Nothing to resample? */
+	if (toDecode == 0)
+	{
+		FAudio_PlatformUnlockMutex(voice->src.bufferLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
+
+		FAudio_PlatformUnlockMutex(voice->sendLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+
+		LOG_FUNC_EXIT(voice->audio)
+		return;
+	}
+
+	/* int to fixed... */
+	toResample = toDecode << FIXED_PRECISION;
+	/* ... round back down based on current offset... */
+	toResample -= voice->src.curBufferOffsetDec;
+	/* ... but also ceil for any fraction value... */
+	toResample += FIXED_FRACTION_MASK;
+	/* ... undo step size, fixed to int. */
+	toResample /= voice->src.resampleStep;
+	/* Add the padding, for some reason this helps? */
+	toResample += EXTRA_DECODE_PADDING;
+	/* FIXME: I feel like this should be an assert but I suck */
+	toResample = FAudio_min(toResample, voice->src.resampleSamples);
+
+	/* Resample... */
+	if (voice->src.resampleStep == FIXED_ONE)
+	{
+		/* Actually, just use the existing buffer... */
+		finalSamples = voice->audio->decodeCache;
+	}
+	else
+	{
+		FAudio_INTERNAL_ResizeResampleCache(
+				voice->audio,
+				voice->src.resampleSamples * voice->src.format->nChannels
+		);
+		voice->src.resample(
+			voice->audio->decodeCache,
+			voice->audio->resampleCache,
+			&voice->src.resampleOffset,
+			voice->src.resampleStep,
+			toResample,
+			(uint8_t) voice->src.format->nChannels
+		);
+		finalSamples = voice->audio->resampleCache;
+	}
+
+	/* Update buffer offsets */
+	if (voice->src.bufferList != NULL)
+	{
+		/* Increment fixed offset by resample size, int to fixed... */
+		voice->src.curBufferOffsetDec += toResample * voice->src.resampleStep;
+		/* ... chop off any ints we got from the above increment */
+		voice->src.curBufferOffsetDec &= FIXED_FRACTION_MASK;
+
+		/* Dec >0? We need one frame from the past...
+		 * FIXME: We can't go back to a prev buffer though?
+		 */
+		if (	voice->src.curBufferOffsetDec > 0 &&
+			voice->src.curBufferOffset > 0	)
+		{
+			voice->src.curBufferOffset -= 1;
+		}
+	}
+	else
+	{
+		voice->src.curBufferOffsetDec = 0;
+		voice->src.curBufferOffset = 0;
+	}
+
+	/* Done with buffers, finally. */
+	FAudio_PlatformUnlockMutex(voice->src.bufferLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
+	mixed = (uint32_t) toResample;
+
+sendwork:
+
+	/* Filters */
+	if (voice->flags & FAUDIO_VOICE_USEFILTER)
+	{
+		FAudio_PlatformLockMutex(voice->filterLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
+		FAudio_INTERNAL_FilterVoice(
+			voice->audio,
+			&voice->filter,
+			voice->filterState,
+			finalSamples,
+			mixed,
+			voice->src.format->nChannels
+		);
+		FAudio_PlatformUnlockMutex(voice->filterLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->filterLock)
+	}
+
+	/* Process effect chain */
+	FAudio_PlatformLockMutex(voice->effectLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
+	if (voice->effects.count > 0)
+	{
+		/* If we didn't get the full size of the update, we have to fill
+		 * it with silence so the effect can process a whole update
+		 */
+		if (mixed < voice->src.resampleSamples)
+		{
+			FAudio_zero(
+				finalSamples + (mixed * voice->src.format->nChannels),
+				(voice->src.resampleSamples - mixed) * voice->src.format->nChannels * sizeof(float)
+			);
+			mixed = voice->src.resampleSamples;
+		}
+		finalSamples = FAudio_INTERNAL_ProcessEffectChain(
+			voice,
+			finalSamples,
+			&mixed
+		);
+	}
+	FAudio_PlatformUnlockMutex(voice->effectLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
+
+	/* Nowhere to send it? Just skip the rest...*/
+	if (voice->sends.SendCount == 0)
+	{
+		FAudio_PlatformUnlockMutex(voice->sendLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+		LOG_FUNC_EXIT(voice->audio)
+		return;
+	}
+
+	/* Send float cache to sends */
+	FAudio_PlatformLockMutex(voice->volumeLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
+	for (i = 0; i < voice->sends.SendCount; i += 1)
+	{
+		out = voice->sends.pSends[i].pOutputVoice;
+		if (out->type == FAUDIO_VOICE_MASTER)
+		{
+			stream = out->master.output;
+			oChan = out->master.inputChannels;
+		}
+		else
+		{
+			stream = out->mix.inputCache;
+			oChan = out->mix.inputChannels;
+		}
+
+		voice->sendMix[i](
+			mixed,
+			voice->outputChannels,
+			oChan,
+			finalSamples,
+			stream,
+			voice->mixCoefficients[i]
+		);
+
+		if (voice->sends.pSends[i].Flags & FAUDIO_SEND_USEFILTER)
+		{
+			FAudio_INTERNAL_FilterVoice(
+				voice->audio,
+				&voice->sendFilter[i],
+				voice->sendFilterState[i],
+				stream,
+				mixed,
+				oChan
+			);
+		}
+	}
+	FAudio_PlatformUnlockMutex(voice->volumeLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
+
+	FAudio_PlatformUnlockMutex(voice->sendLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+	LOG_FUNC_EXIT(voice->audio)
+}
+
+static void FAudio_INTERNAL_MixSubmix(FAudioSubmixVoice *voice)
+{
+	uint32_t i;
+	float *stream;
+	uint32_t oChan;
+	FAudioVoice *out;
+	uint32_t resampled;
+	uint64_t resampleOffset = 0;
+	float *finalSamples;
+
+	LOG_FUNC_ENTER(voice->audio)
+	FAudio_PlatformLockMutex(voice->sendLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
+
+	/* Resample */
+	if (voice->mix.resampleStep == FIXED_ONE)
+	{
+		/* Actually, just use the existing buffer... */
+		finalSamples = voice->mix.inputCache;
+	}
+	else
+	{
+		FAudio_INTERNAL_ResizeResampleCache(
+				voice->audio,
+				voice->mix.outputSamples * voice->mix.inputChannels
+		);
+		voice->mix.resample(
+			voice->mix.inputCache,
+			voice->audio->resampleCache,
+			&resampleOffset,
+			voice->mix.resampleStep,
+			voice->mix.outputSamples,
+			(uint8_t) voice->mix.inputChannels
+		);
+		finalSamples = voice->audio->resampleCache;
+	}
+	resampled = voice->mix.outputSamples * voice->mix.inputChannels;
+
+	/* Submix overall volume is applied _before_ effects/filters, blech! */
+	if (voice->volume != 1.0f)
+	{
+		FAudio_INTERNAL_Amplify(
+			finalSamples,
+			resampled,
+			voice->volume
+		);
+	}
+	resampled /= voice->mix.inputChannels;
+
+	/* Filters */
+	if (voice->flags & FAUDIO_VOICE_USEFILTER)
+	{
+		FAudio_PlatformLockMutex(voice->filterLock);
+		LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
+		FAudio_INTERNAL_FilterVoice(
+			voice->audio,
+			&voice->filter,
+			voice->filterState,
+			finalSamples,
+			resampled,
+			voice->mix.inputChannels
+		);
+		FAudio_PlatformUnlockMutex(voice->filterLock);
+		LOG_MUTEX_UNLOCK(voice->audio, voice->filterLock)
+	}
+
+	/* Process effect chain */
+	FAudio_PlatformLockMutex(voice->effectLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
+	if (voice->effects.count > 0)
+	{
+		finalSamples = FAudio_INTERNAL_ProcessEffectChain(
+			voice,
+			finalSamples,
+			&resampled
+		);
+	}
+	FAudio_PlatformUnlockMutex(voice->effectLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
+
+	/* Nothing more to do? */
+	if (voice->sends.SendCount == 0)
+	{
+		goto end;
+	}
+
+	/* Send float cache to sends */
+	FAudio_PlatformLockMutex(voice->volumeLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
+	for (i = 0; i < voice->sends.SendCount; i += 1)
+	{
+		out = voice->sends.pSends[i].pOutputVoice;
+		if (out->type == FAUDIO_VOICE_MASTER)
+		{
+			stream = out->master.output;
+			oChan = out->master.inputChannels;
+		}
+		else
+		{
+			stream = out->mix.inputCache;
+			oChan = out->mix.inputChannels;
+		}
+
+		voice->sendMix[i](
+			resampled,
+			voice->outputChannels,
+			oChan,
+			finalSamples,
+			stream,
+			voice->mixCoefficients[i]
+		);
+
+		if (voice->sends.pSends[i].Flags & FAUDIO_SEND_USEFILTER)
+		{
+			FAudio_INTERNAL_FilterVoice(
+				voice->audio,
+				&voice->sendFilter[i],
+				voice->sendFilterState[i],
+				stream,
+				resampled,
+				oChan
+			);
+		}
+	}
+	FAudio_PlatformUnlockMutex(voice->volumeLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
+
+	/* Zero this at the end, for the next update */
+end:
+	FAudio_PlatformUnlockMutex(voice->sendLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
+	FAudio_zero(
+		voice->mix.inputCache,
+		sizeof(float) * voice->mix.inputSamples
+	);
+	LOG_FUNC_EXIT(voice->audio)
+}
+
+static void FAudio_INTERNAL_FlushPendingBuffers(FAudioSourceVoice *voice)
+{
+	FAudioBufferEntry *entry;
+
+	FAudio_PlatformLockMutex(voice->src.bufferLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
+
+	/* Remove pending flushed buffers and send an event for each one */
+	while (voice->src.flushList != NULL)
+	{
+		entry = voice->src.flushList;
+		voice->src.flushList = voice->src.flushList->next;
+
+		if (voice->src.callback != NULL && voice->src.callback->OnBufferEnd != NULL)
+		{
+			FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
+			LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
+
+			voice->src.callback->OnBufferEnd(
+				voice->src.callback,
+				entry->buffer.pContext
+			);
+
+			FAudio_PlatformLockMutex(voice->audio->sourceLock);
+			LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+		}
+		voice->audio->pFree(entry);
+	}
+
+	FAudio_PlatformUnlockMutex(voice->src.bufferLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
+}
+
+static void FAUDIOCALL FAudio_INTERNAL_GenerateOutput(FAudio *audio, float *output)
+{
+	uint32_t totalSamples;
+	LinkedList *list;
+	float *effectOut;
+	FAudioEngineCallback *callback;
+
+	LOG_FUNC_ENTER(audio)
+	if (!audio->active)
+	{
+		LOG_FUNC_EXIT(audio)
+		return;
+	}
+
+	/* Apply any committed changes */
+	FAudio_OPERATIONSET_Execute(audio);
+
+	/* ProcessingPassStart callbacks */
+	FAudio_PlatformLockMutex(audio->callbackLock);
+	LOG_MUTEX_LOCK(audio, audio->callbackLock)
+	list = audio->callbacks;
+	while (list != NULL)
+	{
+		callback = (FAudioEngineCallback*) list->entry;
+		if (callback->OnProcessingPassStart != NULL)
+		{
+			callback->OnProcessingPassStart(
+				callback
+			);
+		}
+		list = list->next;
+	}
+	FAudio_PlatformUnlockMutex(audio->callbackLock);
+	LOG_MUTEX_UNLOCK(audio, audio->callbackLock)
+
+	/* Writes to master will directly write to output, but ONLY if there
+	 * isn't any channel-changing effect processing to do first.
+	 */
+	if (audio->master->master.effectCache != NULL)
+	{
+		audio->master->master.output = audio->master->master.effectCache;
+		FAudio_zero(
+			audio->master->master.effectCache,
+			(
+				sizeof(float) *
+				audio->updateSize *
+				audio->master->master.inputChannels
+			)
+		);
+	}
+	else
+	{
+		audio->master->master.output = output;
+	}
+
+	/* Mix sources */
+	FAudio_PlatformLockMutex(audio->sourceLock);
+	LOG_MUTEX_LOCK(audio, audio->sourceLock)
+	list = audio->sources;
+	while (list != NULL)
+	{
+		audio->processingSource = (FAudioSourceVoice*) list->entry;
+
+		FAudio_INTERNAL_FlushPendingBuffers(audio->processingSource);
+		if (audio->processingSource->src.active)
+		{
+			FAudio_INTERNAL_MixSource(audio->processingSource);
+			FAudio_INTERNAL_FlushPendingBuffers(audio->processingSource);
+		}
+
+		list = list->next;
+	}
+	audio->processingSource = NULL;
+	FAudio_PlatformUnlockMutex(audio->sourceLock);
+	LOG_MUTEX_UNLOCK(audio, audio->sourceLock)
+
+	/* Mix submixes, ordered by processing stage */
+	FAudio_PlatformLockMutex(audio->submixLock);
+	LOG_MUTEX_LOCK(audio, audio->submixLock)
+	list = audio->submixes;
+	while (list != NULL)
+	{
+		FAudio_INTERNAL_MixSubmix((FAudioSubmixVoice*) list->entry);
+		list = list->next;
+	}
+	FAudio_PlatformUnlockMutex(audio->submixLock);
+	LOG_MUTEX_UNLOCK(audio, audio->submixLock)
+
+	/* Apply master volume */
+	if (audio->master->volume != 1.0f)
+	{
+		FAudio_INTERNAL_Amplify(
+			audio->master->master.output,
+			audio->updateSize * audio->master->master.inputChannels,
+			audio->master->volume
+		);
+	}
+
+	/* Process master effect chain */
+	FAudio_PlatformLockMutex(audio->master->effectLock);
+	LOG_MUTEX_LOCK(audio, audio->master->effectLock)
+	if (audio->master->effects.count > 0)
+	{
+		totalSamples = audio->updateSize;
+		effectOut = FAudio_INTERNAL_ProcessEffectChain(
+			audio->master,
+			audio->master->master.output,
+			&totalSamples
+		);
+
+		if (effectOut != output)
+		{
+			FAudio_memcpy(
+				output,
+				effectOut,
+				totalSamples * audio->master->outputChannels * sizeof(float)
+			);
+		}
+		if (totalSamples < audio->updateSize)
+		{
+			FAudio_zero(
+				output + (totalSamples * audio->master->outputChannels),
+				(audio->updateSize - totalSamples) * sizeof(float)
+			);
+		}
+	}
+	FAudio_PlatformUnlockMutex(audio->master->effectLock);
+	LOG_MUTEX_UNLOCK(audio, audio->master->effectLock)
+
+	/* OnProcessingPassEnd callbacks */
+	FAudio_PlatformLockMutex(audio->callbackLock);
+	LOG_MUTEX_LOCK(audio, audio->callbackLock)
+	list = audio->callbacks;
+	while (list != NULL)
+	{
+		callback = (FAudioEngineCallback*) list->entry;
+		if (callback->OnProcessingPassEnd != NULL)
+		{
+			callback->OnProcessingPassEnd(
+				callback
+			);
+		}
+		list = list->next;
+	}
+	FAudio_PlatformUnlockMutex(audio->callbackLock);
+	LOG_MUTEX_UNLOCK(audio, audio->callbackLock)
+
+	LOG_FUNC_EXIT(audio)
+}
+
+void FAudio_INTERNAL_UpdateEngine(FAudio *audio, float *output)
+{
+	LOG_FUNC_ENTER(audio)
+	if (audio->pClientEngineProc)
+	{
+		audio->pClientEngineProc(
+			&FAudio_INTERNAL_GenerateOutput,
+			audio,
+			output,
+			audio->clientEngineUser
+		);
+	}
+	else
+	{
+		FAudio_INTERNAL_GenerateOutput(audio, output);
+	}
+	LOG_FUNC_EXIT(audio)
+}
+
+void FAudio_INTERNAL_ResizeDecodeCache(FAudio *audio, uint32_t samples)
+{
+	LOG_FUNC_ENTER(audio)
+	FAudio_PlatformLockMutex(audio->sourceLock);
+	LOG_MUTEX_LOCK(audio, audio->sourceLock)
+	if (samples > audio->decodeSamples)
+	{
+		audio->decodeSamples = samples;
+		audio->decodeCache = (float*) audio->pRealloc(
+			audio->decodeCache,
+			sizeof(float) * audio->decodeSamples
+		);
+	}
+	FAudio_PlatformUnlockMutex(audio->sourceLock);
+	LOG_MUTEX_UNLOCK(audio, audio->sourceLock)
+	LOG_FUNC_EXIT(audio)
+}
+
+void FAudio_INTERNAL_AllocEffectChain(
+	FAudioVoice *voice,
+	const FAudioEffectChain *pEffectChain
+) {
+	uint32_t i;
+
+	LOG_FUNC_ENTER(voice->audio)
+	voice->effects.state = FAPO_BUFFER_VALID;
+	voice->effects.count = pEffectChain->EffectCount;
+	if (voice->effects.count == 0)
+	{
+		LOG_FUNC_EXIT(voice->audio)
+		return;
+	}
+
+	for (i = 0; i < pEffectChain->EffectCount; i += 1)
+	{
+		pEffectChain->pEffectDescriptors[i].pEffect->AddRef(pEffectChain->pEffectDescriptors[i].pEffect);
+	}
+
+	voice->effects.desc = (FAudioEffectDescriptor*) voice->audio->pMalloc(
+		voice->effects.count * sizeof(FAudioEffectDescriptor)
+	);
+	FAudio_memcpy(
+		voice->effects.desc,
+		pEffectChain->pEffectDescriptors,
+		voice->effects.count * sizeof(FAudioEffectDescriptor)
+	);
+	#define ALLOC_EFFECT_PROPERTY(prop, type) \
+		voice->effects.prop = (type*) voice->audio->pMalloc( \
+			voice->effects.count * sizeof(type) \
+		); \
+		FAudio_zero( \
+			voice->effects.prop, \
+			voice->effects.count * sizeof(type) \
+		);
+	ALLOC_EFFECT_PROPERTY(parameters, void*)
+	ALLOC_EFFECT_PROPERTY(parameterSizes, uint32_t)
+	ALLOC_EFFECT_PROPERTY(parameterUpdates, uint8_t)
+	ALLOC_EFFECT_PROPERTY(inPlaceProcessing, uint8_t)
+	#undef ALLOC_EFFECT_PROPERTY
+	LOG_FUNC_EXIT(voice->audio)
+}
+
+void FAudio_INTERNAL_FreeEffectChain(FAudioVoice *voice)
+{
+	uint32_t i;
+
+	LOG_FUNC_ENTER(voice->audio)
+	if (voice->effects.count == 0)
+	{
+		LOG_FUNC_EXIT(voice->audio)
+		return;
+	}
+
+	for (i = 0; i < voice->effects.count; i += 1)
+	{
+		voice->effects.desc[i].pEffect->UnlockForProcess(voice->effects.desc[i].pEffect);
+		voice->effects.desc[i].pEffect->Release(voice->effects.desc[i].pEffect);
+	}
+
+	voice->audio->pFree(voice->effects.desc);
+	voice->audio->pFree(voice->effects.parameters);
+	voice->audio->pFree(voice->effects.parameterSizes);
+	voice->audio->pFree(voice->effects.parameterUpdates);
+	voice->audio->pFree(voice->effects.inPlaceProcessing);
+	LOG_FUNC_EXIT(voice->audio)
+}
+
+uint32_t FAudio_INTERNAL_VoiceOutputFrequency(
+	FAudioVoice *voice,
+	const FAudioVoiceSends *pSendList
+) {
+	uint32_t outSampleRate;
+	uint32_t newResampleSamples;
+	uint64_t resampleSanityCheck;
+
+	LOG_FUNC_ENTER(voice->audio)
+
+	if ((pSendList == NULL) || (pSendList->SendCount == 0))
+	{
+		/* When we're deliberately given no sends, use master rate! */
+		outSampleRate = voice->audio->master->master.inputSampleRate;
+	}
+	else
+	{
+		outSampleRate = pSendList->pSends[0].pOutputVoice->type == FAUDIO_VOICE_MASTER ?
+			pSendList->pSends[0].pOutputVoice->master.inputSampleRate :
+			pSendList->pSends[0].pOutputVoice->mix.inputSampleRate;
+	}
+	newResampleSamples = (uint32_t) FAudio_ceil(
+		voice->audio->updateSize *
+		(double) outSampleRate /
+		(double) voice->audio->master->master.inputSampleRate
+	);
+	if (voice->type == FAUDIO_VOICE_SOURCE)
+	{
+		if (	(voice->src.resampleSamples != 0) &&
+			(newResampleSamples != voice->src.resampleSamples) &&
+			(voice->effects.count > 0)	)
+		{
+			LOG_FUNC_EXIT(voice->audio)
+			return FAUDIO_E_INVALID_CALL;
+		}
+		voice->src.resampleSamples = newResampleSamples;
+	}
+	else /* (voice->type == FAUDIO_VOICE_SUBMIX) */
+	{
+		if (	(voice->mix.outputSamples != 0) &&
+			(newResampleSamples != voice->mix.outputSamples) &&
+			(voice->effects.count > 0)	)
+		{
+			LOG_FUNC_EXIT(voice->audio)
+			return FAUDIO_E_INVALID_CALL;
+		}
+		voice->mix.outputSamples = newResampleSamples;
+
+		voice->mix.resampleStep = DOUBLE_TO_FIXED((
+			(double) voice->mix.inputSampleRate /
+			(double) outSampleRate
+		));
+
+		/* Because we used ceil earlier, there's a chance that
+		 * downsampling submixes will go past the number of samples
+		 * available. Sources can do this thanks to padding, but we
+		 * don't have that luxury for submixes, so unfortunately we
+		 * just have to undo the ceil and turn it into a floor.
+		 * -flibit
+		 */
+		resampleSanityCheck = (
+			voice->mix.resampleStep * voice->mix.outputSamples
+		) >> FIXED_PRECISION;
+		if (resampleSanityCheck > (voice->mix.inputSamples / voice->mix.inputChannels))
+		{
+			voice->mix.outputSamples -= 1;
+		}
+	}
+
+	LOG_FUNC_EXIT(voice->audio)
+	return 0;
+}
+
+const float FAUDIO_INTERNAL_MATRIX_DEFAULTS[8][8][64] =
+{
+	#include "matrix_defaults.inl"
+};
+
+/* PCM Decoding */
+
+void FAudio_INTERNAL_DecodePCM8(
+	FAudioVoice *voice,
+	FAudioBuffer *buffer,
+	float *decodeCache,
+	uint32_t samples
+) {
+	LOG_FUNC_ENTER(voice->audio)
+	FAudio_INTERNAL_Convert_U8_To_F32(
+		((uint8_t*) buffer->pAudioData) + (
+			voice->src.curBufferOffset * voice->src.format->nChannels
+		),
+		decodeCache,
+		samples * voice->src.format->nChannels
+	);
+	LOG_FUNC_EXIT(voice->audio)
+}
+
+void FAudio_INTERNAL_DecodePCM16(
+	FAudioVoice *voice,
+	FAudioBuffer *buffer,
+	float *decodeCache,
+	uint32_t samples
+) {
+	LOG_FUNC_ENTER(voice->audio)
+	FAudio_INTERNAL_Convert_S16_To_F32(
+		((int16_t*) buffer->pAudioData) + (
+			voice->src.curBufferOffset * voice->src.format->nChannels
+		),
+		decodeCache,
+		samples * voice->src.format->nChannels
+	);
+	LOG_FUNC_EXIT(voice->audio)
+}
+
+void FAudio_INTERNAL_DecodePCM24(
+	FAudioVoice *voice,
+	FAudioBuffer *buffer,
+	float *decodeCache,
+	uint32_t samples
+) {
+	uint32_t i, j;
+	const uint8_t *buf;
+	LOG_FUNC_ENTER(voice->audio)
+
+	/* FIXME: Uh... is this something that can be SIMD-ified? */
+	buf = buffer->pAudioData + (
+		voice->src.curBufferOffset * voice->src.format->nBlockAlign
+	);
+	for (i = 0; i < samples; i += 1, buf += voice->src.format->nBlockAlign)
+	for (j = 0; j < voice->src.format->nChannels; j += 1)
+	{
+		*decodeCache++ = ((int32_t) (
+			((uint32_t) buf[(j * 3) + 2] << 24) |
+			((uint32_t) buf[(j * 3) + 1] << 16) |
+			((uint32_t) buf[(j * 3) + 0] << 8)
+		) >> 8) / 8388607.0f;
+	}
+
+	LOG_FUNC_EXIT(voice->audio)
+}
+
+void FAudio_INTERNAL_DecodePCM32(
+	FAudioVoice *voice,
+	FAudioBuffer *buffer,
+	float *decodeCache,
+	uint32_t samples
+) {
+	LOG_FUNC_ENTER(voice->audio)
+	FAudio_INTERNAL_Convert_S32_To_F32(
+		((int32_t*) buffer->pAudioData) + (
+			voice->src.curBufferOffset * voice->src.format->nChannels
+		),
+		decodeCache,
+		samples * voice->src.format->nChannels
+	);
+	LOG_FUNC_EXIT(voice->audio)
+}
+
+void FAudio_INTERNAL_DecodePCM32F(
+	FAudioVoice *voice,
+	FAudioBuffer *buffer,
+	float *decodeCache,
+	uint32_t samples
+) {
+	LOG_FUNC_ENTER(voice->audio)
+	FAudio_memcpy(
+		decodeCache,
+		((float*) buffer->pAudioData) + (
+			voice->src.curBufferOffset * voice->src.format->nChannels
+		),
+		sizeof(float) * samples * voice->src.format->nChannels
+	);
+	LOG_FUNC_EXIT(voice->audio)
+}
+
+/* MSADPCM Decoding */
+
+static inline int16_t FAudio_INTERNAL_ParseNibble(
+	uint8_t nibble,
+	uint8_t predictor,
+	int16_t *delta,
+	int16_t *sample1,
+	int16_t *sample2
+) {
+	static const int32_t AdaptionTable[16] =
+	{
+		230, 230, 230, 230, 307, 409, 512, 614,
+		768, 614, 512, 409, 307, 230, 230, 230
+	};
+	static const int32_t AdaptCoeff_1[7] =
+	{
+		256, 512, 0, 192, 240, 460, 392
+	};
+	static const int32_t AdaptCoeff_2[7] =
+	{
+		0, -256, 0, 64, 0, -208, -232
+	};
+
+	int8_t signedNibble;
+	int32_t sampleInt;
+	int16_t sample;
+
+	signedNibble = (int8_t) nibble;
+	if (signedNibble & 0x08)
+	{
+		signedNibble -= 0x10;
+	}
+
+	sampleInt = (
+		(*sample1 * AdaptCoeff_1[predictor]) +
+		(*sample2 * AdaptCoeff_2[predictor])
+	) / 256;
+	sampleInt += signedNibble * (*delta);
+	sample = FAudio_clamp(sampleInt, -32768, 32767);
+
+	*sample2 = *sample1;
+	*sample1 = sample;
+	*delta = (int16_t) (AdaptionTable[nibble] * (int32_t) (*delta) / 256);
+	if (*delta < 16)
+	{
+		*delta = 16;
+	}
+	return sample;
+}
+
+#define READ(item, type) \
+	item = *((type*) *buf); \
+	*buf += sizeof(type);
+
+static inline void FAudio_INTERNAL_DecodeMonoMSADPCMBlock(
+	uint8_t **buf,
+	int16_t *blockCache,
+	uint32_t align
+) {
+	uint32_t i;
+
+	/* Temp storage for ADPCM blocks */
+	uint8_t predictor;
+	int16_t delta;
+	int16_t sample1;
+	int16_t sample2;
+
+	/* Preamble */
+	READ(predictor, uint8_t)
+	READ(delta, int16_t)
+	READ(sample1, int16_t)
+	READ(sample2, int16_t)
+	align -= 7;
+
+	/* Samples */
+	*blockCache++ = sample2;
+	*blockCache++ = sample1;
+	for (i = 0; i < align; i += 1, *buf += 1)
+	{
+		*blockCache++ = FAudio_INTERNAL_ParseNibble(
+			*(*buf) >> 4,
+			predictor,
+			&delta,
+			&sample1,
+			&sample2
+		);
+		*blockCache++ = FAudio_INTERNAL_ParseNibble(
+			*(*buf) & 0x0F,
+			predictor,
+			&delta,
+			&sample1,
+			&sample2
+		);
+	}
+}
+
+static inline void FAudio_INTERNAL_DecodeStereoMSADPCMBlock(
+	uint8_t **buf,
+	int16_t *blockCache,
+	uint32_t align
+) {
+	uint32_t i;
+
+	/* Temp storage for ADPCM blocks */
+	uint8_t l_predictor;
+	uint8_t r_predictor;
+	int16_t l_delta;
+	int16_t r_delta;
+	int16_t l_sample1;
+	int16_t r_sample1;
+	int16_t l_sample2;
+	int16_t r_sample2;
+
+	/* Preamble */
+	READ(l_predictor, uint8_t)
+	READ(r_predictor, uint8_t)
+	READ(l_delta, int16_t)
+	READ(r_delta, int16_t)
+	READ(l_sample1, int16_t)
+	READ(r_sample1, int16_t)
+	READ(l_sample2, int16_t)
+	READ(r_sample2, int16_t)
+	align -= 14;
+
+	/* Samples */
+	*blockCache++ = l_sample2;
+	*blockCache++ = r_sample2;
+	*blockCache++ = l_sample1;
+	*blockCache++ = r_sample1;
+	for (i = 0; i < align; i += 1, *buf += 1)
+	{
+		*blockCache++ = FAudio_INTERNAL_ParseNibble(
+			*(*buf) >> 4,
+			l_predictor,
+			&l_delta,
+			&l_sample1,
+			&l_sample2
+		);
+		*blockCache++ = FAudio_INTERNAL_ParseNibble(
+			*(*buf) & 0x0F,
+			r_predictor,
+			&r_delta,
+			&r_sample1,
+			&r_sample2
+		);
+	}
+}
+
+#undef READ
+
+void FAudio_INTERNAL_DecodeMonoMSADPCM(
+	FAudioVoice *voice,
+	FAudioBuffer *buffer,
+	float *decodeCache,
+	uint32_t samples
+) {
+	/* Loop variables */
+	uint32_t copy, done = 0;
+
+	/* Read pointers */
+	uint8_t *buf;
+	int32_t midOffset;
+
+	/* PCM block cache */
+	int16_t blockCache[1012]; /* Max block size */
+
+	/* Block size */
+	uint32_t bsize = ((FAudioADPCMWaveFormat*) voice->src.format)->wSamplesPerBlock;
+
+	LOG_FUNC_ENTER(voice->audio)
+
+	/* Where are we starting? */
+	buf = (uint8_t*) buffer->pAudioData + (
+		(voice->src.curBufferOffset / bsize) *
+		voice->src.format->nBlockAlign
+	);
+
+	/* Are we starting in the middle? */
+	midOffset = (voice->src.curBufferOffset % bsize);
+
+	/* Read in each block directly to the decode cache */
+	while (done < samples)
+	{
+		copy = FAudio_min(samples - done, bsize - midOffset);
+		FAudio_INTERNAL_DecodeMonoMSADPCMBlock(
+			&buf,
+			blockCache,
+			voice->src.format->nBlockAlign
+		);
+		FAudio_INTERNAL_Convert_S16_To_F32(
+			blockCache + midOffset,
+			decodeCache,
+			copy
+		);
+		decodeCache += copy;
+		done += copy;
+		midOffset = 0;
+	}
+	LOG_FUNC_EXIT(voice->audio)
+}
+
+void FAudio_INTERNAL_DecodeStereoMSADPCM(
+	FAudioVoice *voice,
+	FAudioBuffer *buffer,
+	float *decodeCache,
+	uint32_t samples
+) {
+	/* Loop variables */
+	uint32_t copy, done = 0;
+
+	/* Read pointers */
+	uint8_t *buf;
+	int32_t midOffset;
+
+	/* PCM block cache */
+	int16_t blockCache[2024]; /* Max block size */
+
+	/* Align, block size */
+	uint32_t bsize = ((FAudioADPCMWaveFormat*) voice->src.format)->wSamplesPerBlock;
+
+	LOG_FUNC_ENTER(voice->audio)
+
+	/* Where are we starting? */
+	buf = (uint8_t*) buffer->pAudioData + (
+		(voice->src.curBufferOffset / bsize) *
+		voice->src.format->nBlockAlign
+	);
+
+	/* Are we starting in the middle? */
+	midOffset = (voice->src.curBufferOffset % bsize);
+
+	/* Read in each block directly to the decode cache */
+	while (done < samples)
+	{
+		copy = FAudio_min(samples - done, bsize - midOffset);
+		FAudio_INTERNAL_DecodeStereoMSADPCMBlock(
+			&buf,
+			blockCache,
+			voice->src.format->nBlockAlign
+		);
+		FAudio_INTERNAL_Convert_S16_To_F32(
+			blockCache + (midOffset * 2),
+			decodeCache,
+			copy * 2
+		);
+		decodeCache += copy * 2;
+		done += copy;
+		midOffset = 0;
+	}
+	LOG_FUNC_EXIT(voice->audio)
+}
+
+/* Fallback WMA decoder, get ready for spam! */
+
+void FAudio_INTERNAL_DecodeWMAERROR(
+	FAudioVoice *voice,
+	FAudioBuffer *buffer,
+	float *decodeCache,
+	uint32_t samples
+) {
+	LOG_FUNC_ENTER(voice->audio)
+	LOG_ERROR(voice->audio, "%s", "WMA IS NOT SUPPORTED IN THIS BUILD!")
+	FAudio_zero(decodeCache, samples * voice->src.format->nChannels * sizeof(float));
+	LOG_FUNC_EXIT(voice->audio)
+}
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FAudio_internal.h b/libs/faudio/src/FAudio_internal.h
new file mode 100644
index 00000000000..11cee11de24
--- /dev/null
+++ b/libs/faudio/src/FAudio_internal.h
@@ -0,0 +1,913 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "FAudio.h"
+#include "FAPOBase.h"
+#include <stdarg.h>
+
+#ifdef FAUDIO_WIN32_PLATFORM
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include <math.h>
+#include <assert.h>
+#include <inttypes.h>
+
+#include <windef.h>
+#include <winbase.h>
+
+#define FAudio_malloc malloc
+#define FAudio_realloc realloc
+#define FAudio_free free
+#define FAudio_alloca(x) alloca(x)
+#define FAudio_dealloca(x) (void)(x)
+#define FAudio_zero(ptr, size) memset(ptr, '\0', size)
+#define FAudio_memset(ptr, val, size) memset(ptr, val, size)
+#define FAudio_memcpy(dst, src, size) memcpy(dst, src, size)
+#define FAudio_memmove(dst, src, size) memmove(dst, src, size)
+#define FAudio_memcmp(ptr1, ptr2, size) memcmp(ptr1, ptr2, size)
+
+#define FAudio_strlen(ptr) strlen(ptr)
+#define FAudio_strcmp(str1, str2) strcmp(str1, str2)
+#define FAudio_strncmp(str1, str2, size) strncmp(str1, str2, size)
+#define FAudio_strlcpy(ptr1, ptr2, size) lstrcpynA(ptr1, ptr2, size)
+
+#define FAudio_pow(x, y) pow(x, y)
+#define FAudio_log(x) log(x)
+#define FAudio_log10(x) log10(x)
+#define FAudio_sin(x) sin(x)
+#define FAudio_cos(x) cos(x)
+#define FAudio_tan(x) tan(x)
+#define FAudio_acos(x) acos(x)
+#define FAudio_ceil(x) ceil(x)
+#define FAudio_floor(x) floor(x)
+#define FAudio_abs(x) abs(x)
+#define FAudio_ldexp(v, e) ldexp(v, e)
+#define FAudio_exp(x) exp(x)
+
+#define FAudio_cosf(x) cosf(x)
+#define FAudio_sinf(x) sinf(x)
+#define FAudio_sqrtf(x) sqrtf(x)
+#define FAudio_acosf(x) acosf(x)
+#define FAudio_atan2f(y, x) atan2f(y, x)
+#define FAudio_fabsf(x) fabsf(x)
+
+#define FAudio_qsort qsort
+
+#define FAudio_assert assert
+#define FAudio_snprintf snprintf
+#define FAudio_vsnprintf vsnprintf
+#define FAudio_getenv getenv
+#define FAudio_PRIu64 PRIu64
+#define FAudio_PRIx64 PRIx64
+
+extern void FAudio_Log(char const *msg);
+
+/* FIXME: Assuming little-endian! */
+#define FAudio_swap16LE(x) (x)
+#define FAudio_swap16BE(x) \
+	((x >> 8)	& 0x00FF) | \
+	((x << 8)	& 0xFF00)
+#define FAudio_swap32LE(x) (x)
+#define FAudio_swap32BE(x) \
+	((x >> 24)	& 0x000000FF) | \
+	((x >> 8)	& 0x0000FF00) | \
+	((x << 8)	& 0x00FF0000) | \
+	((x << 24)	& 0xFF000000)
+#define FAudio_swap64LE(x) (x)
+#define FAudio_swap64BE(x) \
+	((x >> 32)	& 0x00000000000000FF) | \
+	((x >> 24)	& 0x000000000000FF00) | \
+	((x >> 16)	& 0x0000000000FF0000) | \
+	((x >> 8)	& 0x00000000FF000000) | \
+	((x << 8)	& 0x000000FF00000000) | \
+	((x << 16)	& 0x0000FF0000000000) | \
+	((x << 24)	& 0x00FF000000000000) | \
+	((x << 32)	& 0xFF00000000000000)
+#else
+#include <SDL_stdinc.h>
+#include <SDL_assert.h>
+#include <SDL_endian.h>
+#include <SDL_log.h>
+
+#define FAudio_malloc SDL_malloc
+#define FAudio_realloc SDL_realloc
+#define FAudio_free SDL_free
+#define FAudio_alloca(x) SDL_stack_alloc(uint8_t, x)
+#define FAudio_dealloca(x) SDL_stack_free(x)
+#define FAudio_zero(ptr, size) SDL_memset(ptr, '\0', size)
+#define FAudio_memset(ptr, val, size) SDL_memset(ptr, val, size)
+#define FAudio_memcpy(dst, src, size) SDL_memcpy(dst, src, size)
+#define FAudio_memmove(dst, src, size) SDL_memmove(dst, src, size)
+#define FAudio_memcmp(ptr1, ptr2, size) SDL_memcmp(ptr1, ptr2, size)
+
+#define FAudio_strlen(ptr) SDL_strlen(ptr)
+#define FAudio_strcmp(str1, str2) SDL_strcmp(str1, str2)
+#define FAudio_strncmp(str1, str2, size) SDL_strncmp(str1, str1, size)
+#define FAudio_strlcpy(ptr1, ptr2, size) SDL_strlcpy(ptr1, ptr2, size)
+
+#define FAudio_pow(x, y) SDL_pow(x, y)
+#define FAudio_log(x) SDL_log(x)
+#define FAudio_log10(x) SDL_log10(x)
+#define FAudio_sin(x) SDL_sin(x)
+#define FAudio_cos(x) SDL_cos(x)
+#define FAudio_tan(x) SDL_tan(x)
+#define FAudio_acos(x) SDL_acos(x)
+#define FAudio_ceil(x) SDL_ceil(x)
+#define FAudio_floor(x) SDL_floor(x)
+#define FAudio_abs(x) SDL_abs(x)
+#define FAudio_ldexp(v, e) SDL_scalbn(v, e)
+#define FAudio_exp(x) SDL_exp(x)
+
+#define FAudio_cosf(x) SDL_cosf(x)
+#define FAudio_sinf(x) SDL_sinf(x)
+#define FAudio_sqrtf(x) SDL_sqrtf(x)
+#define FAudio_acosf(x) SDL_acosf(x)
+#define FAudio_atan2f(y, x) SDL_atan2f(y, x)
+#define FAudio_fabsf(x) SDL_fabsf(x)
+
+#define FAudio_qsort SDL_qsort
+
+#ifdef FAUDIO_LOG_ASSERTIONS
+#define FAudio_assert(condition) \
+	{ \
+		static uint8_t logged = 0; \
+		if (!(condition) && !logged) \
+		{ \
+			SDL_Log("Assertion failed: %s", #condition); \
+			logged = 1; \
+		} \
+	}
+#else
+#define FAudio_assert SDL_assert
+#endif
+#define FAudio_snprintf SDL_snprintf
+#define FAudio_vsnprintf SDL_vsnprintf
+#define FAudio_Log(msg) SDL_Log("%s", msg)
+#define FAudio_getenv SDL_getenv
+#define FAudio_PRIu64 SDL_PRIu64
+#define FAudio_PRIx64 SDL_PRIx64
+
+#define FAudio_swap16LE(x) SDL_SwapLE16(x)
+#define FAudio_swap16BE(x) SDL_SwapBE16(x)
+#define FAudio_swap32LE(x) SDL_SwapLE32(x)
+#define FAudio_swap32BE(x) SDL_SwapBE32(x)
+#define FAudio_swap64LE(x) SDL_SwapLE64(x)
+#define FAudio_swap64BE(x) SDL_SwapBE64(x)
+#endif
+
+/* Easy Macros */
+#define FAudio_min(val1, val2) \
+	(val1 < val2 ? val1 : val2)
+#define FAudio_max(val1, val2) \
+	(val1 > val2 ? val1 : val2)
+#define FAudio_clamp(val, min, max) \
+	(val > max ? max : (val < min ? min : val))
+
+/* Windows/Visual Studio cruft */
+#ifdef _WIN32
+	#ifdef __cplusplus
+		/* C++ should have `inline`, but not `restrict` */
+		#define restrict
+	#else
+		#define inline __inline
+		#if defined(_MSC_VER)
+			#if (_MSC_VER >= 1700) /* VS2012+ */
+				#define restrict __restrict
+			#else /* VS2010- */
+				#define restrict
+			#endif
+		#else
+			#define restrict
+		#endif
+	#endif
+#endif
+
+/* C++ does not have restrict (though VS2012+ does have __restrict) */
+#if defined(__cplusplus) && !defined(restrict)
+#define restrict
+#endif
+
+/* Threading Types */
+
+typedef void* FAudioThread;
+typedef void* FAudioMutex;
+typedef int32_t (FAUDIOCALL * FAudioThreadFunc)(void* data);
+typedef enum FAudioThreadPriority
+{
+	FAUDIO_THREAD_PRIORITY_LOW,
+	FAUDIO_THREAD_PRIORITY_NORMAL,
+	FAUDIO_THREAD_PRIORITY_HIGH,
+} FAudioThreadPriority;
+
+/* Linked Lists */
+
+typedef struct LinkedList LinkedList;
+struct LinkedList
+{
+	void* entry;
+	LinkedList *next;
+};
+void LinkedList_AddEntry(
+	LinkedList **start,
+	void* toAdd,
+	FAudioMutex lock,
+	FAudioMallocFunc pMalloc
+);
+void LinkedList_PrependEntry(
+	LinkedList **start,
+	void* toAdd,
+	FAudioMutex lock,
+	FAudioMallocFunc pMalloc
+);
+void LinkedList_RemoveEntry(
+	LinkedList **start,
+	void* toRemove,
+	FAudioMutex lock,
+	FAudioFreeFunc pFree
+);
+
+/* Internal FAudio Types */
+
+typedef enum FAudioVoiceType
+{
+	FAUDIO_VOICE_SOURCE,
+	FAUDIO_VOICE_SUBMIX,
+	FAUDIO_VOICE_MASTER
+} FAudioVoiceType;
+
+typedef struct FAudioBufferEntry FAudioBufferEntry;
+struct FAudioBufferEntry
+{
+	FAudioBuffer buffer;
+	FAudioBufferWMA bufferWMA;
+	FAudioBufferEntry *next;
+};
+
+typedef void (FAUDIOCALL * FAudioDecodeCallback)(
+	FAudioVoice *voice,
+	FAudioBuffer *buffer,	/* Buffer to decode */
+	float *decodeCache,	/* Decode into here */
+	uint32_t samples	/* Samples to decode */
+);
+
+typedef void (FAUDIOCALL * FAudioResampleCallback)(
+	float *restrict dCache,
+	float *restrict resampleCache,
+	uint64_t *resampleOffset,
+	uint64_t resampleStep,
+	uint64_t toResample,
+	uint8_t channels
+);
+
+typedef void (FAUDIOCALL * FAudioMixCallback)(
+	uint32_t toMix,
+	uint32_t srcChans,
+	uint32_t dstChans,
+	float *restrict srcData,
+	float *restrict dstData,
+	float *restrict coefficients
+);
+
+typedef float FAudioFilterState[4];
+
+/* Operation Sets, original implementation by Tyler Glaiel */
+
+typedef struct FAudio_OPERATIONSET_Operation FAudio_OPERATIONSET_Operation;
+
+void FAudio_OPERATIONSET_Commit(FAudio *audio, uint32_t OperationSet);
+void FAudio_OPERATIONSET_CommitAll(FAudio *audio);
+void FAudio_OPERATIONSET_Execute(FAudio *audio);
+
+void FAudio_OPERATIONSET_ClearAll(FAudio *audio);
+void FAudio_OPERATIONSET_ClearAllForVoice(FAudioVoice *voice);
+
+void FAudio_OPERATIONSET_QueueEnableEffect(
+	FAudioVoice *voice,
+	uint32_t EffectIndex,
+	uint32_t OperationSet
+);
+void FAudio_OPERATIONSET_QueueDisableEffect(
+	FAudioVoice *voice,
+	uint32_t EffectIndex,
+	uint32_t OperationSet
+);
+void FAudio_OPERATIONSET_QueueSetEffectParameters(
+	FAudioVoice *voice,
+	uint32_t EffectIndex,
+	const void *pParameters,
+	uint32_t ParametersByteSize,
+	uint32_t OperationSet
+);
+void FAudio_OPERATIONSET_QueueSetFilterParameters(
+	FAudioVoice *voice,
+	const FAudioFilterParameters *pParameters,
+	uint32_t OperationSet
+);
+void FAudio_OPERATIONSET_QueueSetOutputFilterParameters(
+	FAudioVoice *voice,
+	FAudioVoice *pDestinationVoice,
+	const FAudioFilterParameters *pParameters,
+	uint32_t OperationSet
+);
+void FAudio_OPERATIONSET_QueueSetVolume(
+	FAudioVoice *voice,
+	float Volume,
+	uint32_t OperationSet
+);
+void FAudio_OPERATIONSET_QueueSetChannelVolumes(
+	FAudioVoice *voice,
+	uint32_t Channels,
+	const float *pVolumes,
+	uint32_t OperationSet
+);
+void FAudio_OPERATIONSET_QueueSetOutputMatrix(
+	FAudioVoice *voice,
+	FAudioVoice *pDestinationVoice,
+	uint32_t SourceChannels,
+	uint32_t DestinationChannels,
+	const float *pLevelMatrix,
+	uint32_t OperationSet
+);
+void FAudio_OPERATIONSET_QueueStart(
+	FAudioSourceVoice *voice,
+	uint32_t Flags,
+	uint32_t OperationSet
+);
+void FAudio_OPERATIONSET_QueueStop(
+	FAudioSourceVoice *voice,
+	uint32_t Flags,
+	uint32_t OperationSet
+);
+void FAudio_OPERATIONSET_QueueExitLoop(
+	FAudioSourceVoice *voice,
+	uint32_t OperationSet
+);
+void FAudio_OPERATIONSET_QueueSetFrequencyRatio(
+	FAudioSourceVoice *voice,
+	float Ratio,
+	uint32_t OperationSet
+);
+
+/* Public FAudio Types */
+
+struct FAudio
+{
+	uint8_t version;
+	uint8_t active;
+	uint32_t refcount;
+	uint32_t initFlags;
+	uint32_t updateSize;
+	FAudioMasteringVoice *master;
+	LinkedList *sources;
+	LinkedList *submixes;
+	LinkedList *callbacks;
+	FAudioMutex sourceLock;
+	FAudioMutex submixLock;
+	FAudioMutex callbackLock;
+	FAudioMutex operationLock;
+	FAudioWaveFormatExtensible mixFormat;
+
+	FAudio_OPERATIONSET_Operation *queuedOperations;
+	FAudio_OPERATIONSET_Operation *committedOperations;
+
+	/* Used to prevent destroying an active voice */
+	FAudioSourceVoice *processingSource;
+
+	/* Temp storage for processing, interleaved PCM32F */
+	#define EXTRA_DECODE_PADDING 2
+	uint32_t decodeSamples;
+	uint32_t resampleSamples;
+	uint32_t effectChainSamples;
+	float *decodeCache;
+	float *resampleCache;
+	float *effectChainCache;
+
+	/* Allocator callbacks */
+	FAudioMallocFunc pMalloc;
+	FAudioFreeFunc pFree;
+	FAudioReallocFunc pRealloc;
+
+	/* EngineProcedureEXT */
+	void *clientEngineUser;
+	FAudioEngineProcedureEXT pClientEngineProc;
+
+#ifndef FAUDIO_DISABLE_DEBUGCONFIGURATION
+	/* Debug Information */
+	FAudioDebugConfiguration debug;
+#endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */
+
+	/* Platform opaque pointer */
+	void *platform;
+};
+
+struct FAudioVoice
+{
+	FAudio *audio;
+	uint32_t flags;
+	FAudioVoiceType type;
+
+	FAudioVoiceSends sends;
+	float **sendCoefficients;
+	float **mixCoefficients;
+	FAudioMixCallback *sendMix;
+	FAudioFilterParameters *sendFilter;
+	FAudioFilterState **sendFilterState;
+	struct
+	{
+		FAPOBufferFlags state;
+		uint32_t count;
+		FAudioEffectDescriptor *desc;
+		void **parameters;
+		uint32_t *parameterSizes;
+		uint8_t *parameterUpdates;
+		uint8_t *inPlaceProcessing;
+	} effects;
+	FAudioFilterParameters filter;
+	FAudioFilterState *filterState;
+	FAudioMutex sendLock;
+	FAudioMutex effectLock;
+	FAudioMutex filterLock;
+
+	float volume;
+	float *channelVolume;
+	uint32_t outputChannels;
+	FAudioMutex volumeLock;
+
+	FAUDIONAMELESS union
+	{
+		struct
+		{
+			/* Sample storage */
+			uint32_t decodeSamples;
+			uint32_t resampleSamples;
+
+			/* Resampler */
+			float resampleFreq;
+			uint64_t resampleStep;
+			uint64_t resampleOffset;
+			uint64_t curBufferOffsetDec;
+			uint32_t curBufferOffset;
+
+			/* WMA decoding */
+#ifdef HAVE_WMADEC
+			struct FAudioWMADEC *wmadec;
+#endif /* HAVE_WMADEC*/
+
+			/* Read-only */
+			float maxFreqRatio;
+			FAudioWaveFormatEx *format;
+			FAudioDecodeCallback decode;
+			FAudioResampleCallback resample;
+			FAudioVoiceCallback *callback;
+
+			/* Dynamic */
+			uint8_t active;
+			float freqRatio;
+			uint8_t newBuffer;
+			uint64_t totalSamples;
+			FAudioBufferEntry *bufferList;
+			FAudioBufferEntry *flushList;
+			FAudioMutex bufferLock;
+		} src;
+		struct
+		{
+			/* Sample storage */
+			uint32_t inputSamples;
+			uint32_t outputSamples;
+			float *inputCache;
+			uint64_t resampleStep;
+			FAudioResampleCallback resample;
+
+			/* Read-only */
+			uint32_t inputChannels;
+			uint32_t inputSampleRate;
+			uint32_t processingStage;
+		} mix;
+		struct
+		{
+			/* Output stream, allocated by Platform */
+			float *output;
+
+			/* Needed when inputChannels != outputChannels */
+			float *effectCache;
+
+			/* Read-only */
+			uint32_t inputChannels;
+			uint32_t inputSampleRate;
+		} master;
+	};
+};
+
+/* Internal Functions */
+void FAudio_INTERNAL_InsertSubmixSorted(
+	LinkedList **start,
+	FAudioSubmixVoice *toAdd,
+	FAudioMutex lock,
+	FAudioMallocFunc pMalloc
+);
+void FAudio_INTERNAL_UpdateEngine(FAudio *audio, float *output);
+void FAudio_INTERNAL_ResizeDecodeCache(FAudio *audio, uint32_t size);
+void FAudio_INTERNAL_AllocEffectChain(
+	FAudioVoice *voice,
+	const FAudioEffectChain *pEffectChain
+);
+void FAudio_INTERNAL_FreeEffectChain(FAudioVoice *voice);
+uint32_t FAudio_INTERNAL_VoiceOutputFrequency(
+	FAudioVoice *voice,
+	const FAudioVoiceSends *pSendList
+);
+extern const float FAUDIO_INTERNAL_MATRIX_DEFAULTS[8][8][64];
+
+/* Debug */
+
+#ifdef FAUDIO_DISABLE_DEBUGCONFIGURATION
+
+#define LOG_ERROR(engine, fmt, ...)
+#define LOG_WARNING(engine, fmt, ...)
+#define LOG_INFO(engine, fmt, ...)
+#define LOG_DETAIL(engine, fmt, ...)
+#define LOG_API_ENTER(engine)
+#define LOG_API_EXIT(engine)
+#define LOG_FUNC_ENTER(engine)
+#define LOG_FUNC_EXIT(engine)
+/* TODO: LOG_TIMING */
+#define LOG_MUTEX_CREATE(engine, mutex)
+#define LOG_MUTEX_DESTROY(engine, mutex)
+#define LOG_MUTEX_LOCK(engine, mutex)
+#define LOG_MUTEX_UNLOCK(engine, mutex)
+/* TODO: LOG_MEMORY */
+/* TODO: LOG_STREAMING */
+
+#define LOG_FORMAT(engine, waveFormat)
+
+#else
+
+#if defined(_MSC_VER)
+/* VC doesn't support __attribute__ at all, and there's no replacement for format. */
+void WINAPIV FAudio_INTERNAL_debug(
+	FAudio *audio,
+	const char *file,
+	uint32_t line,
+	const char *func,
+	const char *fmt,
+	...
+);
+#if _MSC_VER <= 1700 /* <=2012 also doesn't support __func__ */
+#define __func__ __FUNCTION__
+#endif
+#else
+void WINAPIV FAudio_INTERNAL_debug(
+	FAudio *audio,
+	const char *file,
+	uint32_t line,
+	const char *func,
+	const char *fmt,
+	...
+) __attribute__((format(printf,5,6)));
+#endif
+void FAudio_INTERNAL_debug_fmt(
+	FAudio *audio,
+	const char *file,
+	uint32_t line,
+	const char *func,
+	const FAudioWaveFormatEx *fmt
+);
+
+#define PRINT_DEBUG(engine, cond, type, fmt, ...) \
+	if (engine->debug.TraceMask & FAUDIO_LOG_##cond) \
+	{ \
+		FAudio_INTERNAL_debug( \
+			engine, \
+			__FILE__, \
+			__LINE__, \
+			__func__, \
+			type ": " fmt, \
+			__VA_ARGS__ \
+		); \
+	}
+
+#define LOG_ERROR(engine, fmt, ...) PRINT_DEBUG(engine, ERRORS, "ERROR", fmt, __VA_ARGS__)
+#define LOG_WARNING(engine, fmt, ...) PRINT_DEBUG(engine, WARNINGS, "WARNING", fmt, __VA_ARGS__)
+#define LOG_INFO(engine, fmt, ...) PRINT_DEBUG(engine, INFO, "INFO", fmt, __VA_ARGS__)
+#define LOG_DETAIL(engine, fmt, ...) PRINT_DEBUG(engine, DETAIL, "DETAIL", fmt, __VA_ARGS__)
+#define LOG_API_ENTER(engine) PRINT_DEBUG(engine, API_CALLS, "API Enter", "%s", __func__)
+#define LOG_API_EXIT(engine) PRINT_DEBUG(engine, API_CALLS, "API Exit", "%s", __func__)
+#define LOG_FUNC_ENTER(engine) PRINT_DEBUG(engine, FUNC_CALLS, "FUNC Enter", "%s", __func__)
+#define LOG_FUNC_EXIT(engine) PRINT_DEBUG(engine, FUNC_CALLS, "FUNC Exit", "%s", __func__)
+/* TODO: LOG_TIMING */
+#define LOG_MUTEX_CREATE(engine, mutex) PRINT_DEBUG(engine, LOCKS, "Mutex Create", "%p", mutex)
+#define LOG_MUTEX_DESTROY(engine, mutex) PRINT_DEBUG(engine, LOCKS, "Mutex Destroy", "%p", mutex)
+#define LOG_MUTEX_LOCK(engine, mutex) PRINT_DEBUG(engine, LOCKS, "Mutex Lock", "%p", mutex)
+#define LOG_MUTEX_UNLOCK(engine, mutex) PRINT_DEBUG(engine, LOCKS, "Mutex Unlock", "%p", mutex)
+/* TODO: LOG_MEMORY */
+/* TODO: LOG_STREAMING */
+
+#define LOG_FORMAT(engine, waveFormat) \
+	if (engine->debug.TraceMask & FAUDIO_LOG_INFO) \
+	{ \
+		FAudio_INTERNAL_debug_fmt( \
+			engine, \
+			__FILE__, \
+			__LINE__, \
+			__func__, \
+			waveFormat \
+		); \
+	}
+
+#endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */
+
+/* FAPOFX Creators */
+
+#define CREATE_FAPOFX_FUNC(effect) \
+	extern uint32_t FAPOFXCreate##effect( \
+		FAPO **pEffect, \
+		const void *pInitData, \
+		uint32_t InitDataByteSize, \
+		FAudioMallocFunc customMalloc, \
+		FAudioFreeFunc customFree, \
+		FAudioReallocFunc customRealloc, \
+		uint8_t legacy \
+	);
+CREATE_FAPOFX_FUNC(EQ)
+CREATE_FAPOFX_FUNC(MasteringLimiter)
+CREATE_FAPOFX_FUNC(Reverb)
+CREATE_FAPOFX_FUNC(Echo)
+#undef CREATE_FAPOFX_FUNC
+
+/* SIMD Stuff */
+
+/* Callbacks declared as functions (rather than function pointers) are
+ * scalar-only, for now. SIMD versions should be possible for these.
+ */
+
+extern void (*FAudio_INTERNAL_Convert_U8_To_F32)(
+	const uint8_t *restrict src,
+	float *restrict dst,
+	uint32_t len
+);
+extern void (*FAudio_INTERNAL_Convert_S16_To_F32)(
+	const int16_t *restrict src,
+	float *restrict dst,
+	uint32_t len
+);
+extern void (*FAudio_INTERNAL_Convert_S32_To_F32)(
+	const int32_t *restrict src,
+	float *restrict dst,
+	uint32_t len
+);
+
+extern FAudioResampleCallback FAudio_INTERNAL_ResampleMono;
+extern FAudioResampleCallback FAudio_INTERNAL_ResampleStereo;
+extern void FAudio_INTERNAL_ResampleGeneric(
+	float *restrict dCache,
+	float *restrict resampleCache,
+	uint64_t *resampleOffset,
+	uint64_t resampleStep,
+	uint64_t toResample,
+	uint8_t channels
+);
+
+extern void (*FAudio_INTERNAL_Amplify)(
+	float *output,
+	uint32_t totalSamples,
+	float volume
+);
+
+extern FAudioMixCallback FAudio_INTERNAL_Mix_Generic;
+
+#define MIX_FUNC(type) \
+	extern void FAudio_INTERNAL_Mix_##type##_Scalar( \
+		uint32_t toMix, \
+		uint32_t srcChans, \
+		uint32_t dstChans, \
+		float *restrict srcData, \
+		float *restrict dstData, \
+		float *restrict coefficients \
+	);
+MIX_FUNC(Generic)
+MIX_FUNC(1in_1out)
+MIX_FUNC(1in_2out)
+MIX_FUNC(1in_6out)
+MIX_FUNC(1in_8out)
+MIX_FUNC(2in_1out)
+MIX_FUNC(2in_2out)
+MIX_FUNC(2in_6out)
+MIX_FUNC(2in_8out)
+#undef MIX_FUNC
+
+void FAudio_INTERNAL_InitSIMDFunctions(uint8_t hasSSE2, uint8_t hasNEON);
+
+/* Decoders */
+
+#define DECODE_FUNC(type) \
+	extern void FAudio_INTERNAL_Decode##type( \
+		FAudioVoice *voice, \
+		FAudioBuffer *buffer, \
+		float *decodeCache, \
+		uint32_t samples \
+	);
+DECODE_FUNC(PCM8)
+DECODE_FUNC(PCM16)
+DECODE_FUNC(PCM24)
+DECODE_FUNC(PCM32)
+DECODE_FUNC(PCM32F)
+DECODE_FUNC(MonoMSADPCM)
+DECODE_FUNC(StereoMSADPCM)
+DECODE_FUNC(WMAERROR)
+#undef DECODE_FUNC
+
+/* WMA decoding */
+
+#ifdef HAVE_WMADEC
+uint32_t FAudio_WMADEC_init(FAudioSourceVoice *pSourceVoice, uint32_t type);
+void FAudio_WMADEC_free(FAudioSourceVoice *voice);
+void FAudio_WMADEC_end_buffer(FAudioSourceVoice *voice);
+#endif /* HAVE_WMADEC */
+
+/* Platform Functions */
+
+void FAudio_PlatformAddRef(void);
+void FAudio_PlatformRelease(void);
+void FAudio_PlatformInit(
+	FAudio *audio,
+	uint32_t flags,
+	uint32_t deviceIndex,
+	FAudioWaveFormatExtensible *mixFormat,
+	uint32_t *updateSize,
+	void** platformDevice
+);
+void FAudio_PlatformQuit(void* platformDevice);
+
+uint32_t FAudio_PlatformGetDeviceCount(void);
+uint32_t FAudio_PlatformGetDeviceDetails(
+	uint32_t index,
+	FAudioDeviceDetails *details
+);
+
+/* Threading */
+
+FAudioThread FAudio_PlatformCreateThread(
+	FAudioThreadFunc func,
+	const char *name,
+	void* data
+);
+void FAudio_PlatformWaitThread(FAudioThread thread, int32_t *retval);
+void FAudio_PlatformThreadPriority(FAudioThreadPriority priority);
+uint64_t FAudio_PlatformGetThreadID(void);
+FAudioMutex FAudio_PlatformCreateMutex(void);
+void FAudio_PlatformDestroyMutex(FAudioMutex mutex);
+void FAudio_PlatformLockMutex(FAudioMutex mutex);
+void FAudio_PlatformUnlockMutex(FAudioMutex mutex);
+void FAudio_sleep(uint32_t ms);
+
+/* Time */
+
+uint32_t FAudio_timems(void);
+
+/* WaveFormatExtensible Helpers */
+
+static inline uint32_t GetMask(uint16_t channels)
+{
+	if (channels == 1) return SPEAKER_MONO;
+	if (channels == 2) return SPEAKER_STEREO;
+	if (channels == 3) return SPEAKER_2POINT1;
+	if (channels == 4) return SPEAKER_QUAD;
+	if (channels == 5) return SPEAKER_4POINT1;
+	if (channels == 6) return SPEAKER_5POINT1;
+	if (channels == 8) return SPEAKER_7POINT1;
+	FAudio_assert(0 && "Unrecognized speaker layout!");
+	return 0;
+}
+
+static inline void WriteWaveFormatExtensible(
+	FAudioWaveFormatExtensible *fmt,
+	int channels,
+	int samplerate,
+	const FAudioGUID *subformat
+) {
+	FAudio_assert(fmt != NULL);
+	fmt->Format.wBitsPerSample = 32;
+	fmt->Format.wFormatTag = FAUDIO_FORMAT_EXTENSIBLE;
+	fmt->Format.nChannels = channels;
+	fmt->Format.nSamplesPerSec = samplerate;
+	fmt->Format.nBlockAlign = (
+		fmt->Format.nChannels *
+		(fmt->Format.wBitsPerSample / 8)
+	);
+	fmt->Format.nAvgBytesPerSec = (
+		fmt->Format.nSamplesPerSec *
+		fmt->Format.nBlockAlign
+	);
+	fmt->Format.cbSize = sizeof(FAudioWaveFormatExtensible) - sizeof(FAudioWaveFormatEx);
+	fmt->Samples.wValidBitsPerSample = 32;
+	fmt->dwChannelMask = GetMask(fmt->Format.nChannels);
+	FAudio_memcpy(&fmt->SubFormat, subformat, sizeof(FAudioGUID));
+}
+
+/* Resampling */
+
+/* Okay, so here's what all this fixed-point goo is for:
+ *
+ * Inevitably you're going to run into weird sample rates,
+ * both from WaveBank data and from pitch shifting changes.
+ *
+ * How we deal with this is by calculating a fixed "step"
+ * value that steps from sample to sample at the speed needed
+ * to get the correct output sample rate, and the offset
+ * is stored as separate integer and fraction values.
+ *
+ * This allows us to do weird fractional steps between samples,
+ * while at the same time not letting it drift off into death
+ * thanks to floating point madness.
+ *
+ * Steps are stored in fixed-point with 32 bits for the fraction:
+ *
+ * 00000000000000000000000000000000 00000000000000000000000000000000
+ * ^ Integer block (32)             ^ Fraction block (32)
+ *
+ * For example, to get 1.5:
+ * 00000000000000000000000000000001 10000000000000000000000000000000
+ *
+ * The Integer block works exactly like you'd expect.
+ * The Fraction block is divided by the Integer's "One" value.
+ * So, the above Fraction represented visually...
+ *   1 << 31
+ *   -------
+ *   1 << 32
+ * ... which, simplified, is...
+ *   1 << 0
+ *   ------
+ *   1 << 1
+ * ... in other words, 1 / 2, or 0.5.
+ */
+#define FIXED_PRECISION		32
+#define FIXED_ONE		(1LL << FIXED_PRECISION)
+
+/* Quick way to drop parts */
+#define FIXED_FRACTION_MASK	(FIXED_ONE - 1)
+#define FIXED_INTEGER_MASK	~FIXED_FRACTION_MASK
+
+/* Helper macros to convert fixed to float */
+#define DOUBLE_TO_FIXED(dbl) \
+	((uint64_t) (dbl * FIXED_ONE + 0.5))
+#define FIXED_TO_DOUBLE(fxd) ( \
+	(double) (fxd >> FIXED_PRECISION) + /* Integer part */ \
+	((fxd & FIXED_FRACTION_MASK) * (1.0 / FIXED_ONE)) /* Fraction part */ \
+)
+#define FIXED_TO_FLOAT(fxd) ( \
+	(float) (fxd >> FIXED_PRECISION) + /* Integer part */ \
+	((fxd & FIXED_FRACTION_MASK) * (1.0f / FIXED_ONE)) /* Fraction part */ \
+)
+
+#ifdef FAUDIO_DUMP_VOICES
+/* File writing structure */
+typedef size_t (FAUDIOCALL * FAudio_writefunc)(
+	void *data,
+	const void *src,
+	size_t size,
+	size_t count
+);
+typedef size_t (FAUDIOCALL * FAudio_sizefunc)(
+	void *data
+);
+typedef struct FAudioIOStreamOut
+{
+	void *data;
+	FAudio_readfunc read;
+	FAudio_writefunc write;
+	FAudio_seekfunc seek;
+	FAudio_sizefunc size;
+	FAudio_closefunc close;
+	void *lock;
+} FAudioIOStreamOut;
+
+FAudioIOStreamOut* FAudio_fopen_out(const char *path, const char *mode);
+void FAudio_close_out(FAudioIOStreamOut *io);
+#endif /* FAUDIO_DUMP_VOICES */
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FAudio_internal_simd.c b/libs/faudio/src/FAudio_internal_simd.c
new file mode 100644
index 00000000000..8746d358cad
--- /dev/null
+++ b/libs/faudio/src/FAudio_internal_simd.c
@@ -0,0 +1,1616 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#include "FAudio_internal.h"
+
+/* SECTION 0: SSE/NEON Detection */
+
+/* The SSE/NEON detection comes from MojoAL:
+ * https://hg.icculus.org/icculus/mojoAL/file/default/mojoal.c
+ */
+
+#if defined(__x86_64__) || defined(_M_X64)
+	/* Some platforms fail to define this... */
+	#ifndef __SSE2__
+	#define __SSE2__ 1
+	#endif
+
+	/* x86_64 guarantees SSE2. */
+	#define NEED_SCALAR_CONVERTER_FALLBACKS 0
+#elif defined(__aarch64__) || defined(_M_ARM64)
+	/* Some platforms fail to define this... */
+	#ifndef __ARM_NEON__
+	#define __ARM_NEON__ 1
+	#endif
+
+	/* AArch64 guarantees NEON. */
+	#define NEED_SCALAR_CONVERTER_FALLBACKS 0
+#elif __MACOSX__
+	/* Some build systems may need to specify this. */
+	#if !defined(__SSE2__) && !defined(__ARM_NEON__)
+	#error macOS does not have SSE2/NEON? Bad compiler?
+	#endif
+
+	/* Mac OS X/Intel guarantees SSE2. */
+	#define NEED_SCALAR_CONVERTER_FALLBACKS 0
+#else
+	/* Need plain C implementations to support all other hardware */
+	#define NEED_SCALAR_CONVERTER_FALLBACKS 1
+#endif
+
+/* Our NEON paths require AArch64, don't check __ARM_NEON__ here */
+#if defined(__aarch64__) || defined(_M_ARM64)
+#include <arm_neon.h>
+#define HAVE_NEON_INTRINSICS 1
+#endif
+
+
+#ifdef __SSE2__
+#include <emmintrin.h>
+#define HAVE_SSE2_INTRINSICS 1
+#endif
+
+/* SECTION 1: Type Converters */
+
+/* The SSE/NEON converters are based on SDL_audiotypecvt:
+ * https://hg.libsdl.org/SDL/file/default/src/audio/SDL_audiotypecvt.c
+ */
+
+#define DIVBY128 0.0078125f
+#define DIVBY32768 0.000030517578125f
+#define DIVBY8388607 0.00000011920930376163766f
+
+#if NEED_SCALAR_CONVERTER_FALLBACKS
+void FAudio_INTERNAL_Convert_U8_To_F32_Scalar(
+	const uint8_t *restrict src,
+	float *restrict dst,
+	uint32_t len
+) {
+	uint32_t i;
+	for (i = 0; i < len; i += 1)
+	{
+		*dst++ = (*src++ * DIVBY128) - 1.0f;
+	}
+}
+
+void FAudio_INTERNAL_Convert_S16_To_F32_Scalar(
+	const int16_t *restrict src,
+	float *restrict dst,
+	uint32_t len
+) {
+	uint32_t i;
+	for (i = 0; i < len; i += 1)
+	{
+		*dst++ = *src++ * DIVBY32768;
+	}
+}
+
+void FAudio_INTERNAL_Convert_S32_To_F32_Scalar(
+	const int32_t *restrict src,
+	float *restrict dst,
+	uint32_t len
+) {
+	uint32_t i;
+	for (i = 0; i < len; i += 1)
+	{
+		*dst++ = (*src++ >> 8) * DIVBY8388607;
+	}
+}
+#endif /* NEED_SCALAR_CONVERTER_FALLBACKS */
+
+#if HAVE_SSE2_INTRINSICS
+void FAudio_INTERNAL_Convert_U8_To_F32_SSE2(
+	const uint8_t *restrict src,
+	float *restrict dst,
+	uint32_t len
+) {
+    int i;
+    src += len - 1;
+    dst += len - 1;
+
+    /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
+    for (i = len; i && (((size_t) (dst-15)) & 15); --i, --src, --dst) {
+        *dst = (((float) *src) * DIVBY128) - 1.0f;
+    }
+
+    src -= 15; dst -= 15;  /* adjust to read SSE blocks from the start. */
+    FAudio_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do SSE blocks as long as we have 16 bytes available. */
+        const __m128i *mmsrc = (const __m128i *) src;
+        const __m128i zero = _mm_setzero_si128();
+        const __m128 divby128 = _mm_set1_ps(DIVBY128);
+        const __m128 minus1 = _mm_set1_ps(-1.0f);
+        while (i >= 16) {   /* 16 * 8-bit */
+            const __m128i bytes = _mm_load_si128(mmsrc);  /* get 16 uint8 into an XMM register. */
+            /* treat as int16, shift left to clear every other sint16, then back right with zero-extend. Now uint16. */
+            const __m128i shorts1 = _mm_srli_epi16(_mm_slli_epi16(bytes, 8), 8);
+            /* right-shift-zero-extend gets us uint16 with the other set of values. */
+            const __m128i shorts2 = _mm_srli_epi16(bytes, 8);
+            /* unpack against zero to make these int32, convert to float, multiply, add. Whew! */
+            /* Note that AVX2 can do floating point multiply+add in one instruction, fwiw. SSE2 cannot. */
+            const __m128 floats1 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(shorts1, zero)), divby128), minus1);
+            const __m128 floats2 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(shorts2, zero)), divby128), minus1);
+            const __m128 floats3 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(shorts1, zero)), divby128), minus1);
+            const __m128 floats4 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(shorts2, zero)), divby128), minus1);
+            /* Interleave back into correct order, store. */
+            _mm_store_ps(dst, _mm_unpacklo_ps(floats1, floats2));
+            _mm_store_ps(dst+4, _mm_unpackhi_ps(floats1, floats2));
+            _mm_store_ps(dst+8, _mm_unpacklo_ps(floats3, floats4));
+            _mm_store_ps(dst+12, _mm_unpackhi_ps(floats3, floats4));
+            i -= 16; mmsrc--; dst -= 16;
+        }
+
+        src = (const uint8_t *) mmsrc;
+    }
+
+    src += 15; dst += 15;  /* adjust for any scalar finishing. */
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        *dst = (((float) *src) * DIVBY128) - 1.0f;
+        i--; src--; dst--;
+    }
+}
+
+void FAudio_INTERNAL_Convert_S16_To_F32_SSE2(
+	const int16_t *restrict src,
+	float *restrict dst,
+	uint32_t len
+) {
+    int i;
+    src += len - 1;
+    dst += len - 1;
+
+    /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
+    for (i = len; i && (((size_t) (dst-7)) & 15); --i, --src, --dst) {
+        *dst = ((float) *src) * DIVBY32768;
+    }
+
+    src -= 7; dst -= 7;  /* adjust to read SSE blocks from the start. */
+    FAudio_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do SSE blocks as long as we have 16 bytes available. */
+        const __m128 divby32768 = _mm_set1_ps(DIVBY32768);
+        while (i >= 8) {   /* 8 * 16-bit */
+            const __m128i ints = _mm_load_si128((__m128i const *) src);  /* get 8 sint16 into an XMM register. */
+            /* treat as int32, shift left to clear every other sint16, then back right with sign-extend. Now sint32. */
+            const __m128i a = _mm_srai_epi32(_mm_slli_epi32(ints, 16), 16);
+            /* right-shift-sign-extend gets us sint32 with the other set of values. */
+            const __m128i b = _mm_srai_epi32(ints, 16);
+            /* Interleave these back into the right order, convert to float, multiply, store. */
+            _mm_store_ps(dst, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi32(a, b)), divby32768));
+            _mm_store_ps(dst+4, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi32(a, b)), divby32768));
+            i -= 8; src -= 8; dst -= 8;
+        }
+    }
+
+    src += 7; dst += 7;  /* adjust for any scalar finishing. */
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        *dst = ((float) *src) * DIVBY32768;
+        i--; src--; dst--;
+    }
+}
+
+void FAudio_INTERNAL_Convert_S32_To_F32_SSE2(
+	const int32_t *restrict src,
+	float *restrict dst,
+	uint32_t len
+) {
+    int i;
+
+    /* Get dst aligned to 16 bytes */
+    for (i = len; i && (((size_t) dst) & 15); --i, ++src, ++dst) {
+        *dst = ((float) (*src>>8)) * DIVBY8388607;
+    }
+
+    FAudio_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do SSE blocks as long as we have 16 bytes available. */
+        const __m128 divby8388607 = _mm_set1_ps(DIVBY8388607);
+        const __m128i *mmsrc = (const __m128i *) src;
+        while (i >= 4) {   /* 4 * sint32 */
+            /* shift out lowest bits so int fits in a float32. Small precision loss, but much faster. */
+            _mm_store_ps(dst, _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_load_si128(mmsrc), 8)), divby8388607));
+            i -= 4; mmsrc++; dst += 4;
+        }
+        src = (const int32_t *) mmsrc;
+    }
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        *dst = ((float) (*src>>8)) * DIVBY8388607;
+        i--; src++; dst++;
+    }
+}
+#endif /* HAVE_SSE2_INTRINSICS */
+
+#if HAVE_NEON_INTRINSICS
+void FAudio_INTERNAL_Convert_U8_To_F32_NEON(
+	const uint8_t *restrict src,
+	float *restrict dst,
+	uint32_t len
+) {
+    int i;
+    src += len - 1;
+    dst += len - 1;
+
+    /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
+    for (i = len; i && (((size_t) (dst-15)) & 15); --i, --src, --dst) {
+        *dst = (((float) *src) * DIVBY128) - 1.0f;
+    }
+
+    src -= 15; dst -= 15;  /* adjust to read NEON blocks from the start. */
+    FAudio_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const uint8_t *mmsrc = (const uint8_t *) src;
+        const float32x4_t divby128 = vdupq_n_f32(DIVBY128);
+        const float32x4_t negone = vdupq_n_f32(-1.0f);
+        while (i >= 16) {   /* 16 * 8-bit */
+            const uint8x16_t bytes = vld1q_u8(mmsrc);  /* get 16 uint8 into a NEON register. */
+            const uint16x8_t uint16hi = vmovl_u8(vget_high_u8(bytes));  /* convert top 8 bytes to 8 uint16 */
+            const uint16x8_t uint16lo = vmovl_u8(vget_low_u8(bytes));   /* convert bottom 8 bytes to 8 uint16 */
+            /* split uint16 to two uint32, then convert to float, then multiply to normalize, subtract to adjust for sign, store. */
+            vst1q_f32(dst, vmlaq_f32(negone, vcvtq_f32_u32(vmovl_u16(vget_low_u16(uint16hi))), divby128));
+            vst1q_f32(dst+4, vmlaq_f32(negone, vcvtq_f32_u32(vmovl_u16(vget_high_u16(uint16hi))), divby128));
+            vst1q_f32(dst+8, vmlaq_f32(negone, vcvtq_f32_u32(vmovl_u16(vget_low_u16(uint16lo))), divby128));
+            vst1q_f32(dst+12, vmlaq_f32(negone, vcvtq_f32_u32(vmovl_u16(vget_high_u16(uint16lo))), divby128));
+            i -= 16; mmsrc -= 16; dst -= 16;
+        }
+
+        src = (const uint8_t *) mmsrc;
+    }
+
+    src += 15; dst += 15;  /* adjust for any scalar finishing. */
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        *dst = (((float) *src) * DIVBY128) - 1.0f;
+        i--; src--; dst--;
+    }
+}
+
+void FAudio_INTERNAL_Convert_S16_To_F32_NEON(
+	const int16_t *restrict src,
+	float *restrict dst,
+	uint32_t len
+) {
+    int i;
+    src += len - 1;
+    dst += len - 1;
+
+    /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
+    for (i = len; i && (((size_t) (dst-7)) & 15); --i, --src, --dst) {
+        *dst = ((float) *src) * DIVBY32768;
+    }
+
+    src -= 7; dst -= 7;  /* adjust to read NEON blocks from the start. */
+    FAudio_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t divby32768 = vdupq_n_f32(DIVBY32768);
+        while (i >= 8) {   /* 8 * 16-bit */
+            const int16x8_t ints = vld1q_s16((int16_t const *) src);  /* get 8 sint16 into a NEON register. */
+            /* split int16 to two int32, then convert to float, then multiply to normalize, store. */
+            vst1q_f32(dst, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_low_s16(ints))), divby32768));
+            vst1q_f32(dst+4, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_high_s16(ints))), divby32768));
+            i -= 8; src -= 8; dst -= 8;
+        }
+    }
+
+    src += 7; dst += 7;  /* adjust for any scalar finishing. */
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        *dst = ((float) *src) * DIVBY32768;
+        i--; src--; dst--;
+    }
+}
+
+void FAudio_INTERNAL_Convert_S32_To_F32_NEON(
+	const int32_t *restrict src,
+	float *restrict dst,
+	uint32_t len
+) {
+    int i;
+
+    /* Get dst aligned to 16 bytes */
+    for (i = len; i && (((size_t) dst) & 15); --i, ++src, ++dst) {
+        *dst = ((float) (*src>>8)) * DIVBY8388607;
+    }
+
+    FAudio_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t divby8388607 = vdupq_n_f32(DIVBY8388607);
+        const int32_t *mmsrc = (const int32_t *) src;
+        while (i >= 4) {   /* 4 * sint32 */
+            /* shift out lowest bits so int fits in a float32. Small precision loss, but much faster. */
+            vst1q_f32(dst, vmulq_f32(vcvtq_f32_s32(vshrq_n_s32(vld1q_s32(mmsrc), 8)), divby8388607));
+            i -= 4; mmsrc += 4; dst += 4;
+        }
+        src = (const int32_t *) mmsrc;
+    }
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        *dst = ((float) (*src>>8)) * DIVBY8388607;
+        i--; src++; dst++;
+    }
+}
+#endif /* HAVE_NEON_INTRINSICS */
+
+/* SECTION 2: Linear Resamplers */
+
+void FAudio_INTERNAL_ResampleGeneric(
+	float *restrict dCache,
+	float *restrict resampleCache,
+	uint64_t *resampleOffset,
+	uint64_t resampleStep,
+	uint64_t toResample,
+	uint8_t channels
+) {
+	uint32_t i, j;
+	uint64_t cur = *resampleOffset & FIXED_FRACTION_MASK;
+	for (i = 0; i < toResample; i += 1)
+	{
+		for (j = 0; j < channels; j += 1)
+		{
+			/* lerp, then convert to float value */
+			*resampleCache++ = (float) (
+				dCache[j] +
+				(dCache[j + channels] - dCache[j]) *
+				FIXED_TO_DOUBLE(cur)
+			);
+		}
+
+		/* Increment fraction offset by the stepping value */
+		*resampleOffset += resampleStep;
+		cur += resampleStep;
+
+		/* Only increment the sample offset by integer values.
+		 * Sometimes this will be 0 until cur accumulates
+		 * enough steps, especially for "slow" rates.
+		 */
+		dCache += (cur >> FIXED_PRECISION) * channels;
+
+		/* Now that any integer has been added, drop it.
+		 * The offset pointer will preserve the total.
+		 */
+		cur &= FIXED_FRACTION_MASK;
+	}
+}
+
+#if NEED_SCALAR_CONVERTER_FALLBACKS
+void FAudio_INTERNAL_ResampleMono_Scalar(
+	float *restrict dCache,
+	float *restrict resampleCache,
+	uint64_t *resampleOffset,
+	uint64_t resampleStep,
+	uint64_t toResample,
+	uint8_t UNUSED
+) {
+	uint32_t i;
+	uint64_t cur = *resampleOffset & FIXED_FRACTION_MASK;
+	for (i = 0; i < toResample; i += 1)
+	{
+		/* lerp, then convert to float value */
+		*resampleCache++ = (float) (
+			dCache[0] +
+			(dCache[1] - dCache[0]) *
+			FIXED_TO_DOUBLE(cur)
+		);
+
+		/* Increment fraction offset by the stepping value */
+		*resampleOffset += resampleStep;
+		cur += resampleStep;
+
+		/* Only increment the sample offset by integer values.
+		 * Sometimes this will be 0 until cur accumulates
+		 * enough steps, especially for "slow" rates.
+		 */
+		dCache += (cur >> FIXED_PRECISION);
+
+		/* Now that any integer has been added, drop it.
+		 * The offset pointer will preserve the total.
+		 */
+		cur &= FIXED_FRACTION_MASK;
+	}
+}
+
+void FAudio_INTERNAL_ResampleStereo_Scalar(
+	float *restrict dCache,
+	float *restrict resampleCache,
+	uint64_t *resampleOffset,
+	uint64_t resampleStep,
+	uint64_t toResample,
+	uint8_t UNUSED
+) {
+	uint32_t i;
+	uint64_t cur = *resampleOffset & FIXED_FRACTION_MASK;
+	for (i = 0; i < toResample; i += 1)
+	{
+		/* lerp, then convert to float value */
+		*resampleCache++ = (float) (
+			dCache[0] +
+			(dCache[2] - dCache[0]) *
+			FIXED_TO_DOUBLE(cur)
+		);
+		*resampleCache++ = (float) (
+			dCache[1] +
+			(dCache[3] - dCache[1]) *
+			FIXED_TO_DOUBLE(cur)
+		);
+
+		/* Increment fraction offset by the stepping value */
+		*resampleOffset += resampleStep;
+		cur += resampleStep;
+
+		/* Only increment the sample offset by integer values.
+		 * Sometimes this will be 0 until cur accumulates
+		 * enough steps, especially for "slow" rates.
+		 */
+		dCache += (cur >> FIXED_PRECISION) * 2;
+
+		/* Now that any integer has been added, drop it.
+		 * The offset pointer will preserve the total.
+		 */
+		cur &= FIXED_FRACTION_MASK;
+	}
+}
+#endif /* NEED_SCALAR_CONVERTER_FALLBACKS */
+
+/* The SSE2 versions of the resamplers come from @8thMage! */
+
+#if HAVE_SSE2_INTRINSICS
+void FAudio_INTERNAL_ResampleMono_SSE2(
+	float *restrict dCache,
+	float *restrict resampleCache,
+	uint64_t *resampleOffset,
+	uint64_t resampleStep,
+	uint64_t toResample,
+	uint8_t UNUSED
+) {
+	uint32_t i, header, tail;
+	uint64_t cur_scalar_1, cur_scalar_2, cur_scalar_3;
+	float *dCache_1, *dCache_2, *dCache_3;
+	uint64_t cur_scalar = *resampleOffset & FIXED_FRACTION_MASK;
+	__m128 one_over_fixed_one, half, current_next_0_1, current_next_2_3,
+		current, next, sub, cur_fixed, mul, res;
+	__m128i cur_frac, adder_frac, adder_frac_loop;
+
+	/* This is the header, the Dest needs to be aligned to 16B */
+	header = (16 - ((size_t) resampleCache) % 16) / 4;
+	if (header == 4)
+	{
+		header = 0;
+	}
+	for (i = 0; i < header; i += 1)
+	{
+		/* lerp, then convert to float value */
+		*resampleCache++ = (float) (
+			dCache[0] +
+			(dCache[1] - dCache[0]) *
+			FIXED_TO_FLOAT(cur_scalar)
+		);
+
+		/* Increment fraction offset by the stepping value */
+		*resampleOffset += resampleStep;
+		cur_scalar += resampleStep;
+
+		/* Only increment the sample offset by integer values.
+		 * Sometimes this will be 0 until cur accumulates
+		 * enough steps, especially for "slow" rates.
+		 */
+		dCache += (cur_scalar >> FIXED_PRECISION);
+
+		/* Now that any integer has been added, drop it.
+		 * The offset pointer will preserve the total.
+		 */
+		cur_scalar &= FIXED_FRACTION_MASK;
+	}
+
+	toResample -= header;
+
+	/* initialising the varius cur
+	 * cur_frac is the fractional part of cur with 4 samples. as the
+	 * fractional part is 32 bit unsigned value, it can be just added
+	 * and the modulu operation for keeping the fractional part will be implicit.
+	 * the 0.5 is for converting signed values to float (no unsigned convert),
+	 * the 0.5 is added later.
+	 */
+	cur_frac = _mm_set1_epi32(
+		(uint32_t) (cur_scalar & FIXED_FRACTION_MASK) - DOUBLE_TO_FIXED(0.5)
+	);
+	adder_frac = _mm_setr_epi32(
+		0,
+		(uint32_t) (resampleStep & FIXED_FRACTION_MASK),
+		(uint32_t) ((resampleStep * 2) & FIXED_FRACTION_MASK),
+		(uint32_t) ((resampleStep * 3) & FIXED_FRACTION_MASK)
+	);
+	cur_frac = _mm_add_epi32(cur_frac, adder_frac);
+
+	/* The various cur_scalar is for the different samples
+	 * (1, 2, 3 compared to original cur_scalar = 0)
+	 */
+	cur_scalar_1 = cur_scalar + resampleStep;
+	cur_scalar_2 = cur_scalar + resampleStep * 2;
+	cur_scalar_3 = cur_scalar + resampleStep * 3;
+	dCache_1 = dCache + (cur_scalar_1 >> FIXED_PRECISION);
+	dCache_2 = dCache + (cur_scalar_2 >> FIXED_PRECISION);
+	dCache_3 = dCache + (cur_scalar_3 >> FIXED_PRECISION);
+	cur_scalar &= FIXED_FRACTION_MASK;
+	cur_scalar_1 &= FIXED_FRACTION_MASK;
+	cur_scalar_2 &= FIXED_FRACTION_MASK;
+	cur_scalar_3 &= FIXED_FRACTION_MASK;
+
+	/* FIXME: These should be _mm_undefined_ps! */
+	current_next_0_1 = _mm_setzero_ps();
+	current_next_2_3 = _mm_setzero_ps();
+
+	/* Constants */
+	one_over_fixed_one = _mm_set1_ps(1.0f / FIXED_ONE);
+	half = _mm_set1_ps(0.5f);
+	adder_frac_loop = _mm_set1_epi32(
+		(uint32_t) ((resampleStep * 4) & FIXED_FRACTION_MASK)
+	);
+
+	tail = toResample % 4;
+	for (i = 0; i < toResample - tail; i += 4, resampleCache += 4)
+	{
+		/* current next holds 2 pairs of the sample and the sample + 1
+		 * after that need to seperate them.
+		 */
+
+		current_next_0_1 = _mm_loadl_pi(current_next_0_1, (__m64*) dCache);
+		current_next_0_1 = _mm_loadh_pi(current_next_0_1, (__m64*) dCache_1);
+		current_next_2_3 = _mm_loadl_pi(current_next_2_3, (__m64*) dCache_2);
+		current_next_2_3 = _mm_loadh_pi(current_next_2_3, (__m64*) dCache_3);
+
+		/* Unpack them to have seperate current and next in 2 vectors. */
+		current = _mm_shuffle_ps(current_next_0_1, current_next_2_3, 0x88); /* 0b1000 */
+		next = _mm_shuffle_ps(current_next_0_1, current_next_2_3, 0xdd); /* 0b1101 */
+
+		sub = _mm_sub_ps(next, current);
+
+		/* Convert the fractional part to float and then mul to get the fractions out.
+		 * then add back the 0.5 we subtracted before.
+		 */
+		cur_fixed = _mm_add_ps(
+			_mm_mul_ps(
+				_mm_cvtepi32_ps(cur_frac),
+				one_over_fixed_one
+			),
+			half
+		);
+		mul = _mm_mul_ps(sub, cur_fixed);
+		res = _mm_add_ps(current, mul);
+
+		/* Store back */
+		_mm_store_ps(resampleCache, res);
+
+		/* Update dCaches for next iteration */
+		cur_scalar += resampleStep * 4;
+		cur_scalar_1 += resampleStep * 4;
+		cur_scalar_2 += resampleStep * 4;
+		cur_scalar_3 += resampleStep * 4;
+		dCache = dCache + (cur_scalar >> FIXED_PRECISION);
+		dCache_1 = dCache_1 + (cur_scalar_1 >> FIXED_PRECISION);
+		dCache_2 = dCache_2 + (cur_scalar_2 >> FIXED_PRECISION);
+		dCache_3 = dCache_3 + (cur_scalar_3 >> FIXED_PRECISION);
+		cur_scalar &= FIXED_FRACTION_MASK;
+		cur_scalar_1 &= FIXED_FRACTION_MASK;
+		cur_scalar_2 &= FIXED_FRACTION_MASK;
+		cur_scalar_3 &= FIXED_FRACTION_MASK;
+
+		cur_frac = _mm_add_epi32(cur_frac, adder_frac_loop);
+	}
+	*resampleOffset += resampleStep * (toResample - tail);
+
+	/* This is the tail. */
+	for (i = 0; i < tail; i += 1)
+	{
+		/* lerp, then convert to float value */
+		*resampleCache++ = (float) (
+			dCache[0] +
+			(dCache[1] - dCache[0]) *
+			FIXED_TO_FLOAT(cur_scalar)
+		);
+		
+		/* Increment fraction offset by the stepping value */
+		*resampleOffset += resampleStep;
+		cur_scalar += resampleStep;
+
+		/* Only increment the sample offset by integer values.
+		 * Sometimes this will be 0 until cur accumulates
+		 * enough steps, especially for "slow" rates.
+		 */
+		dCache += (cur_scalar >> FIXED_PRECISION);
+
+		/* Now that any integer has been added, drop it.
+		 * The offset pointer will preserve the total.
+		 */
+		cur_scalar &= FIXED_FRACTION_MASK;
+	}
+}
+
+void FAudio_INTERNAL_ResampleStereo_SSE2(
+	float *restrict dCache,
+	float *restrict resampleCache,
+	uint64_t *resampleOffset,
+	uint64_t resampleStep,
+	uint64_t toResample,
+	uint8_t UNUSED
+) {
+	uint32_t i, header, tail;
+	uint64_t cur_scalar, cur_scalar_1;
+	float *dCache_1;
+	__m128 one_over_fixed_one, half, current_next_1, current_next_2,
+		current, next, sub, cur_fixed, mul, res;
+	__m128i cur_frac, adder_frac, adder_frac_loop;
+
+	/* This is the header, the Dest needs to be aligned to 16B */
+	header = (16 - ((size_t) resampleCache) % 16) / 8;
+	if (header == 2)
+	{
+		header = 0;
+	}
+	cur_scalar = *resampleOffset & FIXED_FRACTION_MASK;
+	for (i = 0; i < header; i += 2)
+	{
+		/* lerp, then convert to float value */
+		*resampleCache++ = (float) (
+			dCache[0] +
+			(dCache[2] - dCache[0]) *
+			FIXED_TO_FLOAT(cur_scalar)
+		);
+		*resampleCache++ = (float) (
+			dCache[1] +
+			(dCache[3] - dCache[1]) *
+			FIXED_TO_FLOAT(cur_scalar)
+		);
+
+		/* Increment fraction offset by the stepping value */
+		*resampleOffset += resampleStep;
+		cur_scalar += resampleStep;
+
+		/* Only increment the sample offset by integer values.
+		 * Sometimes this will be 0 until cur accumulates
+		 * enough steps, especially for "slow" rates.
+		 */
+		dCache += (cur_scalar >> FIXED_PRECISION) * 2;
+
+		/* Now that any integer has been added, drop it.
+		 * The offset pointer will preserve the total.
+		 */
+		cur_scalar &= FIXED_FRACTION_MASK;
+	}
+
+	toResample -= header;
+
+	/* initialising the varius cur.
+	 * cur_frac holds the fractional part of cur.
+	 * to avoid duplication please see the mono part for a thorough
+	 * explanation.
+	 */
+	cur_frac = _mm_set1_epi32(
+		(uint32_t) (cur_scalar & FIXED_FRACTION_MASK) - DOUBLE_TO_FIXED(0.5)
+	);
+	adder_frac = _mm_setr_epi32(
+		0,
+		0,
+		(uint32_t) (resampleStep & FIXED_FRACTION_MASK),
+		(uint32_t) (resampleStep & FIXED_FRACTION_MASK)
+	);
+	cur_frac = _mm_add_epi32(cur_frac, adder_frac);
+
+	/* dCache_1 is the pointer for dcache in the next resample pos. */
+	cur_scalar_1 = cur_scalar + resampleStep;
+	dCache_1 = dCache + (cur_scalar_1 >> FIXED_PRECISION) * 2;
+	cur_scalar_1 &= FIXED_FRACTION_MASK;
+
+	one_over_fixed_one = _mm_set1_ps(1.0f / FIXED_ONE);
+	half = _mm_set1_ps(0.5f);
+	adder_frac_loop = _mm_set1_epi32(
+		(uint32_t) ((resampleStep * 2) & FIXED_FRACTION_MASK)
+	);
+
+	tail = toResample % 2;
+	for (i = 0; i < toResample - tail; i += 2, resampleCache += 4)
+	{
+		/* Current_next_1 and current_next_2 each holds 4 src
+		 * sample points for getting 4 dest resample point at the end.
+		 * current_next_1 holds:
+		 * (current_ch_1, current_ch_2, next_ch_1, next_ch_2)
+		 * for the first resample position, while current_next_2 holds
+		 * the same for the 2nd resample position
+		 */
+		current_next_1 = _mm_loadu_ps(dCache); /* A1B1A2B2 */
+		current_next_2 = _mm_loadu_ps(dCache_1); /* A3B3A4B4 */
+
+		/* Unpack them to get the current and the next in seperate vectors. */
+		current = _mm_castpd_ps(
+			_mm_unpacklo_pd(
+				_mm_castps_pd(current_next_1),
+				_mm_castps_pd(current_next_2)
+			)
+		);
+		next = _mm_castpd_ps(
+			_mm_unpackhi_pd(
+				_mm_castps_pd(current_next_1),
+				_mm_castps_pd(current_next_2)
+			)
+		);
+
+		sub = _mm_sub_ps(next, current);
+
+		/* Adding the 0.5 back.
+		 * See mono explanation for more elaborate explanation.
+		 */
+		cur_fixed = _mm_add_ps(
+			_mm_mul_ps(
+				_mm_cvtepi32_ps(cur_frac),
+				one_over_fixed_one
+			),
+			half
+		);
+		mul = _mm_mul_ps(sub, cur_fixed);
+		res = _mm_add_ps(current, mul);
+
+		/* Store the results */
+		_mm_store_ps(resampleCache, res);
+
+		/* Update dCaches for next iteration */
+		cur_scalar += resampleStep * 2;
+		cur_scalar_1 += resampleStep * 2;
+		dCache = dCache + (cur_scalar >> FIXED_PRECISION) * 2;
+		dCache_1 = dCache_1 + (cur_scalar_1 >> FIXED_PRECISION) * 2;
+		cur_scalar &= FIXED_FRACTION_MASK;
+		cur_scalar_1 &= FIXED_FRACTION_MASK;
+
+		cur_frac = _mm_add_epi32(cur_frac, adder_frac_loop);
+	}
+	*resampleOffset += resampleStep * (toResample - tail);
+
+	/* This is the tail. */
+	for (i = 0; i < tail; i += 1)
+	{
+		/* lerp, then convert to float value */
+		*resampleCache++ = (float) (
+			dCache[0] +
+			(dCache[2] - dCache[0]) *
+			FIXED_TO_FLOAT(cur_scalar)
+		);
+		*resampleCache++ = (float) (
+			dCache[1] +
+			(dCache[3] - dCache[1]) *
+			FIXED_TO_FLOAT(cur_scalar)
+		);
+
+		/* Increment fraction offset by the stepping value */
+		*resampleOffset += resampleStep;
+		cur_scalar += resampleStep;
+
+		/* Only increment the sample offset by integer values.
+		 * Sometimes this will be 0 until cur accumulates
+		 * enough steps, especially for "slow" rates.
+		 */
+		dCache += (cur_scalar >> FIXED_PRECISION) * 2;
+
+		/* Now that any integer has been added, drop it.
+		 * The offset pointer will preserve the total.
+		 */
+		cur_scalar &= FIXED_FRACTION_MASK;
+	}
+}
+#endif /* HAVE_SSE2_INTRINSICS */
+
+#if HAVE_NEON_INTRINSICS
+void FAudio_INTERNAL_ResampleMono_NEON(
+	float *restrict dCache,
+	float *restrict resampleCache,
+	uint64_t *resampleOffset,
+	uint64_t resampleStep,
+	uint64_t toResample,
+	uint8_t UNUSED
+) {
+	uint32_t i, header, tail;
+	uint64_t cur_scalar_1, cur_scalar_2, cur_scalar_3;
+	float *dCache_1, *dCache_2, *dCache_3;
+	uint64_t cur_scalar = *resampleOffset & FIXED_FRACTION_MASK;
+	float32x4_t one_over_fixed_one, half, current_next_0_1, current_next_2_3,
+		current, next, sub, cur_fixed, mul, res;
+	int32x4_t cur_frac, adder_frac, adder_frac_loop;
+
+	/* This is the header, the Dest needs to be aligned to 16B */
+	header = (16 - ((size_t) resampleCache) % 16) / 4;
+	if (header == 4)
+	{
+		header = 0;
+	}
+	for (i = 0; i < header; i += 1)
+	{
+		/* lerp, then convert to float value */
+		*resampleCache++ = (float) (
+			dCache[0] +
+			(dCache[1] - dCache[0]) *
+			FIXED_TO_FLOAT(cur_scalar)
+		);
+
+		/* Increment fraction offset by the stepping value */
+		*resampleOffset += resampleStep;
+		cur_scalar += resampleStep;
+
+		/* Only increment the sample offset by integer values.
+		 * Sometimes this will be 0 until cur accumulates
+		 * enough steps, especially for "slow" rates.
+		 */
+		dCache += (cur_scalar >> FIXED_PRECISION);
+
+		/* Now that any integer has been added, drop it.
+		 * The offset pointer will preserve the total.
+		 */
+		cur_scalar &= FIXED_FRACTION_MASK;
+	}
+
+	toResample -= header;
+
+	/* initialising the varius cur
+	 * cur_frac is the fractional part of cur with 4 samples. as the
+	 * fractional part is 32 bit unsigned value, it can be just added
+	 * and the modulu operation for keeping the fractional part will be implicit.
+	 * the 0.5 is for converting signed values to float (no unsigned convert),
+	 * the 0.5 is added later.
+	 */
+	cur_frac = vdupq_n_s32(
+		(uint32_t) (cur_scalar & FIXED_FRACTION_MASK) - DOUBLE_TO_FIXED(0.5)
+	);
+	int32_t __attribute__((aligned(16))) data[4] =
+	{
+		0,
+		(uint32_t) (resampleStep & FIXED_FRACTION_MASK),
+		(uint32_t) ((resampleStep * 2) & FIXED_FRACTION_MASK),
+		(uint32_t) ((resampleStep * 3) & FIXED_FRACTION_MASK)
+	};
+	adder_frac = vld1q_s32(data);
+	cur_frac = vaddq_s32(cur_frac, adder_frac);
+
+	/* The various cur_scalar is for the different samples
+	 * (1, 2, 3 compared to original cur_scalar = 0)
+	 */
+	cur_scalar_1 = cur_scalar + resampleStep;
+	cur_scalar_2 = cur_scalar + resampleStep * 2;
+	cur_scalar_3 = cur_scalar + resampleStep * 3;
+	dCache_1 = dCache + (cur_scalar_1 >> FIXED_PRECISION);
+	dCache_2 = dCache + (cur_scalar_2 >> FIXED_PRECISION);
+	dCache_3 = dCache + (cur_scalar_3 >> FIXED_PRECISION);
+	cur_scalar &= FIXED_FRACTION_MASK;
+	cur_scalar_1 &= FIXED_FRACTION_MASK;
+	cur_scalar_2 &= FIXED_FRACTION_MASK;
+	cur_scalar_3 &= FIXED_FRACTION_MASK;
+
+	/* Constants */
+	one_over_fixed_one = vdupq_n_f32(1.0f / FIXED_ONE);
+	half = vdupq_n_f32(0.5f);
+	adder_frac_loop = vdupq_n_s32(
+		(uint32_t) ((resampleStep * 4) & FIXED_FRACTION_MASK)
+	);
+
+	tail = toResample % 4;
+	for (i = 0; i < toResample - tail; i += 4, resampleCache += 4)
+	{
+		/* current next holds 2 pairs of the sample and the sample + 1
+		 * after that need to separate them.
+		 */
+		current_next_0_1 = vcombine_f32(
+			vld1_f32(dCache),
+			vld1_f32(dCache_1)
+		);
+		current_next_2_3 = vcombine_f32(
+			vld1_f32(dCache_2),
+			vld1_f32(dCache_3)
+		);
+
+		/* Unpack them to have seperate current and next in 2 vectors. */
+		current = vuzp1q_f32(current_next_0_1, current_next_2_3);
+		next = vuzp2q_f32(current_next_0_1, current_next_2_3);
+
+		sub = vsubq_f32(next, current);
+
+		/* Convert the fractional part to float and then mul to get the fractions out.
+		 * then add back the 0.5 we subtracted before.
+		 */
+		cur_fixed = vaddq_f32(
+			vmulq_f32(
+				vcvtq_f32_s32(cur_frac),
+				one_over_fixed_one
+			),
+			half
+		);
+		mul = vmulq_f32(sub, cur_fixed);
+		res = vaddq_f32(current, mul);
+
+		/* Store back */
+		vst1q_f32(resampleCache, res);
+
+		/* Update dCaches for next iteration */
+		cur_scalar += resampleStep * 4;
+		cur_scalar_1 += resampleStep * 4;
+		cur_scalar_2 += resampleStep * 4;
+		cur_scalar_3 += resampleStep * 4;
+		dCache = dCache + (cur_scalar >> FIXED_PRECISION);
+		dCache_1 = dCache_1 + (cur_scalar_1 >> FIXED_PRECISION);
+		dCache_2 = dCache_2 + (cur_scalar_2 >> FIXED_PRECISION);
+		dCache_3 = dCache_3 + (cur_scalar_3 >> FIXED_PRECISION);
+		cur_scalar &= FIXED_FRACTION_MASK;
+		cur_scalar_1 &= FIXED_FRACTION_MASK;
+		cur_scalar_2 &= FIXED_FRACTION_MASK;
+		cur_scalar_3 &= FIXED_FRACTION_MASK;
+
+		cur_frac = vaddq_s32(cur_frac, adder_frac_loop);
+	}
+	*resampleOffset += resampleStep * (toResample - tail);
+
+	/* This is the tail. */
+	for (i = 0; i < tail; i += 1)
+	{
+		/* lerp, then convert to float value */
+		*resampleCache++ = (float) (
+			dCache[0] +
+			(dCache[1] - dCache[0]) *
+			FIXED_TO_FLOAT(cur_scalar)
+		);
+
+		/* Increment fraction offset by the stepping value */
+		*resampleOffset += resampleStep;
+		cur_scalar += resampleStep;
+
+		/* Only increment the sample offset by integer values.
+		 * Sometimes this will be 0 until cur accumulates
+		 * enough steps, especially for "slow" rates.
+		 */
+		dCache += (cur_scalar >> FIXED_PRECISION);
+
+		/* Now that any integer has been added, drop it.
+		 * The offset pointer will preserve the total.
+		 */
+		cur_scalar &= FIXED_FRACTION_MASK;
+	}
+}
+
+void FAudio_INTERNAL_ResampleStereo_NEON(
+	float *restrict dCache,
+	float *restrict resampleCache,
+	uint64_t *resampleOffset,
+	uint64_t resampleStep,
+	uint64_t toResample,
+	uint8_t channels
+) {
+	uint32_t i, header, tail;
+	uint64_t cur_scalar, cur_scalar_1;
+	float *dCache_1;
+	float32x4_t one_over_fixed_one, half, current, next, sub, cur_fixed, mul, res;
+	int32x4_t cur_frac, adder_frac, adder_frac_loop;
+
+	/* This is the header, the Dest needs to be aligned to 16B */
+	header = (16 - ((size_t) resampleCache) % 16) / 8;
+	if (header == 2)
+	{
+		header = 0;
+	}
+	cur_scalar = *resampleOffset & FIXED_FRACTION_MASK;
+	for (i = 0; i < header; i += 2)
+	{
+		/* lerp, then convert to float value */
+		*resampleCache++ = (float) (
+			dCache[0] +
+			(dCache[2] - dCache[0]) *
+			FIXED_TO_FLOAT(cur_scalar)
+		);
+		*resampleCache++ = (float) (
+			dCache[1] +
+			(dCache[3] - dCache[1]) *
+			FIXED_TO_FLOAT(cur_scalar)
+		);
+
+		/* Increment fraction offset by the stepping value */
+		*resampleOffset += resampleStep;
+		cur_scalar += resampleStep;
+
+		/* Only increment the sample offset by integer values.
+		 * Sometimes this will be 0 until cur accumulates
+		 * enough steps, especially for "slow" rates.
+		 */
+		dCache += (cur_scalar >> FIXED_PRECISION) * 2;
+
+		/* Now that any integer has been added, drop it.
+		 * The offset pointer will preserve the total.
+		 */
+		cur_scalar &= FIXED_FRACTION_MASK;
+	}
+
+	toResample -= header;
+
+	/* initialising the varius cur.
+	 * cur_frac holds the fractional part of cur.
+	 * to avoid duplication please see the mono part for a thorough
+	 * explanation.
+	 */
+	cur_frac = vdupq_n_s32(
+		(uint32_t) (cur_scalar & FIXED_FRACTION_MASK) - DOUBLE_TO_FIXED(0.5)
+	);
+	int32_t __attribute__((aligned(16))) data[4] =
+	{
+		0,
+		0,
+		(uint32_t) (resampleStep & FIXED_FRACTION_MASK),
+		(uint32_t) (resampleStep & FIXED_FRACTION_MASK)
+	};
+	adder_frac = vld1q_s32(data);
+	cur_frac = vaddq_s32(cur_frac, adder_frac);
+
+	/* dCache_1 is the pointer for dcache in the next resample pos. */
+	cur_scalar_1 = cur_scalar + resampleStep;
+	dCache_1 = dCache + (cur_scalar_1 >> FIXED_PRECISION) * 2;
+	cur_scalar_1 &= FIXED_FRACTION_MASK;
+
+	one_over_fixed_one = vdupq_n_f32(1.0f / FIXED_ONE);
+	half = vdupq_n_f32(0.5f);
+	adder_frac_loop = vdupq_n_s32(
+		(uint32_t) ((resampleStep * 2) & FIXED_FRACTION_MASK)
+	);
+
+	tail = toResample % 2;
+	for (i = 0; i < toResample - tail; i += 2, resampleCache += 4)
+	{
+		/* Current_next_1 and current_next_2 each holds 4 src
+		 * sample points for getting 4 dest resample point at the end.
+		 * current_next_1 holds:
+		 * (current_ch_1, current_ch_2, next_ch_1, next_ch_2)
+		 * for the first resample position, while current_next_2 holds
+		 * the same for the 2nd resample position
+		 */
+		current = vcombine_f32(
+			vld1_f32(dCache), /* A1B1 */
+			vld1_f32(dCache_1) /* A3B3 */
+		);
+		next = vcombine_f32(
+			vld1_f32(dCache + 2), /* A2B2 */
+			vld1_f32(dCache_1 + 2) /* A4B4 */
+		);
+
+		sub = vsubq_f32(next, current);
+
+		/* Adding the 0.5 back.
+		 * See mono explanation for more elaborate explanation.
+		 */
+		cur_fixed = vaddq_f32(
+			vmulq_f32(
+				vcvtq_f32_s32(cur_frac),
+				one_over_fixed_one
+			),
+			half
+		);
+		mul = vmulq_f32(sub, cur_fixed);
+		res = vaddq_f32(current, mul);
+
+		/* Store the results */
+		vst1q_f32(resampleCache, res);
+
+		/* Update dCaches for next iteration */
+		cur_scalar += resampleStep * 2;
+		cur_scalar_1 += resampleStep * 2;
+		dCache = dCache + (cur_scalar >> FIXED_PRECISION) * 2;
+		dCache_1 = dCache_1 + (cur_scalar_1 >> FIXED_PRECISION) * 2;
+		cur_scalar &= FIXED_FRACTION_MASK;
+		cur_scalar_1 &= FIXED_FRACTION_MASK;
+
+		cur_frac = vaddq_s32(cur_frac, adder_frac_loop);
+	}
+	*resampleOffset += resampleStep * (toResample - tail);
+
+	/* This is the tail. */
+	for (i = 0; i < tail; i += 1)
+	{
+		/* lerp, then convert to float value */
+		*resampleCache++ = (float) (
+			dCache[0] +
+			(dCache[2] - dCache[0]) *
+			FIXED_TO_FLOAT(cur_scalar)
+		);
+		*resampleCache++ = (float) (
+			dCache[1] +
+			(dCache[3] - dCache[1]) *
+			FIXED_TO_FLOAT(cur_scalar)
+		);
+
+		/* Increment fraction offset by the stepping value */
+		*resampleOffset += resampleStep;
+		cur_scalar += resampleStep;
+
+		/* Only increment the sample offset by integer values.
+		 * Sometimes this will be 0 until cur accumulates
+		 * enough steps, especially for "slow" rates.
+		 */
+		dCache += (cur_scalar >> FIXED_PRECISION) * 2;
+
+		/* Now that any integer has been added, drop it.
+		 * The offset pointer will preserve the total.
+		 */
+		cur_scalar &= FIXED_FRACTION_MASK;
+	}
+}
+#endif /* HAVE_NEON_INTRINSICS */
+
+/* SECTION 3: Amplifiers */
+
+#if NEED_SCALAR_CONVERTER_FALLBACKS
+void FAudio_INTERNAL_Amplify_Scalar(
+	float* output,
+	uint32_t totalSamples,
+	float volume
+) {
+	uint32_t i;
+	for (i = 0; i < totalSamples; i += 1)
+	{
+		output[i] *= volume;
+	}
+}
+#endif /* NEED_SCALAR_CONVERTER_FALLBACKS */
+
+/* The SSE2 version of the amplifier comes from @8thMage! */
+
+#if HAVE_SSE2_INTRINSICS
+void FAudio_INTERNAL_Amplify_SSE2(
+	float* output,
+	uint32_t totalSamples,
+	float volume
+) {
+	uint32_t i;
+	uint32_t header = (16 - (((size_t) output) % 16)) / 4;
+	uint32_t tail = (totalSamples - header) % 4;
+	__m128 volumeVec, outVec;
+	if (header == 4)
+	{
+		header = 0;
+	}
+	if (tail == 4)
+	{
+		tail = 0;
+	}
+
+	for (i = 0; i < header; i += 1)
+	{
+		output[i] *= volume;
+	}
+
+	volumeVec = _mm_set1_ps(volume);
+	for (i = header; i < totalSamples - tail; i += 4)
+	{
+		outVec = _mm_load_ps(output + i);
+		outVec = _mm_mul_ps(outVec, volumeVec);
+		_mm_store_ps(output + i, outVec);
+	}
+
+	for (i = totalSamples - tail; i < totalSamples; i += 1)
+	{
+		output[i] *= volume;
+	}
+}
+#endif /* HAVE_SSE2_INTRINSICS */
+
+#if HAVE_NEON_INTRINSICS
+void FAudio_INTERNAL_Amplify_NEON(
+	float* output,
+	uint32_t totalSamples,
+	float volume
+) {
+	uint32_t i;
+	uint32_t header = (16 - (((size_t) output) % 16)) / 4;
+	uint32_t tail = (totalSamples - header) % 4;
+	float32x4_t volumeVec, outVec;
+	if (header == 4)
+	{
+		header = 0;
+	}
+	if (tail == 4)
+	{
+		tail = 0;
+	}
+
+	for (i = 0; i < header; i += 1)
+	{
+		output[i] *= volume;
+	}
+
+	volumeVec = vdupq_n_f32(volume);
+	for (i = header; i < totalSamples - tail; i += 4)
+	{
+		outVec = vld1q_f32(output + i);
+		outVec = vmulq_f32(outVec, volumeVec);
+		vst1q_f32(output + i, outVec);
+	}
+
+	for (i = totalSamples - tail; i < totalSamples; i += 1)
+	{
+		output[i] *= volume;
+	}
+}
+#endif /* HAVE_NEON_INTRINSICS */
+
+/* SECTION 4: Mixer Functions */
+
+void FAudio_INTERNAL_Mix_Generic_Scalar(
+	uint32_t toMix,
+	uint32_t srcChans,
+	uint32_t dstChans,
+	float *restrict src,
+	float *restrict dst,
+	float *restrict coefficients
+) {
+	uint32_t i, co, ci;
+	for (i = 0; i < toMix; i += 1, src += srcChans, dst += dstChans)
+	for (co = 0; co < dstChans; co += 1)
+	{
+		for (ci = 0; ci < srcChans; ci += 1)
+		{
+			dst[co] += (
+				src[ci] *
+				coefficients[co * srcChans + ci]
+			);
+		}
+	}
+}
+
+#if HAVE_SSE2_INTRINSICS
+/* SSE horizontal add by Peter Cordes, CC-BY-SA.
+ * From https://stackoverflow.com/a/35270026 */
+static inline float FAudio_simd_hadd(__m128 v)
+{
+	__m128 shuf = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 3, 0, 1));
+	__m128 sums = _mm_add_ps(v, shuf);
+	shuf = _mm_movehl_ps(shuf, sums);
+	sums = _mm_add_ss(sums, shuf);
+	return _mm_cvtss_f32(sums);
+}
+
+void FAudio_INTERNAL_Mix_Generic_SSE2(
+	uint32_t toMix,
+	uint32_t srcChans,
+	uint32_t dstChans,
+	float *restrict src,
+	float *restrict dst,
+	float *restrict coefficients
+) {
+	uint32_t i, co, ci;
+	for (i = 0; i < toMix; i += 1, src += srcChans, dst += dstChans)
+	for (co = 0; co < dstChans; co += 1)
+	{
+		for (ci = 0; srcChans - ci >= 4; ci += 4)
+		{
+			/* do SIMD */
+			const __m128 vols = _mm_loadu_ps(&coefficients[co * srcChans + ci]);
+			const __m128 dat = _mm_loadu_ps(&src[ci]);
+			dst[co] += FAudio_simd_hadd(_mm_mul_ps(dat, vols));
+		}
+
+		for (; ci < srcChans; ci += 1)
+		{
+			/* do scalar */
+			dst[co] += (
+				src[ci] *
+				coefficients[co * srcChans + ci]
+			);
+		}
+	}
+}
+#endif /* HAVE_SSE2_INTRINSICS */
+
+void FAudio_INTERNAL_Mix_1in_1out_Scalar(
+	uint32_t toMix,
+	uint32_t UNUSED1,
+	uint32_t UNUSED2,
+	float *restrict src,
+	float *restrict dst,
+	float *restrict coefficients
+) {
+	uint32_t i;
+	for (i = 0; i < toMix; i += 1, src += 1, dst += 1)
+	{
+		/* Base source data, combined with the coefficients */
+		dst[0] += src[0] * coefficients[0];
+	}
+}
+
+void FAudio_INTERNAL_Mix_1in_2out_Scalar(
+	uint32_t toMix,
+	uint32_t UNUSED1,
+	uint32_t UNUSED2,
+	float *restrict src,
+	float *restrict dst,
+	float *restrict coefficients
+) {
+	uint32_t i;
+	for (i = 0; i < toMix; i += 1, src += 1, dst += 2)
+	{
+		dst[0] += src[0] * coefficients[0];
+		dst[1] += src[0] * coefficients[1];
+	}
+}
+
+void FAudio_INTERNAL_Mix_1in_6out_Scalar(
+	uint32_t toMix,
+	uint32_t UNUSED1,
+	uint32_t UNUSED2,
+	float *restrict src,
+	float *restrict dst,
+	float *restrict coefficients
+) {
+	uint32_t i;
+	for (i = 0; i < toMix; i += 1, src += 1, dst += 6)
+	{
+		dst[0] += src[0] * coefficients[0];
+		dst[1] += src[0] * coefficients[1];
+		dst[2] += src[0] * coefficients[2];
+		dst[3] += src[0] * coefficients[3];
+		dst[4] += src[0] * coefficients[4];
+		dst[5] += src[0] * coefficients[5];
+	}
+}
+
+void FAudio_INTERNAL_Mix_1in_8out_Scalar(
+	uint32_t toMix,
+	uint32_t UNUSED1,
+	uint32_t UNUSED2,
+	float *restrict src,
+	float *restrict dst,
+	float *restrict coefficients
+) {
+	uint32_t i;
+	for (i = 0; i < toMix; i += 1, src += 1, dst += 8)
+	{
+		dst[0] += src[0] * coefficients[0];
+		dst[1] += src[0] * coefficients[1];
+		dst[2] += src[0] * coefficients[2];
+		dst[3] += src[0] * coefficients[3];
+		dst[4] += src[0] * coefficients[4];
+		dst[5] += src[0] * coefficients[5];
+		dst[6] += src[0] * coefficients[6];
+		dst[7] += src[0] * coefficients[7];
+	}
+}
+
+void FAudio_INTERNAL_Mix_2in_1out_Scalar(
+	uint32_t toMix,
+	uint32_t UNUSED1,
+	uint32_t UNUSED2,
+	float *restrict src,
+	float *restrict dst,
+	float *restrict coefficients
+) {
+	uint32_t i;
+	for (i = 0; i < toMix; i += 1, src += 2, dst += 1)
+	{
+		/* Base source data, combined with the coefficients */
+		dst[0] += (
+			(src[0] * coefficients[0]) +
+			(src[1] * coefficients[1])
+		);
+	}
+}
+
+void FAudio_INTERNAL_Mix_2in_2out_Scalar(
+	uint32_t toMix,
+	uint32_t UNUSED1,
+	uint32_t UNUSED2,
+	float *restrict src,
+	float *restrict dst,
+	float *restrict coefficients
+) {
+	uint32_t i;
+	for (i = 0; i < toMix; i += 1, src += 2, dst += 2)
+	{
+		dst[0] += (
+			(src[0] * coefficients[0]) +
+			(src[1] * coefficients[1])
+		);
+		dst[1] += (
+			(src[0] * coefficients[2]) +
+			(src[1] * coefficients[3])
+		);
+	}
+}
+
+void FAudio_INTERNAL_Mix_2in_6out_Scalar(
+	uint32_t toMix,
+	uint32_t UNUSED1,
+	uint32_t UNUSED2,
+	float *restrict src,
+	float *restrict dst,
+	float *restrict coefficients
+) {
+	uint32_t i;
+	for (i = 0; i < toMix; i += 1, src += 2, dst += 6)
+	{
+		dst[0] += (
+			(src[0] * coefficients[0]) +
+			(src[1] * coefficients[1])
+		);
+		dst[1] += (
+			(src[0] * coefficients[2]) +
+			(src[1] * coefficients[3])
+		);
+		dst[2] += (
+			(src[0] * coefficients[4]) +
+			(src[1] * coefficients[5])
+		);
+		dst[3] += (
+			(src[0] * coefficients[6]) +
+			(src[1] * coefficients[7])
+		);
+		dst[4] += (
+			(src[0] * coefficients[8]) +
+			(src[1] * coefficients[9])
+		);
+		dst[5] += (
+			(src[0] * coefficients[10]) +
+			(src[1] * coefficients[11])
+		);
+	}
+}
+
+void FAudio_INTERNAL_Mix_2in_8out_Scalar(
+	uint32_t toMix,
+	uint32_t UNUSED1,
+	uint32_t UNUSED2,
+	float *restrict src,
+	float *restrict dst,
+	float *restrict coefficients
+) {
+	uint32_t i;
+	for (i = 0; i < toMix; i += 1, src += 2, dst += 8)
+	{
+		dst[0] += (
+			(src[0] * coefficients[0]) +
+			(src[1] * coefficients[1])
+		);
+		dst[1] += (
+			(src[0] * coefficients[2]) +
+			(src[1] * coefficients[3])
+		);
+		dst[2] += (
+			(src[0] * coefficients[4]) +
+			(src[1] * coefficients[5])
+		);
+		dst[3] += (
+			(src[0] * coefficients[6]) +
+			(src[1] * coefficients[7])
+		);
+		dst[4] += (
+			(src[0] * coefficients[8]) +
+			(src[1] * coefficients[9])
+		);
+		dst[5] += (
+			(src[0] * coefficients[10]) +
+			(src[1] * coefficients[11])
+		);
+		dst[6] += (
+			(src[0] * coefficients[12]) +
+			(src[1] * coefficients[13])
+		);
+		dst[7] += (
+			(src[0] * coefficients[14]) +
+			(src[1] * coefficients[15])
+		);
+	}
+}
+
+/* SECTION 5: InitSIMDFunctions. Assigns based on SSE2/NEON support. */
+
+void (*FAudio_INTERNAL_Convert_U8_To_F32)(
+	const uint8_t *restrict src,
+	float *restrict dst,
+	uint32_t len
+);
+void (*FAudio_INTERNAL_Convert_S16_To_F32)(
+	const int16_t *restrict src,
+	float *restrict dst,
+	uint32_t len
+);
+void (*FAudio_INTERNAL_Convert_S32_To_F32)(
+	const int32_t *restrict src,
+	float *restrict dst,
+	uint32_t len
+);
+
+FAudioResampleCallback FAudio_INTERNAL_ResampleMono;
+FAudioResampleCallback FAudio_INTERNAL_ResampleStereo;
+
+void (*FAudio_INTERNAL_Amplify)(
+	float *output,
+	uint32_t totalSamples,
+	float volume
+);
+
+FAudioMixCallback FAudio_INTERNAL_Mix_Generic;
+
+void FAudio_INTERNAL_InitSIMDFunctions(uint8_t hasSSE2, uint8_t hasNEON)
+{
+#if HAVE_SSE2_INTRINSICS
+	if (hasSSE2)
+	{
+		FAudio_INTERNAL_Convert_U8_To_F32 = FAudio_INTERNAL_Convert_U8_To_F32_SSE2;
+		FAudio_INTERNAL_Convert_S16_To_F32 = FAudio_INTERNAL_Convert_S16_To_F32_SSE2;
+		FAudio_INTERNAL_Convert_S32_To_F32 = FAudio_INTERNAL_Convert_S32_To_F32_SSE2;
+		FAudio_INTERNAL_ResampleMono = FAudio_INTERNAL_ResampleMono_SSE2;
+		FAudio_INTERNAL_ResampleStereo = FAudio_INTERNAL_ResampleStereo_SSE2;
+		FAudio_INTERNAL_Amplify = FAudio_INTERNAL_Amplify_SSE2;
+		FAudio_INTERNAL_Mix_Generic = FAudio_INTERNAL_Mix_Generic_SSE2;
+		return;
+	}
+#endif
+#if HAVE_NEON_INTRINSICS
+	if (hasNEON)
+	{
+		FAudio_INTERNAL_Convert_U8_To_F32 = FAudio_INTERNAL_Convert_U8_To_F32_NEON;
+		FAudio_INTERNAL_Convert_S16_To_F32 = FAudio_INTERNAL_Convert_S16_To_F32_NEON;
+		FAudio_INTERNAL_Convert_S32_To_F32 = FAudio_INTERNAL_Convert_S32_To_F32_NEON;
+		FAudio_INTERNAL_ResampleMono = FAudio_INTERNAL_ResampleMono_NEON;
+		FAudio_INTERNAL_ResampleStereo = FAudio_INTERNAL_ResampleStereo_NEON;
+		FAudio_INTERNAL_Amplify = FAudio_INTERNAL_Amplify_NEON;
+		FAudio_INTERNAL_Mix_Generic = FAudio_INTERNAL_Mix_Generic_Scalar;
+		return;
+	}
+#endif
+#if NEED_SCALAR_CONVERTER_FALLBACKS
+	FAudio_INTERNAL_Convert_U8_To_F32 = FAudio_INTERNAL_Convert_U8_To_F32_Scalar;
+	FAudio_INTERNAL_Convert_S16_To_F32 = FAudio_INTERNAL_Convert_S16_To_F32_Scalar;
+	FAudio_INTERNAL_Convert_S32_To_F32 = FAudio_INTERNAL_Convert_S32_To_F32_Scalar;
+	FAudio_INTERNAL_ResampleMono = FAudio_INTERNAL_ResampleMono_Scalar;
+	FAudio_INTERNAL_ResampleStereo = FAudio_INTERNAL_ResampleStereo_Scalar;
+	FAudio_INTERNAL_Amplify = FAudio_INTERNAL_Amplify_Scalar;
+	FAudio_INTERNAL_Mix_Generic = FAudio_INTERNAL_Mix_Generic_Scalar;
+#else
+	FAudio_assert(0 && "Need converter functions!");
+#endif
+}
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FAudio_operationset.c b/libs/faudio/src/FAudio_operationset.c
new file mode 100644
index 00000000000..f17cd9270df
--- /dev/null
+++ b/libs/faudio/src/FAudio_operationset.c
@@ -0,0 +1,777 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+/* FAudio_operationset.c originally written by Tyler Glaiel */
+
+#include "FAudio_internal.h"
+
+/* Core OperationSet Types */
+
+typedef enum FAudio_OPERATIONSET_Type
+{
+	FAUDIOOP_ENABLEEFFECT,
+	FAUDIOOP_DISABLEEFFECT,
+	FAUDIOOP_SETEFFECTPARAMETERS,
+	FAUDIOOP_SETFILTERPARAMETERS,
+	FAUDIOOP_SETOUTPUTFILTERPARAMETERS,
+	FAUDIOOP_SETVOLUME,
+	FAUDIOOP_SETCHANNELVOLUMES,
+	FAUDIOOP_SETOUTPUTMATRIX,
+	FAUDIOOP_START,
+	FAUDIOOP_STOP,
+	FAUDIOOP_EXITLOOP,
+	FAUDIOOP_SETFREQUENCYRATIO
+} FAudio_OPERATIONSET_Type;
+
+struct FAudio_OPERATIONSET_Operation
+{
+	FAudio_OPERATIONSET_Type Type;
+	uint32_t OperationSet;
+	FAudioVoice *Voice;
+
+	union
+	{
+		struct
+		{
+			uint32_t EffectIndex;
+		} EnableEffect;
+		struct
+		{
+			uint32_t EffectIndex;
+		} DisableEffect;
+		struct
+		{
+			uint32_t EffectIndex;
+			void *pParameters;
+			uint32_t ParametersByteSize;
+		} SetEffectParameters;
+		struct
+		{
+			FAudioFilterParameters Parameters;
+		} SetFilterParameters;
+		struct
+		{
+			FAudioVoice *pDestinationVoice;
+			FAudioFilterParameters Parameters;
+		} SetOutputFilterParameters;
+		struct
+		{
+			float Volume;
+		} SetVolume;
+		struct
+		{
+			uint32_t Channels;
+			float *pVolumes;
+		} SetChannelVolumes;
+		struct
+		{
+			FAudioVoice *pDestinationVoice;
+			uint32_t SourceChannels;
+			uint32_t DestinationChannels;
+			float *pLevelMatrix;
+		} SetOutputMatrix;
+		struct
+		{
+			uint32_t Flags;
+		} Start;
+		struct
+		{
+			uint32_t Flags;
+		} Stop;
+		/* No special data for ExitLoop
+		struct
+		{
+		} ExitLoop;
+		*/
+		struct
+		{
+			float Ratio;
+		} SetFrequencyRatio;
+	} Data;
+
+	FAudio_OPERATIONSET_Operation *next;
+};
+
+/* Used by both Commit and Clear routines */
+
+static inline void DeleteOperation(
+	FAudio_OPERATIONSET_Operation *op,
+	FAudioFreeFunc pFree
+) {
+	if (op->Type == FAUDIOOP_SETEFFECTPARAMETERS)
+	{
+		pFree(op->Data.SetEffectParameters.pParameters);
+	}
+	else if (op->Type == FAUDIOOP_SETCHANNELVOLUMES)
+	{
+		pFree(op->Data.SetChannelVolumes.pVolumes);
+	}
+	else if (op->Type == FAUDIOOP_SETOUTPUTMATRIX)
+	{
+		pFree(op->Data.SetOutputMatrix.pLevelMatrix);
+	}
+	pFree(op);
+}
+
+/* OperationSet Execution */
+
+static inline void ExecuteOperation(FAudio_OPERATIONSET_Operation *op)
+{
+	switch (op->Type)
+	{
+	case FAUDIOOP_ENABLEEFFECT:
+		FAudioVoice_EnableEffect(
+			op->Voice,
+			op->Data.EnableEffect.EffectIndex,
+			FAUDIO_COMMIT_NOW
+		);
+	break;
+
+	case FAUDIOOP_DISABLEEFFECT:
+		FAudioVoice_DisableEffect(
+			op->Voice,
+			op->Data.DisableEffect.EffectIndex,
+			FAUDIO_COMMIT_NOW
+		);
+	break;
+
+	case FAUDIOOP_SETEFFECTPARAMETERS:
+		FAudioVoice_SetEffectParameters(
+			op->Voice,
+			op->Data.SetEffectParameters.EffectIndex,
+			op->Data.SetEffectParameters.pParameters,
+			op->Data.SetEffectParameters.ParametersByteSize,
+			FAUDIO_COMMIT_NOW
+		);
+	break;
+
+	case FAUDIOOP_SETFILTERPARAMETERS:
+		FAudioVoice_SetFilterParameters(
+			op->Voice,
+			&op->Data.SetFilterParameters.Parameters,
+			FAUDIO_COMMIT_NOW
+		);
+	break;
+
+	case FAUDIOOP_SETOUTPUTFILTERPARAMETERS:
+		FAudioVoice_SetOutputFilterParameters(
+			op->Voice,
+			op->Data.SetOutputFilterParameters.pDestinationVoice,
+			&op->Data.SetOutputFilterParameters.Parameters,
+			FAUDIO_COMMIT_NOW
+		);
+	break;
+
+	case FAUDIOOP_SETVOLUME:
+		FAudioVoice_SetVolume(
+			op->Voice,
+			op->Data.SetVolume.Volume,
+			FAUDIO_COMMIT_NOW
+		);
+	break;
+
+	case FAUDIOOP_SETCHANNELVOLUMES:
+		FAudioVoice_SetChannelVolumes(
+			op->Voice,
+			op->Data.SetChannelVolumes.Channels,
+			op->Data.SetChannelVolumes.pVolumes,
+			FAUDIO_COMMIT_NOW
+		);
+	break;
+
+	case FAUDIOOP_SETOUTPUTMATRIX:
+		FAudioVoice_SetOutputMatrix(
+			op->Voice,
+			op->Data.SetOutputMatrix.pDestinationVoice,
+			op->Data.SetOutputMatrix.SourceChannels,
+			op->Data.SetOutputMatrix.DestinationChannels,
+			op->Data.SetOutputMatrix.pLevelMatrix,
+			FAUDIO_COMMIT_NOW
+		);
+	break;
+
+	case FAUDIOOP_START:
+		FAudioSourceVoice_Start(
+			op->Voice,
+			op->Data.Start.Flags,
+			FAUDIO_COMMIT_NOW
+		);
+	break;
+
+	case FAUDIOOP_STOP:
+		FAudioSourceVoice_Stop(
+			op->Voice,
+			op->Data.Stop.Flags,
+			FAUDIO_COMMIT_NOW
+		);
+	break;
+
+	case FAUDIOOP_EXITLOOP:
+		FAudioSourceVoice_ExitLoop(
+			op->Voice,
+			FAUDIO_COMMIT_NOW
+		);
+	break;
+
+	case FAUDIOOP_SETFREQUENCYRATIO:
+		FAudioSourceVoice_SetFrequencyRatio(
+			op->Voice,
+			op->Data.SetFrequencyRatio.Ratio,
+			FAUDIO_COMMIT_NOW
+		);
+	break;
+
+	default:
+		FAudio_assert(0 && "Unrecognized operation type!");
+	break;
+	}
+}
+
+void FAudio_OPERATIONSET_CommitAll(FAudio *audio)
+{
+	FAudio_OPERATIONSET_Operation *op, *next, **committed_end;
+
+	FAudio_PlatformLockMutex(audio->operationLock);
+	LOG_MUTEX_LOCK(audio, audio->operationLock)
+
+	if (audio->queuedOperations == NULL)
+	{
+		FAudio_PlatformUnlockMutex(audio->operationLock);
+		LOG_MUTEX_UNLOCK(audio, audio->operationLock)
+		return;
+	}
+
+	committed_end = &audio->committedOperations;
+	while (*committed_end)
+	{
+		committed_end = &((*committed_end)->next);
+	}
+
+	op = audio->queuedOperations;
+	do
+	{
+		next = op->next;
+
+		*committed_end = op;
+		op->next = NULL;
+		committed_end = &op->next;
+
+		op = next;
+	} while (op != NULL);
+	audio->queuedOperations = NULL;
+
+	FAudio_PlatformUnlockMutex(audio->operationLock);
+	LOG_MUTEX_UNLOCK(audio, audio->operationLock)
+}
+
+void FAudio_OPERATIONSET_Commit(FAudio *audio, uint32_t OperationSet)
+{
+	FAudio_OPERATIONSET_Operation *op, *next, *prev, **committed_end;
+
+	FAudio_PlatformLockMutex(audio->operationLock);
+	LOG_MUTEX_LOCK(audio, audio->operationLock)
+
+	if (audio->queuedOperations == NULL)
+	{
+		FAudio_PlatformUnlockMutex(audio->operationLock);
+		LOG_MUTEX_UNLOCK(audio, audio->operationLock)
+		return;
+	}
+
+	committed_end = &audio->committedOperations;
+	while (*committed_end)
+	{
+		committed_end = &((*committed_end)->next);
+	}
+
+	op = audio->queuedOperations;
+	prev = NULL;
+	do
+	{
+		next = op->next;
+		if (op->OperationSet == OperationSet)
+		{
+			if (prev == NULL) /* Start of linked list */
+			{
+				audio->queuedOperations = next;
+			}
+			else
+			{
+				prev->next = next;
+			}
+
+			*committed_end = op;
+			op->next = NULL;
+			committed_end = &op->next;
+		}
+		else
+		{
+			prev = op;
+		}
+		op = next;
+	} while (op != NULL);
+
+	FAudio_PlatformUnlockMutex(audio->operationLock);
+	LOG_MUTEX_UNLOCK(audio, audio->operationLock)
+}
+
+void FAudio_OPERATIONSET_Execute(FAudio *audio)
+{
+	FAudio_OPERATIONSET_Operation *op, *next;
+
+	FAudio_PlatformLockMutex(audio->operationLock);
+	LOG_MUTEX_LOCK(audio, audio->operationLock)
+
+	op = audio->committedOperations;
+	while (op != NULL)
+	{
+		next = op->next;
+		ExecuteOperation(op);
+		DeleteOperation(op, audio->pFree);
+		op = next;
+	}
+	audio->committedOperations = NULL;
+
+	FAudio_PlatformUnlockMutex(audio->operationLock);
+	LOG_MUTEX_UNLOCK(audio, audio->operationLock)
+}
+
+/* OperationSet Compilation */
+
+static inline FAudio_OPERATIONSET_Operation* QueueOperation(
+	FAudioVoice *voice,
+	FAudio_OPERATIONSET_Type type,
+	uint32_t operationSet
+) {
+	FAudio_OPERATIONSET_Operation *latest;
+	FAudio_OPERATIONSET_Operation *newop = voice->audio->pMalloc(
+		sizeof(FAudio_OPERATIONSET_Operation)
+	);
+
+	newop->Type = type;
+	newop->Voice = voice;
+	newop->OperationSet = operationSet;
+	newop->next = NULL;
+
+	if (voice->audio->queuedOperations == NULL)
+	{
+		voice->audio->queuedOperations = newop;
+	}
+	else
+	{
+		latest = voice->audio->queuedOperations;
+		while (latest->next != NULL)
+		{
+			latest = latest->next;
+		}
+		latest->next = newop;
+	}
+
+	return newop;
+}
+
+void FAudio_OPERATIONSET_QueueEnableEffect(
+	FAudioVoice *voice,
+	uint32_t EffectIndex,
+	uint32_t OperationSet
+) {
+	FAudio_OPERATIONSET_Operation *op;
+
+	FAudio_PlatformLockMutex(voice->audio->operationLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->audio->operationLock)
+
+	op = QueueOperation(
+		voice,
+		FAUDIOOP_ENABLEEFFECT,
+		OperationSet
+	);
+
+	op->Data.EnableEffect.EffectIndex = EffectIndex;
+
+	FAudio_PlatformUnlockMutex(voice->audio->operationLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->audio->operationLock)
+}
+
+void FAudio_OPERATIONSET_QueueDisableEffect(
+	FAudioVoice *voice,
+	uint32_t EffectIndex,
+	uint32_t OperationSet
+) {
+	FAudio_OPERATIONSET_Operation *op;
+
+	FAudio_PlatformLockMutex(voice->audio->operationLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->audio->operationLock)
+
+	op = QueueOperation(
+		voice,
+		FAUDIOOP_DISABLEEFFECT,
+		OperationSet
+	);
+
+	op->Data.DisableEffect.EffectIndex = EffectIndex;
+
+	FAudio_PlatformUnlockMutex(voice->audio->operationLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->audio->operationLock)
+}
+
+void FAudio_OPERATIONSET_QueueSetEffectParameters(
+	FAudioVoice *voice,
+	uint32_t EffectIndex,
+	const void *pParameters,
+	uint32_t ParametersByteSize,
+	uint32_t OperationSet
+) {
+	FAudio_OPERATIONSET_Operation *op;
+
+	FAudio_PlatformLockMutex(voice->audio->operationLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->audio->operationLock)
+
+	op = QueueOperation(
+		voice,
+		FAUDIOOP_SETEFFECTPARAMETERS,
+		OperationSet
+	);
+
+	op->Data.SetEffectParameters.EffectIndex = EffectIndex;
+	op->Data.SetEffectParameters.pParameters = voice->audio->pMalloc(
+		ParametersByteSize
+	);
+	FAudio_memcpy(
+		op->Data.SetEffectParameters.pParameters,
+		pParameters,
+		ParametersByteSize
+	);
+	op->Data.SetEffectParameters.ParametersByteSize = ParametersByteSize;
+
+	FAudio_PlatformUnlockMutex(voice->audio->operationLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->audio->operationLock)
+}
+
+void FAudio_OPERATIONSET_QueueSetFilterParameters(
+	FAudioVoice *voice,
+	const FAudioFilterParameters *pParameters,
+	uint32_t OperationSet
+) {
+	FAudio_OPERATIONSET_Operation *op;
+
+	FAudio_PlatformLockMutex(voice->audio->operationLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->audio->operationLock)
+
+	op = QueueOperation(
+		voice,
+		FAUDIOOP_SETFILTERPARAMETERS,
+		OperationSet
+	);
+
+	FAudio_memcpy(
+		&op->Data.SetFilterParameters.Parameters,
+		pParameters,
+		sizeof(FAudioFilterParameters)
+	);
+
+	FAudio_PlatformUnlockMutex(voice->audio->operationLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->audio->operationLock)
+}
+
+void FAudio_OPERATIONSET_QueueSetOutputFilterParameters(
+	FAudioVoice *voice,
+	FAudioVoice *pDestinationVoice,
+	const FAudioFilterParameters *pParameters,
+	uint32_t OperationSet
+) {
+	FAudio_OPERATIONSET_Operation *op;
+
+	FAudio_PlatformLockMutex(voice->audio->operationLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->audio->operationLock)
+
+	op = QueueOperation(
+		voice,
+		FAUDIOOP_SETOUTPUTFILTERPARAMETERS,
+		OperationSet
+	);
+
+	op->Data.SetOutputFilterParameters.pDestinationVoice = pDestinationVoice;
+	FAudio_memcpy(
+		&op->Data.SetOutputFilterParameters.Parameters,
+		pParameters,
+		sizeof(FAudioFilterParameters)
+	);
+
+	FAudio_PlatformUnlockMutex(voice->audio->operationLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->audio->operationLock)
+}
+
+void FAudio_OPERATIONSET_QueueSetVolume(
+	FAudioVoice *voice,
+	float Volume,
+	uint32_t OperationSet
+) {
+	FAudio_OPERATIONSET_Operation *op;
+
+	FAudio_PlatformLockMutex(voice->audio->operationLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->audio->operationLock)
+
+	op = QueueOperation(
+		voice,
+		FAUDIOOP_SETVOLUME,
+		OperationSet
+	);
+
+	op->Data.SetVolume.Volume = Volume;
+
+	FAudio_PlatformUnlockMutex(voice->audio->operationLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->audio->operationLock)
+}
+
+void FAudio_OPERATIONSET_QueueSetChannelVolumes(
+	FAudioVoice *voice,
+	uint32_t Channels,
+	const float *pVolumes,
+	uint32_t OperationSet
+) {
+	FAudio_OPERATIONSET_Operation *op;
+
+	FAudio_PlatformLockMutex(voice->audio->operationLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->audio->operationLock)
+
+	op = QueueOperation(
+		voice,
+		FAUDIOOP_SETCHANNELVOLUMES,
+		OperationSet
+	);
+
+	op->Data.SetChannelVolumes.Channels = Channels;
+	op->Data.SetChannelVolumes.pVolumes = voice->audio->pMalloc(
+		sizeof(float) * Channels
+	);
+	FAudio_memcpy(
+		op->Data.SetChannelVolumes.pVolumes,
+		pVolumes,
+		sizeof(float) * Channels
+	);
+
+	FAudio_PlatformUnlockMutex(voice->audio->operationLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->audio->operationLock)
+}
+
+void FAudio_OPERATIONSET_QueueSetOutputMatrix(
+	FAudioVoice *voice,
+	FAudioVoice *pDestinationVoice,
+	uint32_t SourceChannels,
+	uint32_t DestinationChannels,
+	const float *pLevelMatrix,
+	uint32_t OperationSet
+) {
+	FAudio_OPERATIONSET_Operation *op;
+
+	FAudio_PlatformLockMutex(voice->audio->operationLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->audio->operationLock)
+
+	op = QueueOperation(
+		voice,
+		FAUDIOOP_SETOUTPUTMATRIX,
+		OperationSet
+	);
+
+	op->Data.SetOutputMatrix.pDestinationVoice = pDestinationVoice;
+	op->Data.SetOutputMatrix.SourceChannels = SourceChannels;
+	op->Data.SetOutputMatrix.DestinationChannels = DestinationChannels;
+	op->Data.SetOutputMatrix.pLevelMatrix = voice->audio->pMalloc(
+		sizeof(float) * SourceChannels * DestinationChannels
+	);
+	FAudio_memcpy(
+		op->Data.SetOutputMatrix.pLevelMatrix,
+		pLevelMatrix,
+		sizeof(float) * SourceChannels * DestinationChannels
+	);
+
+	FAudio_PlatformUnlockMutex(voice->audio->operationLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->audio->operationLock)
+}
+
+void FAudio_OPERATIONSET_QueueStart(
+	FAudioSourceVoice *voice,
+	uint32_t Flags,
+	uint32_t OperationSet
+) {
+	FAudio_OPERATIONSET_Operation *op;
+
+	FAudio_PlatformLockMutex(voice->audio->operationLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->audio->operationLock)
+
+	op = QueueOperation(
+		voice,
+		FAUDIOOP_START,
+		OperationSet
+	);
+
+	op->Data.Start.Flags = Flags;
+
+	FAudio_PlatformUnlockMutex(voice->audio->operationLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->audio->operationLock)
+}
+
+void FAudio_OPERATIONSET_QueueStop(
+	FAudioSourceVoice *voice,
+	uint32_t Flags,
+	uint32_t OperationSet
+) {
+	FAudio_OPERATIONSET_Operation *op;
+
+	FAudio_PlatformLockMutex(voice->audio->operationLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->audio->operationLock)
+
+	op = QueueOperation(
+		voice,
+		FAUDIOOP_STOP,
+		OperationSet
+	);
+
+	op->Data.Stop.Flags = Flags;
+
+	FAudio_PlatformUnlockMutex(voice->audio->operationLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->audio->operationLock)
+}
+
+void FAudio_OPERATIONSET_QueueExitLoop(
+	FAudioSourceVoice *voice,
+	uint32_t OperationSet
+) {
+	FAudio_PlatformLockMutex(voice->audio->operationLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->audio->operationLock)
+
+	QueueOperation(
+		voice,
+		FAUDIOOP_EXITLOOP,
+		OperationSet
+	);
+
+	/* No special data for ExitLoop */
+
+	FAudio_PlatformUnlockMutex(voice->audio->operationLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->audio->operationLock)
+}
+
+void FAudio_OPERATIONSET_QueueSetFrequencyRatio(
+	FAudioSourceVoice *voice,
+	float Ratio,
+	uint32_t OperationSet
+) {
+	FAudio_OPERATIONSET_Operation *op;
+
+	FAudio_PlatformLockMutex(voice->audio->operationLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->audio->operationLock)
+
+	op = QueueOperation(
+		voice,
+		FAUDIOOP_SETFREQUENCYRATIO,
+		OperationSet
+	);
+
+	op->Data.SetFrequencyRatio.Ratio = Ratio;
+
+	FAudio_PlatformUnlockMutex(voice->audio->operationLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->audio->operationLock)
+}
+
+/* Called when releasing the engine */
+
+void FAudio_OPERATIONSET_ClearAll(FAudio *audio)
+{
+	FAudio_OPERATIONSET_Operation *current, *next;
+
+	FAudio_PlatformLockMutex(audio->operationLock);
+	LOG_MUTEX_LOCK(audio, audio->operationLock)
+
+	current = audio->queuedOperations;
+	while (current != NULL)
+	{
+		next = current->next;
+		DeleteOperation(current, audio->pFree);
+		current = next;
+	}
+	audio->queuedOperations = NULL;
+
+	FAudio_PlatformUnlockMutex(audio->operationLock);
+	LOG_MUTEX_UNLOCK(audio, audio->operationLock)
+}
+
+/* Called when releasing a voice */
+
+static inline void RemoveFromList(
+	FAudioVoice *voice,
+	FAudio_OPERATIONSET_Operation **list
+) {
+	FAudio_OPERATIONSET_Operation *current, *next, *prev;
+
+	current = *list;
+	prev = NULL;
+	while (current != NULL)
+	{
+		const uint8_t baseVoice = (voice == current->Voice);
+		const uint8_t dstVoice = (
+			current->Type == FAUDIOOP_SETOUTPUTFILTERPARAMETERS &&
+			voice == current->Data.SetOutputFilterParameters.pDestinationVoice
+		) || (
+			current->Type == FAUDIOOP_SETOUTPUTMATRIX &&
+			voice == current->Data.SetOutputMatrix.pDestinationVoice
+		);
+
+		next = current->next;
+		if (baseVoice || dstVoice)
+		{
+			if (prev == NULL) /* Start of linked list */
+			{
+				*list = next;
+			}
+			else
+			{
+				prev->next = next;
+			}
+
+			DeleteOperation(current, voice->audio->pFree);
+		}
+		else
+		{
+			prev = current;
+		}
+		current = next;
+	}
+}
+
+void FAudio_OPERATIONSET_ClearAllForVoice(FAudioVoice *voice)
+{
+	FAudio_PlatformLockMutex(voice->audio->operationLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->audio->operationLock)
+
+	RemoveFromList(voice, &voice->audio->queuedOperations);
+	RemoveFromList(voice, &voice->audio->committedOperations);
+
+	FAudio_PlatformUnlockMutex(voice->audio->operationLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->audio->operationLock)
+}
+
+/* vim: set noexpandtab shiftwidth=8 tabstop=8: */
diff --git a/libs/faudio/src/FAudio_platform_win32.c b/libs/faudio/src/FAudio_platform_win32.c
new file mode 100644
index 00000000000..f70e260c542
--- /dev/null
+++ b/libs/faudio/src/FAudio_platform_win32.c
@@ -0,0 +1,1583 @@
+/* FAudio - XAudio Reimplementation for FNA
+ *
+ * Copyright (c) 2011-2020 Ethan Lee, Luigi Auriemma, and the MonoGame Team
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
+ *
+ */
+
+#ifdef FAUDIO_WIN32_PLATFORM
+
+#include "FAudio_internal.h"
+
+#include <stddef.h>
+
+#define COBJMACROS
+#include <windows.h>
+#include <mfidl.h>
+#include <mfapi.h>
+#include <mferror.h>
+#include <mfreadwrite.h>
+#include <propvarutil.h>
+
+#include <initguid.h>
+#include <audioclient.h>
+#include <mmdeviceapi.h>
+
+DEFINE_GUID(CLSID_CWMADecMediaObject, 0x2eeb4adf, 0x4578, 0x4d10, 0xbc, 0xa7, 0xbb, 0x95, 0x5f, 0x56, 0x32, 0x0a);
+DEFINE_MEDIATYPE_GUID(MFAudioFormat_XMAudio2, FAUDIO_FORMAT_XMAUDIO2);
+
+static CRITICAL_SECTION faudio_cs = { NULL, -1, 0, 0, 0, 0 };
+static IMMDeviceEnumerator *device_enumerator;
+static HRESULT init_hr;
+
+struct FAudioWin32PlatformData
+{
+	IAudioClient *client;
+	HANDLE audioThread;
+	HANDLE stopEvent;
+};
+
+struct FAudioAudioClientThreadArgs
+{
+	WAVEFORMATEXTENSIBLE format;
+	IAudioClient *client;
+	HANDLE events[2];
+	FAudio *audio;
+	UINT updateSize;
+};
+
+void FAudio_Log(char const *msg)
+{
+	OutputDebugStringA(msg);
+}
+
+static HRESULT FAudio_FillAudioClientBuffer(
+	struct FAudioAudioClientThreadArgs *args,
+	IAudioRenderClient *client,
+	UINT frames,
+	UINT padding
+) {
+	HRESULT hr = S_OK;
+	BYTE *buffer;
+
+	while (padding + args->updateSize <= frames)
+	{
+		hr = IAudioRenderClient_GetBuffer(
+			client,
+			frames - padding,
+			&buffer
+		);
+		if (FAILED(hr)) return hr;
+
+		FAudio_zero(
+			buffer,
+			args->updateSize * args->format.Format.nBlockAlign
+		);
+
+		if (args->audio->active)
+		{
+			FAudio_INTERNAL_UpdateEngine(
+				args->audio,
+				(float*) buffer
+			);
+		}
+
+		hr = IAudioRenderClient_ReleaseBuffer(
+			client,
+			args->updateSize,
+			0
+		);
+		if (FAILED(hr)) return hr;
+
+		padding += args->updateSize;
+	}
+
+	return hr;
+}
+
+static DWORD WINAPI FAudio_AudioClientThread(void *user)
+{
+	struct FAudioAudioClientThreadArgs *args = user;
+	IAudioRenderClient *render_client;
+	HRESULT hr = S_OK;
+	UINT frames, padding = 0;
+
+	hr = IAudioClient_GetService(
+		args->client,
+		&IID_IAudioRenderClient,
+		(void **)&render_client
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to get IAudioRenderClient service!");
+
+	hr = IAudioClient_GetBufferSize(args->client, &frames);
+	FAudio_assert(!FAILED(hr) && "Failed to get IAudioClient buffer size!");
+
+	hr = FAudio_FillAudioClientBuffer(args, render_client, frames, 0);
+	FAudio_assert(!FAILED(hr) && "Failed to initialize IAudioClient buffer!");
+
+	hr = IAudioClient_Start(args->client);
+	FAudio_assert(!FAILED(hr) && "Failed to start IAudioClient!");
+
+	while (WaitForMultipleObjects(2, args->events, FALSE, INFINITE) == WAIT_OBJECT_0)
+	{
+		hr = IAudioClient_GetCurrentPadding(args->client, &padding);
+		FAudio_assert(!FAILED(hr) && "Failed to get IAudioClient current padding!");
+
+		hr = FAudio_FillAudioClientBuffer(args, render_client, frames, padding);
+		FAudio_assert(!FAILED(hr) && "Failed to fill IAudioClient buffer!");
+	}
+
+	hr = IAudioClient_Stop(args->client);
+	FAudio_assert(!FAILED(hr) && "Failed to stop IAudioClient!");
+
+	IAudioRenderClient_Release(render_client);
+	FAudio_free(args);
+	return 0;
+}
+
+void FAudio_PlatformInit(
+	FAudio *audio,
+	uint32_t flags,
+	uint32_t deviceIndex,
+	FAudioWaveFormatExtensible *mixFormat,
+	uint32_t *updateSize,
+	void** platformDevice
+) {
+	struct FAudioAudioClientThreadArgs *args;
+	struct FAudioWin32PlatformData *data;
+	REFERENCE_TIME duration;
+	WAVEFORMATEX *closest;
+	IMMDevice *device = NULL;
+	HRESULT hr;
+	HANDLE audioEvent = NULL;
+	BOOL has_sse2 = IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
+
+	FAudio_INTERNAL_InitSIMDFunctions(has_sse2, FALSE);
+
+	FAudio_PlatformAddRef();
+
+	*platformDevice = NULL;
+	if (deviceIndex > 0) return;
+
+	args = FAudio_malloc(sizeof(*args));
+	FAudio_assert(!!args && "Failed to allocate FAudio thread args!");
+
+	data = FAudio_malloc(sizeof(*data));
+	FAudio_assert(!!data && "Failed to allocate FAudio platform data!");
+	FAudio_zero(data, sizeof(*data));
+
+	args->format.Format.wFormatTag = mixFormat->Format.wFormatTag;
+	args->format.Format.nChannels = mixFormat->Format.nChannels;
+	args->format.Format.nSamplesPerSec = mixFormat->Format.nSamplesPerSec;
+	args->format.Format.nAvgBytesPerSec = mixFormat->Format.nAvgBytesPerSec;
+	args->format.Format.nBlockAlign = mixFormat->Format.nBlockAlign;
+	args->format.Format.wBitsPerSample = mixFormat->Format.wBitsPerSample;
+	args->format.Format.cbSize = mixFormat->Format.cbSize;
+
+	if (args->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
+	{
+		args->format.Samples.wValidBitsPerSample = mixFormat->Samples.wValidBitsPerSample;
+		args->format.dwChannelMask = mixFormat->dwChannelMask;
+		FAudio_memcpy(
+			&args->format.SubFormat,
+			&mixFormat->SubFormat,
+			sizeof(GUID)
+		);
+	}
+
+	audioEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+	FAudio_assert(!!audioEvent && "Failed to create FAudio thread buffer event!");
+
+	data->stopEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+	FAudio_assert(!!data->stopEvent && "Failed to create FAudio thread stop event!");
+
+	hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(
+		device_enumerator,
+		eRender,
+		eConsole,
+		&device
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to get default audio endpoint!");
+
+	hr = IMMDevice_Activate(
+		device,
+		&IID_IAudioClient,
+		CLSCTX_ALL,
+		NULL,
+		(void **)&data->client
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to create audio client!");
+	IMMDevice_Release(device);
+
+	if (flags & FAUDIO_1024_QUANTUM) duration = 21330;
+	else duration = 30000;
+
+	hr = IAudioClient_IsFormatSupported(
+		data->client,
+		AUDCLNT_SHAREMODE_SHARED,
+		&args->format.Format,
+		&closest
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to find supported audio format!");
+
+	if (closest)
+	{
+		if (closest->wFormatTag != WAVE_FORMAT_EXTENSIBLE) args->format.Format = *closest;
+		else args->format = *(WAVEFORMATEXTENSIBLE *)closest;
+		CoTaskMemFree(closest);
+	}
+
+	hr = IAudioClient_Initialize(
+		data->client,
+		AUDCLNT_SHAREMODE_SHARED,
+		AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
+		duration,
+		0,
+		&args->format.Format,
+		&GUID_NULL
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to initialize audio client!");
+
+	hr = IAudioClient_SetEventHandle(data->client, audioEvent);
+	FAudio_assert(!FAILED(hr) && "Failed to set audio client event!");
+
+	mixFormat->Format.wFormatTag = args->format.Format.wFormatTag;
+	mixFormat->Format.nChannels = args->format.Format.nChannels;
+	mixFormat->Format.nSamplesPerSec = args->format.Format.nSamplesPerSec;
+	mixFormat->Format.nAvgBytesPerSec = args->format.Format.nAvgBytesPerSec;
+	mixFormat->Format.nBlockAlign = args->format.Format.nBlockAlign;
+	mixFormat->Format.wBitsPerSample = args->format.Format.wBitsPerSample;
+
+	if (args->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
+	{
+		mixFormat->Format.cbSize = sizeof(FAudioWaveFormatExtensible) - sizeof(FAudioWaveFormatEx);
+		mixFormat->Samples.wValidBitsPerSample = args->format.Samples.wValidBitsPerSample;
+		mixFormat->dwChannelMask = args->format.dwChannelMask;
+		FAudio_memcpy(
+			&mixFormat->SubFormat,
+			&args->format.SubFormat,
+			sizeof(GUID)
+		);
+	}
+	else
+	{
+		mixFormat->Format.cbSize = sizeof(FAudioWaveFormatEx);
+	}
+
+	args->client = data->client;
+	args->events[0] = audioEvent;
+	args->events[1] = data->stopEvent;
+	args->audio = audio;
+	args->updateSize = args->format.Format.nSamplesPerSec / 100;
+
+	data->audioThread = CreateThread(NULL, 0, &FAudio_AudioClientThread, args, 0, NULL);
+	FAudio_assert(!!data->audioThread && "Failed to create audio client thread!");
+
+	*updateSize = args->updateSize;
+	*platformDevice = data;
+	return;
+}
+
+void FAudio_PlatformQuit(void* platformDevice)
+{
+	struct FAudioWin32PlatformData *data = platformDevice;
+
+	SetEvent(data->stopEvent);
+	WaitForSingleObject(data->audioThread, INFINITE);
+	if (data->client) IAudioClient_Release(data->client);
+	FAudio_PlatformRelease();
+}
+
+void FAudio_PlatformAddRef()
+{
+	HRESULT hr;
+	EnterCriticalSection(&faudio_cs);
+	if (!device_enumerator)
+	{
+		init_hr = CoInitialize(NULL);
+		hr = CoCreateInstance(
+			&CLSID_MMDeviceEnumerator,
+			NULL,
+			CLSCTX_INPROC_SERVER,
+			&IID_IMMDeviceEnumerator,
+			(void**)&device_enumerator
+		);
+		FAudio_assert(!FAILED(hr) && "CoCreateInstance failed!");
+	}
+	else IMMDeviceEnumerator_AddRef(device_enumerator);
+	LeaveCriticalSection(&faudio_cs);
+}
+
+void FAudio_PlatformRelease()
+{
+	EnterCriticalSection(&faudio_cs);
+	if (!IMMDeviceEnumerator_Release(device_enumerator))
+	{
+		device_enumerator = NULL;
+		if (SUCCEEDED(init_hr)) CoUninitialize();
+	}
+	LeaveCriticalSection(&faudio_cs);
+}
+
+uint32_t FAudio_PlatformGetDeviceCount(void)
+{
+	IMMDevice *device;
+	uint32_t count;
+	HRESULT hr;
+
+	FAudio_PlatformAddRef();
+	hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(
+		device_enumerator,
+		eRender,
+		eConsole,
+		&device
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to get default audio endpoint!");
+
+	IMMDevice_Release(device);
+	FAudio_PlatformRelease();
+
+	return 1;
+}
+
+uint32_t FAudio_PlatformGetDeviceDetails(
+	uint32_t index,
+	FAudioDeviceDetails *details
+) {
+	WAVEFORMATEXTENSIBLE *ext;
+	WAVEFORMATEX *format;
+	IAudioClient *client;
+	IMMDevice *device;
+	uint32_t ret = 0;
+	HRESULT hr;
+	WCHAR *str;
+
+	FAudio_memset(details, 0, sizeof(FAudioDeviceDetails));
+	if (index > 0) return FAUDIO_E_INVALID_CALL;
+
+	FAudio_PlatformAddRef();
+
+	hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(
+		device_enumerator,
+		eRender,
+		eConsole,
+		&device
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to get default audio endpoint!");
+
+	details->Role = FAudioGlobalDefaultDevice;
+
+	hr = IMMDevice_GetId(device, &str);
+	FAudio_assert(!FAILED(hr) && "Failed to get audio endpoint id!");
+
+	lstrcpynW(details->DeviceID, str, ARRAYSIZE(details->DeviceID) - 1);
+	lstrcpynW(details->DisplayName, str, ARRAYSIZE(details->DisplayName) - 1);
+	CoTaskMemFree(str);
+
+	hr = IMMDevice_Activate(
+		device,
+		&IID_IAudioClient,
+		CLSCTX_ALL,
+		NULL,
+		(void **)&client
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to activate audio client!");
+
+	hr = IAudioClient_GetMixFormat(client, &format);
+	FAudio_assert(!FAILED(hr) && "Failed to get audio client mix format!");
+
+	details->OutputFormat.Format.wFormatTag = format->wFormatTag;
+	details->OutputFormat.Format.nChannels = format->nChannels;
+	details->OutputFormat.Format.nSamplesPerSec = format->nSamplesPerSec;
+	details->OutputFormat.Format.nAvgBytesPerSec = format->nAvgBytesPerSec;
+	details->OutputFormat.Format.nBlockAlign = format->nBlockAlign;
+	details->OutputFormat.Format.wBitsPerSample = format->wBitsPerSample;
+	details->OutputFormat.Format.cbSize = format->cbSize;
+
+	if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
+	{
+		ext = (WAVEFORMATEXTENSIBLE *)format;
+		details->OutputFormat.Samples.wValidBitsPerSample = ext->Samples.wValidBitsPerSample;
+		details->OutputFormat.dwChannelMask = ext->dwChannelMask;
+		FAudio_memcpy(
+			&details->OutputFormat.SubFormat,
+			&ext->SubFormat,
+			sizeof(GUID)
+		);
+	}
+
+	IAudioClient_Release(client);
+
+	IMMDevice_Release(device);
+
+	FAudio_PlatformRelease();
+
+	return ret;
+}
+
+FAudioMutex FAudio_PlatformCreateMutex(void)
+{
+	CRITICAL_SECTION *cs;
+
+	cs = FAudio_malloc(sizeof(CRITICAL_SECTION));
+	if (!cs) return NULL;
+
+	InitializeCriticalSection(cs);
+
+	return cs;
+}
+
+void FAudio_PlatformLockMutex(FAudioMutex mutex)
+{
+	if (mutex) EnterCriticalSection(mutex);
+}
+
+void FAudio_PlatformUnlockMutex(FAudioMutex mutex)
+{
+	if (mutex) LeaveCriticalSection(mutex);
+}
+
+void FAudio_PlatformDestroyMutex(FAudioMutex mutex)
+{
+	if (mutex) DeleteCriticalSection(mutex);
+	FAudio_free(mutex);
+}
+
+struct FAudioThreadArgs
+{
+	FAudioThreadFunc func;
+	const char *name;
+	void* data;
+};
+
+static DWORD WINAPI FaudioThreadWrapper(void *user)
+{
+	struct FAudioThreadArgs *args = user;
+	DWORD ret;
+
+	ret = args->func(args->data);
+
+	FAudio_free(args);
+	return ret;
+}
+
+FAudioThread FAudio_PlatformCreateThread(
+	FAudioThreadFunc func,
+	const char *name,
+	void* data
+) {
+	struct FAudioThreadArgs *args;
+
+	if (!(args = FAudio_malloc(sizeof(*args)))) return NULL;
+	args->func = func;
+	args->name = name;
+	args->data = data;
+
+	return CreateThread(NULL, 0, &FaudioThreadWrapper, args, 0, NULL);
+}
+
+void FAudio_PlatformWaitThread(FAudioThread thread, int32_t *retval)
+{
+	WaitForSingleObject(thread, INFINITE);
+	GetExitCodeThread(thread, (DWORD *)retval);
+}
+
+void FAudio_PlatformThreadPriority(FAudioThreadPriority priority)
+{
+	/* FIXME */
+}
+
+uint64_t FAudio_PlatformGetThreadID(void)
+{
+	return GetCurrentThreadId();
+}
+
+void FAudio_sleep(uint32_t ms)
+{
+	Sleep(ms);
+}
+
+uint32_t FAudio_timems()
+{
+	return GetTickCount();
+}
+
+/* FAudio I/O */
+
+static size_t FAUDIOCALL FAudio_FILE_read(
+	void *data,
+	void *dst,
+	size_t size,
+	size_t count
+) {
+	if (!data) return 0;
+	return fread(dst, size, count, data);
+}
+
+static int64_t FAUDIOCALL FAudio_FILE_seek(
+	void *data,
+	int64_t offset,
+	int whence
+) {
+	if (!data) return -1;
+	fseek(data, offset, whence);
+	return ftell(data);
+}
+
+static int FAUDIOCALL FAudio_FILE_close(void *data)
+{
+	if (!data) return 0;
+	fclose(data);
+	return 0;
+}
+
+FAudioIOStream* FAudio_fopen(const char *path)
+{
+	FAudioIOStream *io;
+
+	io = (FAudioIOStream*) FAudio_malloc(sizeof(FAudioIOStream));
+	if (!io) return NULL;
+
+	io->data = fopen(path, "rb");
+	io->read = FAudio_FILE_read;
+	io->seek = FAudio_FILE_seek;
+	io->close = FAudio_FILE_close;
+	io->lock = FAudio_PlatformCreateMutex();
+
+	return io;
+}
+
+struct FAudio_mem
+{
+	char *mem;
+	int64_t len;
+	int64_t pos;
+};
+
+static size_t FAUDIOCALL FAudio_mem_read(
+	void *data,
+	void *dst,
+	size_t size,
+	size_t count
+) {
+	struct FAudio_mem *io = data;
+	size_t len = size * count;
+
+	if (!data) return 0;
+
+	while (len && len > (io->len - io->pos)) len -= size;
+	FAudio_memcpy(dst, io->mem + io->pos, len);
+	io->pos += len;
+
+	return len;
+}
+
+static int64_t FAUDIOCALL FAudio_mem_seek(
+	void *data,
+	int64_t offset,
+	int whence
+) {
+	struct FAudio_mem *io = data;
+	if (!data) return -1;
+
+	if (whence == SEEK_SET)
+	{
+		if (io->len > offset) io->pos = offset;
+		else io->pos = io->len;
+	}
+	if (whence == SEEK_CUR)
+	{
+		if (io->len > io->pos + offset) io->pos += offset;
+		else io->pos = io->len;
+	}
+	if (whence == SEEK_END)
+	{
+		if (io->len > offset) io->pos = io->len - offset;
+		else io->pos = 0;
+	}
+
+	return io->pos;
+}
+
+static int FAUDIOCALL FAudio_mem_close(void *data)
+{
+	if (!data) return 0;
+	FAudio_free(data);
+}
+
+FAudioIOStream* FAudio_memopen(void *mem, int len)
+{
+	struct FAudio_mem *data;
+	FAudioIOStream *io;
+
+	io = (FAudioIOStream*) FAudio_malloc(sizeof(FAudioIOStream));
+	if (!io) return NULL;
+
+	data = FAudio_malloc(sizeof(struct FAudio_mem));
+	if (!data)
+	{
+		FAudio_free(io);
+		return NULL;
+	}
+
+	data->mem = mem;
+	data->len = len;
+	data->pos = 0;
+
+	io->data = data;
+	io->read = FAudio_mem_read;
+	io->seek = FAudio_mem_seek;
+	io->close = FAudio_mem_close;
+	io->lock = FAudio_PlatformCreateMutex();
+	return io;
+}
+
+uint8_t* FAudio_memptr(FAudioIOStream *io, size_t offset)
+{
+	struct FAudio_mem *memio = io->data;
+	return memio->mem + offset;
+}
+
+void FAudio_close(FAudioIOStream *io)
+{
+	io->close(io->data);
+	FAudio_PlatformDestroyMutex((FAudioMutex) io->lock);
+	FAudio_free(io);
+}
+
+/* XNA Song implementation over Win32 MF */
+
+static FAudioWaveFormatEx activeSongFormat;
+IMFSourceReader *activeSong;
+static uint8_t *songBuffer;
+static SIZE_T songBufferSize;
+
+static float songVolume = 1.0f;
+static FAudio *songAudio = NULL;
+static FAudioMasteringVoice *songMaster = NULL;
+
+static FAudioSourceVoice *songVoice = NULL;
+static FAudioVoiceCallback callbacks;
+
+/* Internal Functions */
+
+static void XNA_SongSubmitBuffer(FAudioVoiceCallback *callback, void *pBufferContext)
+{
+	IMFMediaBuffer *media_buffer;
+	FAudioBuffer buffer;
+	IMFSample *sample;
+	HRESULT hr;
+	DWORD flags, buffer_size = 0;
+	BYTE *buffer_ptr;
+
+	LOG_FUNC_ENTER(songAudio);
+
+	FAudio_memset(&buffer, 0, sizeof(buffer));
+
+	hr = IMFSourceReader_ReadSample(
+		activeSong,
+		MF_SOURCE_READER_FIRST_AUDIO_STREAM,
+		0,
+		NULL,
+		&flags,
+		NULL,
+		&sample
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to read audio sample!");
+
+	if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
+	{
+		buffer.Flags = FAUDIO_END_OF_STREAM;
+	}
+	else
+	{
+		hr = IMFSample_ConvertToContiguousBuffer(
+			sample,
+			&media_buffer
+		);
+		FAudio_assert(!FAILED(hr) && "Failed to get sample buffer!");
+
+		hr = IMFMediaBuffer_Lock(
+			media_buffer,
+			&buffer_ptr,
+			NULL,
+			&buffer_size
+		);
+		FAudio_assert(!FAILED(hr) && "Failed to lock buffer bytes!");
+
+		if (songBufferSize < buffer_size)
+		{
+			songBufferSize = buffer_size;
+			songBuffer = FAudio_realloc(songBuffer, songBufferSize);
+			FAudio_assert(songBuffer != NULL && "Failed to allocate song buffer!");
+		}
+		FAudio_memcpy(songBuffer, buffer_ptr, buffer_size);
+
+		hr = IMFMediaBuffer_Unlock(media_buffer);
+		FAudio_assert(!FAILED(hr) && "Failed to unlock buffer bytes!");
+
+		IMFMediaBuffer_Release(media_buffer);
+		IMFSample_Release(sample);
+	}
+
+	if (buffer_size > 0)
+	{
+		buffer.AudioBytes = buffer_size;
+		buffer.pAudioData = songBuffer;
+		buffer.PlayBegin = 0;
+		buffer.PlayLength = buffer_size / activeSongFormat.nBlockAlign;
+		buffer.LoopBegin = 0;
+		buffer.LoopLength = 0;
+		buffer.LoopCount = 0;
+		buffer.pContext = NULL;
+		FAudioSourceVoice_SubmitSourceBuffer(
+			songVoice,
+			&buffer,
+			NULL
+		);
+	}
+
+	LOG_FUNC_EXIT(songAudio);
+}
+
+static void XNA_SongKill()
+{
+	if (songVoice != NULL)
+	{
+		FAudioSourceVoice_Stop(songVoice, 0, 0);
+		FAudioVoice_DestroyVoice(songVoice);
+		songVoice = NULL;
+	}
+	if (activeSong)
+	{
+		IMFSourceReader_Release(activeSong);
+		activeSong = NULL;
+	}
+	FAudio_free(songBuffer);
+	songBuffer = NULL;
+	songBufferSize = 0;
+}
+
+/* "Public" API */
+
+FAUDIOAPI void XNA_SongInit()
+{
+	HRESULT hr;
+
+	hr = MFStartup(MF_VERSION, MFSTARTUP_FULL);
+	FAudio_assert(!FAILED(hr) && "Failed to initialize Media Foundation!");
+
+	FAudioCreate(&songAudio, 0, FAUDIO_DEFAULT_PROCESSOR);
+	FAudio_CreateMasteringVoice(
+		songAudio,
+		&songMaster,
+		FAUDIO_DEFAULT_CHANNELS,
+		FAUDIO_DEFAULT_SAMPLERATE,
+		0,
+		0,
+		NULL
+	);
+}
+
+FAUDIOAPI void XNA_SongQuit()
+{
+	XNA_SongKill();
+	FAudioVoice_DestroyVoice(songMaster);
+	FAudio_Release(songAudio);
+        MFShutdown();
+}
+
+FAUDIOAPI float XNA_PlaySong(const char *name)
+{
+	IMFAttributes *attributes = NULL;
+	IMFMediaType *media_type = NULL;
+	UINT32 channels, samplerate;
+	UINT64 duration;
+	PROPVARIANT var;
+	HRESULT hr;
+	WCHAR filename_w[MAX_PATH];
+
+	LOG_FUNC_ENTER(songAudio);
+	LOG_INFO(songAudio, "name %s\n", name);
+	XNA_SongKill();
+
+	MultiByteToWideChar(CP_UTF8, 0, name, -1, filename_w, MAX_PATH);
+
+	hr = MFCreateAttributes(&attributes, 1);
+	FAudio_assert(!FAILED(hr) && "Failed to create attributes!");
+	hr = MFCreateSourceReaderFromURL(
+		filename_w,
+		attributes,
+		&activeSong
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to create source reader!");
+	IMFAttributes_Release(attributes);
+
+	hr = MFCreateMediaType(&media_type);
+	FAudio_assert(!FAILED(hr) && "Failed to create media type!");
+	hr = IMFMediaType_SetGUID(
+		media_type,
+		&MF_MT_MAJOR_TYPE,
+		&MFMediaType_Audio
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to set major type!");
+	hr = IMFMediaType_SetGUID(
+		media_type,
+		&MF_MT_SUBTYPE,
+		&MFAudioFormat_Float
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to set sub type!");
+	hr = IMFSourceReader_SetCurrentMediaType(
+		activeSong,
+		MF_SOURCE_READER_FIRST_AUDIO_STREAM,
+		NULL,
+		media_type
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to set source media type!");
+	hr = IMFSourceReader_SetStreamSelection(
+		activeSong,
+		MF_SOURCE_READER_FIRST_AUDIO_STREAM,
+		TRUE
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to select source stream!");
+	IMFMediaType_Release(media_type);
+
+	hr = IMFSourceReader_GetCurrentMediaType(
+		activeSong,
+		MF_SOURCE_READER_FIRST_AUDIO_STREAM,
+		&media_type
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to get current media type!");
+	hr = IMFMediaType_GetUINT32(
+		media_type,
+		&MF_MT_AUDIO_NUM_CHANNELS,
+		&channels
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to get channel count!");
+	hr = IMFMediaType_GetUINT32(
+		media_type,
+		&MF_MT_AUDIO_SAMPLES_PER_SECOND,
+		&samplerate
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to get sample rate!");
+	IMFMediaType_Release(media_type);
+
+	hr = IMFSourceReader_GetPresentationAttribute(
+		activeSong,
+		MF_SOURCE_READER_MEDIASOURCE,
+		&MF_PD_DURATION,
+		&var
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to get song duration!");
+        hr = PropVariantToInt64(&var, &duration);
+	FAudio_assert(!FAILED(hr) && "Failed to get song duration!");
+        PropVariantClear(&var);
+
+	activeSongFormat.wFormatTag = FAUDIO_FORMAT_IEEE_FLOAT;
+	activeSongFormat.nChannels = channels;
+	activeSongFormat.nSamplesPerSec = samplerate;
+	activeSongFormat.wBitsPerSample = sizeof(float) * 8;
+	activeSongFormat.nBlockAlign = activeSongFormat.nChannels * activeSongFormat.wBitsPerSample / 8;
+	activeSongFormat.nAvgBytesPerSec = activeSongFormat.nSamplesPerSec * activeSongFormat.nBlockAlign;
+	activeSongFormat.cbSize = 0;
+
+	/* Init voice */
+	FAudio_zero(&callbacks, sizeof(FAudioVoiceCallback));
+	callbacks.OnBufferEnd = XNA_SongSubmitBuffer;
+	FAudio_CreateSourceVoice(
+		songAudio,
+		&songVoice,
+		&activeSongFormat,
+		0,
+		1.0f, /* No pitch shifting here! */
+		&callbacks,
+		NULL,
+		NULL
+	);
+	FAudioVoice_SetVolume(songVoice, songVolume, 0);
+	XNA_SongSubmitBuffer(NULL, NULL);
+
+	/* Finally. */
+	FAudioSourceVoice_Start(songVoice, 0, 0);
+	LOG_FUNC_EXIT(songAudio);
+	return duration / 10000000.;
+}
+
+FAUDIOAPI void XNA_PauseSong()
+{
+	if (songVoice == NULL)
+	{
+		return;
+	}
+	FAudioSourceVoice_Stop(songVoice, 0, 0);
+}
+
+FAUDIOAPI void XNA_ResumeSong()
+{
+	if (songVoice == NULL)
+	{
+		return;
+	}
+	FAudioSourceVoice_Start(songVoice, 0, 0);
+}
+
+FAUDIOAPI void XNA_StopSong()
+{
+	XNA_SongKill();
+}
+
+FAUDIOAPI void XNA_SetSongVolume(float volume)
+{
+	songVolume = volume;
+	if (songVoice != NULL)
+	{
+		FAudioVoice_SetVolume(songVoice, songVolume, 0);
+	}
+}
+
+FAUDIOAPI uint32_t XNA_GetSongEnded()
+{
+	FAudioVoiceState state;
+	if (songVoice == NULL || activeSong == NULL)
+	{
+		return 1;
+	}
+	FAudioSourceVoice_GetState(songVoice, &state, 0);
+	return state.BuffersQueued == 0;
+}
+
+FAUDIOAPI void XNA_EnableVisualization(uint32_t enable)
+{
+	/* TODO: Enable/Disable FAPO effect */
+}
+
+FAUDIOAPI uint32_t XNA_VisualizationEnabled()
+{
+	/* TODO: Query FAPO effect enabled */
+	return 0;
+}
+
+FAUDIOAPI void XNA_GetSongVisualizationData(
+	float *frequencies,
+	float *samples,
+	uint32_t count
+) {
+	/* TODO: Visualization FAPO that reads in Song samples, FFT analysis */
+}
+
+/* FAudio WMADEC implementation over Win32 MF */
+
+struct FAudioWMADEC
+{
+	IMFTransform *decoder;
+	IMFSample *output_sample;
+
+	char *output_buf;
+	size_t output_pos;
+	size_t output_size;
+	size_t input_pos;
+	size_t input_size;
+};
+
+static HRESULT FAudio_WMAMF_ProcessInput(
+	FAudioVoice *voice,
+	FAudioBuffer *buffer
+) {
+	struct FAudioWMADEC *impl = voice->src.wmadec;
+	IMFMediaBuffer *media_buffer;
+	IMFSample *sample;
+	DWORD copy_size;
+	BYTE *copy_buf;
+	HRESULT hr;
+
+	copy_size = min(buffer->AudioBytes - impl->input_pos, impl->input_size);
+	if (!copy_size) return S_FALSE;
+	LOG_INFO(voice->audio, "pushing %x bytes at %x", copy_size, impl->input_pos);
+
+	hr = MFCreateSample(&sample);
+	FAudio_assert(!FAILED(hr) && "Failed to create sample!");
+	hr = MFCreateMemoryBuffer(copy_size, &media_buffer);
+	FAudio_assert(!FAILED(hr) && "Failed to create buffer!");
+	hr = IMFMediaBuffer_SetCurrentLength(media_buffer, copy_size);
+	FAudio_assert(!FAILED(hr) && "Failed to set buffer length!");
+	hr = IMFMediaBuffer_Lock(
+		media_buffer,
+		&copy_buf,
+		NULL,
+		&copy_size
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to lock buffer bytes!");
+	FAudio_memcpy(copy_buf, buffer->pAudioData + impl->input_pos, copy_size);
+	hr = IMFMediaBuffer_Unlock(media_buffer);
+	FAudio_assert(!FAILED(hr) && "Failed to unlock buffer bytes!");
+
+	hr = IMFSample_AddBuffer(sample, media_buffer);
+	FAudio_assert(!FAILED(hr) && "Failed to buffer to sample!");
+	IMFMediaBuffer_Release(media_buffer);
+
+	hr = IMFTransform_ProcessInput(impl->decoder, 0, sample, 0);
+	IMFSample_Release(sample);
+	if (hr == MF_E_NOTACCEPTING) return S_OK;
+	if (FAILED(hr))
+	{
+		LOG_ERROR(voice->audio, "IMFTransform_ProcessInput returned %#x", hr);
+		return hr;
+	}
+
+	impl->input_pos += copy_size;
+	return S_OK;
+};
+
+static HRESULT FAudio_WMAMF_ProcessOutput(
+	FAudioVoice *voice,
+	FAudioBuffer *buffer
+) {
+	struct FAudioWMADEC *impl = voice->src.wmadec;
+	MFT_OUTPUT_DATA_BUFFER output;
+	IMFMediaBuffer *media_buffer;
+	DWORD status, copy_size;
+	BYTE *copy_buf;
+	HRESULT hr;
+
+	while (1)
+	{
+		FAudio_memset(&output, 0, sizeof(output));
+		output.pSample = impl->output_sample;
+		hr = IMFTransform_ProcessOutput(impl->decoder, 0, 1, &output, &status);
+		if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) return S_FALSE;
+		if (FAILED(hr))
+		{
+			LOG_ERROR(voice->audio, "IMFTransform_ProcessInput returned %#x", hr);
+			return hr;
+		}
+
+		if (output.dwStatus & MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE) continue;
+
+		hr = IMFSample_ConvertToContiguousBuffer(
+			output.pSample,
+			&media_buffer
+		);
+		FAudio_assert(!FAILED(hr) && "Failed to get sample buffer!");
+		hr = IMFMediaBuffer_Lock(
+			media_buffer,
+			&copy_buf,
+			NULL,
+			&copy_size
+		);
+		FAudio_assert(!FAILED(hr) && "Failed to lock buffer bytes!");
+		if (impl->output_pos + copy_size > impl->output_size)
+		{
+			impl->output_size = max(
+				impl->output_pos + copy_size,
+				impl->output_size * 3 / 2
+			);
+			impl->output_buf = voice->audio->pRealloc(
+				impl->output_buf,
+				impl->output_size
+			);
+			FAudio_assert(impl->output_buf && "Failed to resize output buffer!");
+		}
+		FAudio_memcpy(impl->output_buf + impl->output_pos, copy_buf, copy_size);
+		impl->output_pos += copy_size;
+		LOG_INFO(voice->audio, "pulled %x bytes at %x", copy_size, impl->output_pos);
+		hr = IMFMediaBuffer_Unlock(media_buffer);
+		FAudio_assert(!FAILED(hr) && "Failed to unlock buffer bytes!");
+
+		IMFMediaBuffer_Release(media_buffer);
+		if (!impl->output_sample) IMFSample_Release(output.pSample);
+	}
+
+	return S_OK;
+};
+
+static void FAudio_INTERNAL_DecodeWMAMF(
+	FAudioVoice *voice,
+	FAudioBuffer *buffer,
+	float *decodeCache,
+	uint32_t samples
+) {
+	const FAudioWaveFormatExtensible *wfx = (FAudioWaveFormatExtensible *)voice->src.format;
+	struct FAudioWMADEC *impl = voice->src.wmadec;
+	size_t samples_pos, samples_size, copy_size;
+	HRESULT hr;
+
+	LOG_FUNC_ENTER(voice->audio)
+
+	if (!impl->output_pos)
+	{
+		if (wfx->Format.wFormatTag == FAUDIO_FORMAT_EXTENSIBLE)
+		{
+			const FAudioBufferWMA *wma = &voice->src.bufferList->bufferWMA;
+			const UINT32 *output_sizes = wma->pDecodedPacketCumulativeBytes;
+
+			impl->input_size = wfx->Format.nBlockAlign;
+			impl->output_size = max(
+				impl->output_size,
+				output_sizes[wma->PacketCount - 1]
+			);
+		}
+		else
+		{
+			const FAudioXMA2WaveFormat *xwf = (const FAudioXMA2WaveFormat *)wfx;
+
+			impl->input_size = xwf->dwBytesPerBlock;
+			impl->output_size = max(
+				impl->output_size,
+				(size_t) xwf->dwSamplesEncoded *
+				voice->src.format->nChannels *
+				(voice->src.format->wBitsPerSample / 8)
+			);
+		}
+
+		impl->output_buf = voice->audio->pRealloc(
+			impl->output_buf,
+			impl->output_size
+		);
+		FAudio_assert(impl->output_buf && "Failed to allocate output buffer!");
+
+		LOG_INFO(voice->audio, "sending BOS to %p", impl->decoder);
+		hr = IMFTransform_ProcessMessage(
+			impl->decoder,
+			MFT_MESSAGE_NOTIFY_START_OF_STREAM,
+			0
+		);
+		FAudio_assert(!FAILED(hr) && "Failed to notify decoder stream start!");
+		FAudio_WMAMF_ProcessInput(voice, buffer);
+	}
+
+	samples_pos = voice->src.curBufferOffset * voice->src.format->nChannels * sizeof(float);
+	samples_size = samples * voice->src.format->nChannels * sizeof(float);
+
+	while (impl->output_pos < samples_pos + samples_size)
+	{
+		hr = FAudio_WMAMF_ProcessOutput(voice, buffer);
+		if (FAILED(hr)) goto error;
+		if (hr == S_OK) continue;
+
+		hr  = FAudio_WMAMF_ProcessInput(voice, buffer);
+		if (FAILED(hr)) goto error;
+		if (hr == S_OK) continue;
+
+		if (!impl->input_size) break;
+
+		LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);
+		hr = IMFTransform_ProcessMessage(
+			impl->decoder,
+			MFT_MESSAGE_NOTIFY_END_OF_STREAM,
+			0
+		);
+		FAudio_assert(!FAILED(hr) && "Failed to send EOS!");
+		impl->input_size = 0;
+	}
+
+	copy_size = FAudio_clamp(impl->output_pos - samples_pos, 0, samples_size);
+	FAudio_memcpy(decodeCache, impl->output_buf + samples_pos, copy_size);
+	LOG_INFO(
+		voice->audio,
+		"decoded %x / %x bytes, copied %x / %x bytes",
+		impl->output_pos,
+		impl->output_size,
+		copy_size,
+		samples_size
+	);
+
+	LOG_FUNC_EXIT(voice->audio)
+	return;
+
+error:
+	FAudio_zero(decodeCache, samples * voice->src.format->nChannels * sizeof(float));
+	LOG_FUNC_EXIT(voice->audio)
+}
+
+uint32_t FAudio_WMADEC_init(FAudioSourceVoice *voice, uint32_t type)
+{
+	static const uint8_t fake_codec_data[16] = {0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+	const FAudioWaveFormatExtensible *wfx = (FAudioWaveFormatExtensible *)voice->src.format;
+	struct FAudioWMADEC *impl;
+	MFT_OUTPUT_STREAM_INFO info = {0};
+	IMFMediaBuffer *media_buffer;
+	IMFMediaType *media_type;
+	IMFTransform *decoder;
+	HRESULT hr;
+	UINT32 i, value;
+	GUID guid;
+
+	LOG_FUNC_ENTER(voice->audio)
+
+	if (!(impl = voice->audio->pMalloc(sizeof(*impl)))) return -1;
+	FAudio_memset(impl, 0, sizeof(*impl));
+
+	hr = CoCreateInstance(
+		&CLSID_CWMADecMediaObject,
+		0,
+		CLSCTX_INPROC_SERVER,
+		&IID_IMFTransform,
+		(void **)&decoder
+	);
+	if (FAILED(hr))
+	{
+		voice->audio->pFree(impl->output_buf);
+		return -2;
+	}
+
+	hr = MFCreateMediaType(&media_type);
+	FAudio_assert(!FAILED(hr) && "Failed create media type!");
+	hr = IMFMediaType_SetGUID(
+		media_type,
+		&MF_MT_MAJOR_TYPE,
+		&MFMediaType_Audio
+	);
+	FAudio_assert(!FAILED(hr) && "Failed set media major type!");
+
+	switch (type)
+	{
+	case FAUDIO_FORMAT_WMAUDIO2:
+		hr = IMFMediaType_SetBlob(
+			media_type,
+			&MF_MT_USER_DATA,
+			(void *)fake_codec_data,
+			sizeof(fake_codec_data)
+		);
+		FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
+		hr = IMFMediaType_SetGUID(
+			media_type,
+			&MF_MT_SUBTYPE,
+			&MFAudioFormat_WMAudioV8
+		);
+		FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
+		hr = IMFMediaType_SetUINT32(
+			media_type,
+			&MF_MT_AUDIO_BLOCK_ALIGNMENT,
+			wfx->Format.nBlockAlign
+		);
+		FAudio_assert(!FAILED(hr) && "Failed set input block align!");
+		break;
+	case FAUDIO_FORMAT_WMAUDIO3:
+		hr = IMFMediaType_SetBlob(
+			media_type,
+			&MF_MT_USER_DATA,
+			(void *)&wfx->Samples,
+			wfx->Format.cbSize
+		);
+		FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
+		hr = IMFMediaType_SetGUID(
+			media_type,
+			&MF_MT_SUBTYPE,
+			&MFAudioFormat_WMAudioV9
+		);
+		FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
+		hr = IMFMediaType_SetUINT32(
+			media_type,
+			&MF_MT_AUDIO_BLOCK_ALIGNMENT,
+			wfx->Format.nBlockAlign
+		);
+		FAudio_assert(!FAILED(hr) && "Failed set input block align!");
+		break;
+	case FAUDIO_FORMAT_WMAUDIO_LOSSLESS:
+		hr = IMFMediaType_SetBlob(
+			media_type,
+			&MF_MT_USER_DATA,
+			(void *)&wfx->Samples,
+			wfx->Format.cbSize
+		);
+		FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
+		hr = IMFMediaType_SetGUID(
+			media_type,
+			&MF_MT_SUBTYPE,
+			&MFAudioFormat_WMAudio_Lossless
+		);
+		FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
+		hr = IMFMediaType_SetUINT32(
+			media_type,
+			&MF_MT_AUDIO_BLOCK_ALIGNMENT,
+			wfx->Format.nBlockAlign
+		);
+		FAudio_assert(!FAILED(hr) && "Failed set input block align!");
+		break;
+	case FAUDIO_FORMAT_XMAUDIO2:
+	{
+		const FAudioXMA2WaveFormat *xwf = (const FAudioXMA2WaveFormat *)wfx;
+		hr = IMFMediaType_SetBlob(
+			media_type,
+			&MF_MT_USER_DATA,
+			(void *)&wfx->Samples,
+			wfx->Format.cbSize
+		);
+		FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
+		hr = IMFMediaType_SetGUID(
+			media_type,
+			&MF_MT_SUBTYPE,
+			&MFAudioFormat_XMAudio2
+		);
+		FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
+		hr = IMFMediaType_SetUINT32(
+			media_type,
+			&MF_MT_AUDIO_BLOCK_ALIGNMENT,
+			xwf->dwBytesPerBlock
+		);
+		FAudio_assert(!FAILED(hr) && "Failed set input block align!");
+		break;
+	}
+	default:
+		FAudio_assert(0 && "Unsupported type!");
+		break;
+	}
+
+	hr = IMFMediaType_SetUINT32(
+		media_type,
+		&MF_MT_AUDIO_BITS_PER_SAMPLE,
+		wfx->Format.wBitsPerSample
+	);
+	FAudio_assert(!FAILED(hr) && "Failed set input bits per sample!");
+	hr = IMFMediaType_SetUINT32(
+		media_type,
+		&MF_MT_AUDIO_AVG_BYTES_PER_SECOND,
+		wfx->Format.nAvgBytesPerSec
+	);
+	FAudio_assert(!FAILED(hr) && "Failed set input bytes per sample!");
+	hr = IMFMediaType_SetUINT32(
+		media_type,
+		&MF_MT_AUDIO_NUM_CHANNELS,
+		wfx->Format.nChannels
+	);
+	FAudio_assert(!FAILED(hr) && "Failed set input channel count!");
+	hr = IMFMediaType_SetUINT32(
+		media_type,
+		&MF_MT_AUDIO_SAMPLES_PER_SECOND,
+		wfx->Format.nSamplesPerSec
+	);
+	FAudio_assert(!FAILED(hr) && "Failed set input sample rate!");
+
+	hr = IMFTransform_SetInputType(
+		decoder,
+		0,
+		media_type,
+		0
+	);
+	FAudio_assert(!FAILED(hr) && "Failed set decoder input type!");
+	IMFMediaType_Release(media_type);
+
+	i = 0;
+	while (SUCCEEDED(hr))
+	{
+		hr = IMFTransform_GetOutputAvailableType(
+			decoder,
+			0,
+			i++,
+			&media_type
+		);
+		FAudio_assert(!FAILED(hr) && "Failed get output media type!");
+
+		hr = IMFMediaType_GetGUID(
+			media_type,
+			&MF_MT_MAJOR_TYPE,
+			&guid
+		);
+		FAudio_assert(!FAILED(hr) && "Failed get media major type!");
+		if (!IsEqualGUID(&MFMediaType_Audio, &guid)) goto next;
+
+		hr = IMFMediaType_GetGUID(
+			media_type,
+			&MF_MT_SUBTYPE,
+			&guid
+		);
+		FAudio_assert(!FAILED(hr) && "Failed get media major type!");
+		if (!IsEqualGUID(&MFAudioFormat_Float, &guid)) goto next;
+
+		hr = IMFMediaType_GetUINT32(
+			media_type,
+			&MF_MT_AUDIO_BITS_PER_SAMPLE,
+			&value
+		);
+		if (FAILED(hr))
+		{
+			value = 32;
+			hr = IMFMediaType_SetUINT32(
+				media_type,
+				&MF_MT_AUDIO_BITS_PER_SAMPLE,
+				value
+			);
+		}
+		FAudio_assert(!FAILED(hr) && "Failed get bits per sample!");
+		if (value != 32) goto next;
+
+		hr = IMFMediaType_GetUINT32(
+			media_type,
+			&MF_MT_AUDIO_NUM_CHANNELS,
+			&value
+		);
+		if (FAILED(hr))
+		{
+			value = wfx->Format.nChannels;
+			hr = IMFMediaType_SetUINT32(
+				media_type,
+				&MF_MT_AUDIO_NUM_CHANNELS,
+				value
+			);
+		}
+		FAudio_assert(!FAILED(hr) && "Failed get channel count!");
+		if (value != wfx->Format.nChannels) goto next;
+
+		hr = IMFMediaType_GetUINT32(
+			media_type,
+			&MF_MT_AUDIO_SAMPLES_PER_SECOND,
+			&value
+		);
+		if (FAILED(hr))
+		{
+			value = wfx->Format.nSamplesPerSec;
+			hr = IMFMediaType_SetUINT32(
+				media_type,
+				&MF_MT_AUDIO_SAMPLES_PER_SECOND,
+				value
+			);
+		}
+		FAudio_assert(!FAILED(hr) && "Failed get sample rate!");
+		if (value != wfx->Format.nSamplesPerSec) goto next;
+
+		hr = IMFMediaType_GetUINT32(
+			media_type,
+			&MF_MT_AUDIO_BLOCK_ALIGNMENT,
+			&value
+		);
+		if (FAILED(hr))
+		{
+			value = wfx->Format.nChannels * sizeof(float);
+			hr = IMFMediaType_SetUINT32(
+				media_type,
+				&MF_MT_AUDIO_BLOCK_ALIGNMENT,
+				value
+			);
+		}
+		FAudio_assert(!FAILED(hr) && "Failed get block align!");
+		if (value == wfx->Format.nChannels * sizeof(float)) break;
+
+next:
+		IMFMediaType_Release(media_type);
+	}
+	FAudio_assert(!FAILED(hr) && "Failed to find output media type!");
+	hr = IMFTransform_SetOutputType(decoder, 0, media_type, 0);
+	FAudio_assert(!FAILED(hr) && "Failed set decoder output type!");
+	IMFMediaType_Release(media_type);
+
+	hr = IMFTransform_GetOutputStreamInfo(decoder, 0, &info);
+	FAudio_assert(!FAILED(hr) && "Failed to get output stream info!");
+
+	impl->decoder = decoder;
+	if (!(info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES))
+	{
+		hr = MFCreateSample(&impl->output_sample);
+		FAudio_assert(!FAILED(hr) && "Failed to create sample!");
+		hr = MFCreateMemoryBuffer(info.cbSize, &media_buffer);
+		FAudio_assert(!FAILED(hr) && "Failed to create buffer!");
+		hr = IMFSample_AddBuffer(impl->output_sample, media_buffer);
+		FAudio_assert(!FAILED(hr) && "Failed to buffer to sample!");
+		IMFMediaBuffer_Release(media_buffer);
+	}
+
+	hr = IMFTransform_ProcessMessage(
+		decoder,
+		MFT_MESSAGE_NOTIFY_BEGIN_STREAMING,
+		0
+	);
+	FAudio_assert(!FAILED(hr) && "Failed to start decoder stream!");
+
+	voice->src.wmadec = impl;
+	voice->src.decode = FAudio_INTERNAL_DecodeWMAMF;
+
+	LOG_FUNC_EXIT(voice->audio);
+	return 0;
+}
+
+void FAudio_WMADEC_free(FAudioSourceVoice *voice)
+{
+	struct FAudioWMADEC *impl = voice->src.wmadec;
+	HRESULT hr;
+
+	LOG_FUNC_ENTER(voice->audio)
+	FAudio_PlatformLockMutex(voice->audio->sourceLock);
+	LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
+
+	if (impl->input_size)
+	{
+		LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);
+		hr = IMFTransform_ProcessMessage(
+			impl->decoder,
+			MFT_MESSAGE_NOTIFY_END_OF_STREAM,
+			0
+		);
+		FAudio_assert(!FAILED(hr) && "Failed to send EOS!");
+		impl->input_size = 0;
+	}
+	if (impl->output_pos)
+	{
+		LOG_INFO(voice->audio, "sending DRAIN to %p", impl->decoder);
+		hr = IMFTransform_ProcessMessage(
+			impl->decoder,
+			MFT_MESSAGE_COMMAND_DRAIN,
+			0
+		);
+		FAudio_assert(!FAILED(hr) && "Failed to send DRAIN!");
+		impl->output_pos = 0;
+	}
+
+	if (impl->output_sample) IMFSample_Release(impl->output_sample);
+	IMFTransform_Release(impl->decoder);
+	voice->audio->pFree(impl->output_buf);
+	voice->audio->pFree(voice->src.wmadec);
+	voice->src.wmadec = NULL;
+	voice->src.decode = NULL;
+
+	FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
+	LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
+	LOG_FUNC_EXIT(voice->audio)
+}
+
+void FAudio_WMADEC_end_buffer(FAudioSourceVoice *voice)
+{
+	struct FAudioWMADEC *impl = voice->src.wmadec;
+	HRESULT hr;
+
+	LOG_FUNC_ENTER(voice->audio)
+
+	if (impl->input_size)
+	{
+		LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);
+		hr = IMFTransform_ProcessMessage(
+			impl->decoder,
+			MFT_MESSAGE_NOTIFY_END_OF_STREAM,
+			0
+		);
+		FAudio_assert(!FAILED(hr) && "Failed to send EOS!");
+		impl->input_size = 0;
+	}
+	impl->output_pos = 0;
+	impl->input_pos = 0;
+
+	LOG_FUNC_EXIT(voice->audio)
+}
+
+#else
+
+extern int this_tu_is_empty;
+
+#endif /* FAUDIO_WIN32_PLATFORM */
diff --git a/libs/faudio/src/matrix_defaults.inl b/libs/faudio/src/matrix_defaults.inl
new file mode 100644
index 00000000000..2262a6a0744
--- /dev/null
+++ b/libs/faudio/src/matrix_defaults.inl
@@ -0,0 +1,147 @@
+/* This was generated by making 8 sources and 8 submixes, then assigning each
+ * submix to each voice and dumping the output matrix. Terrible, but it worked!
+ */
+{
+	/* 1 x 1 */
+	{ 1.000000000f },
+	/* 1 x 2 */
+	{ 1.000000000f, 1.000000000f },
+	/* 1 x 3 */
+	{ 1.000000000f, 1.000000000f, 0.000000000f },
+	/* 1 x 4 */
+	{ 1.000000000f, 1.000000000f, 0.000000000f, 0.000000000f },
+	/* 1 x 5 */
+	{ 1.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f },
+	/* 1 x 6 */
+	{ 1.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f },
+	/* 1 x 7 */
+	{ 1.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f },
+	/* 1 x 8 */
+	{ 1.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f }
+},
+{
+	/* 2 x 1 */
+	{ 0.500000000f, 0.500000000f },
+	/* 2 x 2 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 1.000000000f },
+	/* 2 x 3 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f },
+	/* 2 x 4 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f },
+	/* 2 x 5 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f },
+	/* 2 x 6 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f },
+	/* 2 x 7 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f },
+	/* 2 x 8 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f }
+},
+{
+	/* 3 x 1 */
+	{ 0.333333343f, 0.333333343f, 0.333333343f },
+	/* 3 x 2 */
+	{ 0.800000012f, 0.000000000f, 0.200000003f, 0.000000000f, 0.800000012f, 0.200000003f },
+	/* 3 x 3 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f },
+	/* 3 x 4 */
+	{ 0.888888896f, 0.000000000f, 0.111111112f, 0.000000000f, 0.888888896f, 0.111111112f, 0.000000000f, 0.000000000f, 0.111111112f, 0.000000000f, 0.000000000f, 0.111111112f },
+	/* 3 x 5 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f },
+	/* 3 x 6 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f },
+	/* 3 x 7 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f },
+	/* 3 x 8 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f }
+},
+{
+	/* 4 x 1 */
+	{ 0.250000000f, 0.250000000f, 0.250000000f, 0.250000000f },
+	/* 4 x 2 */
+	{ 0.421000004f, 0.000000000f, 0.358999997f, 0.219999999f, 0.000000000f, 0.421000004f, 0.219999999f, 0.358999997f },
+	/* 4 x 3 */
+	{ 0.421000004f, 0.000000000f, 0.358999997f, 0.219999999f, 0.000000000f, 0.421000004f, 0.219999999f, 0.358999997f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f },
+	/* 4 x 4 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f },
+	/* 4 x 5 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f },
+	/* 4 x 6 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f },
+	/* 4 x 7 */
+	{ 0.939999998f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.939999998f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.500000000f, 0.500000000f, 0.000000000f, 0.000000000f, 0.796000004f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.796000004f },
+	/* 4 x 8 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f }
+},
+{
+	/* 5 x 1 */
+	{ 0.200000003f, 0.200000003f, 0.200000003f, 0.200000003f, 0.200000003f },
+	/* 5 x 2 */
+	{ 0.374222219f, 0.000000000f, 0.111111112f, 0.319111109f, 0.195555553f, 0.000000000f, 0.374222219f, 0.111111112f, 0.195555553f, 0.319111109f },
+	/* 5 x 3 */
+	{ 0.421000004f, 0.000000000f, 0.000000000f, 0.358999997f, 0.219999999f, 0.000000000f, 0.421000004f, 0.000000000f, 0.219999999f, 0.358999997f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f },
+	/* 5 x 4 */
+	{ 0.941176474f, 0.000000000f, 0.058823530f, 0.000000000f, 0.000000000f, 0.000000000f, 0.941176474f, 0.058823530f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.058823530f, 0.941176474f, 0.000000000f, 0.000000000f, 0.000000000f, 0.058823530f, 0.000000000f, 0.941176474f },
+	/* 5 x 5 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f },
+	/* 5 x 6 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f },
+	/* 5 x 7 */
+	{ 0.939999998f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.939999998f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.500000000f, 0.500000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.796000004f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.796000004f },
+	/* 5 x 8 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f }
+},
+{
+	/* 6 x 1 */
+	{ 0.166666672f, 0.166666672f, 0.166666672f, 0.166666672f, 0.166666672f, 0.166666672f },
+	/* 6 x 2 */
+	{ 0.294545442f, 0.000000000f, 0.208181813f, 0.090909094f, 0.251818180f, 0.154545456f, 0.000000000f, 0.294545442f, 0.208181813f, 0.090909094f, 0.154545456f, 0.251818180f },
+	/* 6 x 3 */
+	{ 0.324000001f, 0.000000000f, 0.229000002f, 0.000000000f, 0.277000010f, 0.170000002f, 0.000000000f, 0.324000001f, 0.229000002f, 0.000000000f, 0.170000002f, 0.277000010f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f },
+	/* 6 x 4 */
+	{ 0.558095276f, 0.000000000f, 0.394285709f, 0.047619049f, 0.000000000f, 0.000000000f, 0.000000000f, 0.558095276f, 0.394285709f, 0.047619049f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.047619049f, 0.558095276f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.047619049f, 0.000000000f, 0.558095276f },
+	/* 6 x 5 */
+	{ 0.586000025f, 0.000000000f, 0.414000005f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.586000025f, 0.414000005f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.586000025f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.586000025f },
+	/* 6 x 6 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f },
+	/* 6 x 7 */
+	{ 0.939999998f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.939999998f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.939999998f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.500000000f, 0.500000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.796000004f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.796000004f },
+	/* 6 x 8 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f }
+},
+{
+	/* 7 x 1 */
+	{ 0.143142849f, 0.143142849f, 0.143142849f, 0.142857149f, 0.143142849f, 0.143142849f, 0.143142849f },
+	/* 7 x 2 */
+	{ 0.247384623f, 0.000000000f, 0.174461529f, 0.076923080f, 0.174461529f, 0.226153851f, 0.100615382f, 0.000000000f, 0.247384623f, 0.174461529f, 0.076923080f, 0.174461529f, 0.100615382f, 0.226153851f },
+	/* 7 x 3 */
+	{ 0.268000007f, 0.000000000f, 0.188999996f, 0.000000000f, 0.188999996f, 0.245000005f, 0.108999997f, 0.000000000f, 0.268000007f, 0.188999996f, 0.000000000f, 0.188999996f, 0.108999997f, 0.245000005f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f },
+	/* 7 x 4 */
+	{ 0.463679999f, 0.000000000f, 0.327360004f, 0.040000003f, 0.000000000f, 0.168960005f, 0.000000000f, 0.000000000f, 0.463679999f, 0.327360004f, 0.040000003f, 0.000000000f, 0.000000000f, 0.168960005f, 0.000000000f, 0.000000000f, 0.000000000f, 0.040000003f, 0.327360004f, 0.431039989f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.040000003f, 0.327360004f, 0.000000000f, 0.431039989f },
+	/* 7 x 5 */
+	{ 0.483000010f, 0.000000000f, 0.340999991f, 0.000000000f, 0.000000000f, 0.175999999f, 0.000000000f, 0.000000000f, 0.483000010f, 0.340999991f, 0.000000000f, 0.000000000f, 0.000000000f, 0.175999999f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.340999991f, 0.449000001f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.340999991f, 0.000000000f, 0.449000001f },
+	/* 7 x 6 */
+	{ 0.611000001f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.223000005f, 0.000000000f, 0.000000000f, 0.611000001f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.223000005f, 0.000000000f, 0.000000000f, 0.611000001f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.432000011f, 0.568000019f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.432000011f, 0.000000000f, 0.568000019f },
+	/* 7 x 7 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f },
+	/* 7 x 8 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.707000017f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.707000017f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f }
+},
+{
+	/* 8 x 1 */
+	{ 0.125125006f, 0.125125006f, 0.125125006f, 0.125000000f, 0.125125006f, 0.125125006f, 0.125125006f, 0.125125006f },
+	/* 8 x 2 */
+	{ 0.211866662f, 0.000000000f, 0.150266662f, 0.066666670f, 0.181066677f, 0.111066669f, 0.194133341f, 0.085866667f, 0.000000000f, 0.211866662f, 0.150266662f, 0.066666670f, 0.111066669f, 0.181066677f, 0.085866667f, 0.194133341f },
+	/* 8 x 3 */
+	{ 0.226999998f, 0.000000000f, 0.160999998f, 0.000000000f, 0.194000006f, 0.119000003f, 0.208000004f, 0.092000000f, 0.000000000f, 0.226999998f, 0.160999998f, 0.000000000f, 0.119000003f, 0.194000006f, 0.092000000f, 0.208000004f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f },
+	/* 8 x 4 */
+	{ 0.466344833f, 0.000000000f, 0.329241365f, 0.034482758f, 0.000000000f, 0.000000000f, 0.169931039f, 0.000000000f, 0.000000000f, 0.466344833f, 0.329241365f, 0.034482758f, 0.000000000f, 0.000000000f, 0.000000000f, 0.169931039f, 0.000000000f, 0.000000000f, 0.000000000f, 0.034482758f, 0.466344833f, 0.000000000f, 0.433517247f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.034482758f, 0.000000000f, 0.466344833f, 0.000000000f, 0.433517247f },
+	/* 8 x 5 */
+	{ 0.483000010f, 0.000000000f, 0.340999991f, 0.000000000f, 0.000000000f, 0.000000000f, 0.175999999f, 0.000000000f, 0.000000000f, 0.483000010f, 0.340999991f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.175999999f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.483000010f, 0.000000000f, 0.449000001f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.483000010f, 0.000000000f, 0.449000001f },
+	/* 8 x 6 */
+	{ 0.518000007f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.188999996f, 0.000000000f, 0.000000000f, 0.518000007f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.188999996f, 0.000000000f, 0.000000000f, 0.518000007f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.518000007f, 0.000000000f, 0.481999993f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.518000007f, 0.000000000f, 0.481999993f },
+	/* 8 x 7 */
+	{ 0.541000009f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.541000009f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.541000009f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.287999988f, 0.287999988f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.458999991f, 0.000000000f, 0.541000009f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.458999991f, 0.000000000f, 0.541000009f },
+	/* 8 x 8 */
+	{ 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f }
+}
diff --git a/libs/faudio/src/stb.h b/libs/faudio/src/stb.h
new file mode 100644
index 00000000000..5f1724a736d
--- /dev/null
+++ b/libs/faudio/src/stb.h
@@ -0,0 +1,406 @@
+/* stb.h - v2.32 - Sean's Tool Box -- public domain -- http://nothings.org/stb.h
+          no warranty is offered or implied; use this code at your own risk
+
+   This is a single header file with a bunch of useful utilities
+   for getting stuff done in C/C++.
+
+   Documentation: http://nothings.org/stb/stb_h.html
+   Unit tests:    http://nothings.org/stb/stb.c
+
+
+ ============================================================================
+   You MUST                                                                  
+                                                                             
+      #define STB_DEFINE                                                     
+                                                                             
+   in EXACTLY _one_ C or C++ file that includes this header, BEFORE the
+   include, like this:                                                                
+                                                                             
+      #define STB_DEFINE                                                     
+      #include "stb.h"
+      
+   All other files should just #include "stb.h" without the #define.
+ ============================================================================
+
+
+Version History
+
+   2.32   stb_intcmprev, stb_uidict, fix random numbers on Linux
+   2.31   stb_ucharcmp
+   2.30   MinGW fix
+   2.29   attempt to fix use of swprintf()
+   2.28   various new functionality
+   2.27   test _WIN32 not WIN32 in STB_THREADS
+   2.26   various warning & bugfixes
+   2.25   various warning & bugfixes
+   2.24   various warning & bugfixes
+   2.23   fix 2.22
+   2.22   64-bit fixes from '!='; fix stb_sdict_copy() to have preferred name
+   2.21   utf-8 decoder rejects "overlong" encodings; attempted 64-bit improvements
+   2.20   fix to hash "copy" function--reported by someone with handle "!="
+   2.19   ???
+   2.18   stb_readdir_subdirs_mask
+   2.17   stb_cfg_dir
+   2.16   fix stb_bgio_, add stb_bgio_stat(); begin a streaming wrapper
+   2.15   upgraded hash table template to allow:
+            - aggregate keys (explicit comparison func for EMPTY and DEL keys)
+            - "static" implementations (so they can be culled if unused)
+   2.14   stb_mprintf
+   2.13   reduce identifiable strings in STB_NO_STB_STRINGS
+   2.12   fix STB_ONLY -- lots of uint32s, TRUE/FALSE things had crept in
+   2.11   fix bug in stb_dirtree_get() which caused "c://path" sorts of stuff
+   2.10   STB_F(), STB_I() inline constants (also KI,KU,KF,KD)
+   2.09   stb_box_face_vertex_axis_side
+   2.08   bugfix stb_trimwhite()
+   2.07   colored printing in windows (why are we in 1985?)
+   2.06   comparison functions are now functions-that-return-functions and
+          accept a struct-offset as a parameter (not thread-safe)
+   2.05   compile and pass tests under Linux (but no threads); thread cleanup
+   2.04   stb_cubic_bezier_1d, smoothstep, avoid dependency on registry
+   2.03   ?
+   2.02   remove integrated documentation
+   2.01   integrate various fixes; stb_force_uniprocessor
+   2.00   revised stb_dupe to use multiple hashes
+   1.99   stb_charcmp
+   1.98   stb_arr_deleten, stb_arr_insertn
+   1.97   fix stb_newell_normal()
+   1.96   stb_hash_number()
+   1.95   hack stb__rec_max; clean up recursion code to use new functions
+   1.94   stb_dirtree; rename stb_extra to stb_ptrmap
+   1.93   stb_sem_new() API cleanup (no blockflag-starts blocked; use 'extra')
+   1.92   stb_threadqueue--multi reader/writer queue, fixed size or resizeable
+   1.91   stb_bgio_* for reading disk asynchronously
+   1.90   stb_mutex uses CRITICAL_REGION; new stb_sync primitive for thread
+          joining; workqueue supports stb_sync instead of stb_semaphore
+   1.89   support ';' in constant-string wildcards; stb_mutex wrapper (can
+          implement with EnterCriticalRegion eventually)
+   1.88   portable threading API (only for win32 so far); worker thread queue
+   1.87   fix wildcard handling in stb_readdir_recursive
+   1.86   support ';' in wildcards
+   1.85   make stb_regex work with non-constant strings;
+               beginnings of stb_introspect()
+   1.84   (forgot to make notes)
+   1.83   whoops, stb_keep_if_different wasn't deleting the temp file
+   1.82   bring back stb_compress from stb_file.h for cmirror
+   1.81   various bugfixes, STB_FASTMALLOC_INIT inits FASTMALLOC in release
+   1.80   stb_readdir returns utf8; write own utf8-utf16 because lib was wrong
+   1.79   stb_write
+   1.78   calloc() support for malloc wrapper, STB_FASTMALLOC
+   1.77   STB_FASTMALLOC
+   1.76   STB_STUA - Lua-like language; (stb_image, stb_csample, stb_bilinear)
+   1.75   alloc/free array of blocks; stb_hheap bug; a few stb_ps_ funcs;
+          hash*getkey, hash*copy; stb_bitset; stb_strnicmp; bugfix stb_bst
+   1.74   stb_replaceinplace; use stdlib C function to convert utf8 to UTF-16
+   1.73   fix performance bug & leak in stb_ischar (C++ port lost a 'static')
+   1.72   remove stb_block, stb_block_manager, stb_decompress (to stb_file.h)
+   1.71   stb_trimwhite, stb_tokens_nested, etc.
+   1.70   back out 1.69 because it might problemize mixed builds; stb_filec()
+   1.69   (stb_file returns 'char *' in C++)
+   1.68   add a special 'tree root' data type for stb_bst; stb_arr_end
+   1.67   full C++ port. (stb_block_manager)
+   1.66   stb_newell_normal
+   1.65   stb_lex_item_wild -- allow wildcard items which MUST match entirely
+   1.64   stb_data
+   1.63   stb_log_name
+   1.62   stb_define_sort; C++ cleanup
+   1.61   stb_hash_fast -- Paul Hsieh's hash function (beats Bob Jenkins'?)
+   1.60   stb_delete_directory_recursive
+   1.59   stb_readdir_recursive
+   1.58   stb_bst variant with parent pointer for O(1) iteration, not O(log N)
+   1.57   replace LCG random with Mersenne Twister (found a public domain one)
+   1.56   stb_perfect_hash, stb_ischar, stb_regex
+   1.55   new stb_bst API allows multiple BSTs per node (e.g. secondary keys)
+   1.54   bugfix: stb_define_hash, stb_wildmatch, regexp
+   1.53   stb_define_hash; recoded stb_extra, stb_sdict use it
+   1.52   stb_rand_define, stb_bst, stb_reverse
+   1.51   fix 'stb_arr_setlen(NULL, 0)'
+   1.50   stb_wordwrap
+   1.49   minor improvements to enable the scripting language
+   1.48   better approach for stb_arr using stb_malloc; more invasive, clearer
+   1.47   stb_lex (lexes stb.h at 1.5ML/s on 3Ghz P4; 60/70% of optimal/flex)
+   1.46   stb_wrapper_*, STB_MALLOC_WRAPPER
+   1.45   lightly tested DFA acceleration of regexp searching
+   1.44   wildcard matching & searching; regexp matching & searching
+   1.43   stb_temp
+   1.42   allow stb_arr to use stb_malloc/realloc; note this is global
+   1.41   make it compile in C++; (disable stb_arr in C++)
+   1.40   stb_dupe tweak; stb_swap; stb_substr
+   1.39   stb_dupe; improve stb_file_max to be less stupid
+   1.38   stb_sha1_file: generate sha1 for file, even > 4GB
+   1.37   stb_file_max; partial support for utf8 filenames in Windows
+   1.36   remove STB__NO_PREFIX - poor interaction with IDE, not worth it
+          streamline stb_arr to make it separately publishable
+   1.35   bugfixes for stb_sdict, stb_malloc(0), stristr
+   1.34   (streaming interfaces for stb_compress)
+   1.33   stb_alloc; bug in stb_getopt; remove stb_overflow
+   1.32   (stb_compress returns, smaller&faster; encode window & 64-bit len)
+   1.31   stb_prefix_count
+   1.30   (STB__NO_PREFIX - remove stb_ prefixes for personal projects)
+   1.29   stb_fput_varlen64, etc.
+   1.28   stb_sha1
+   1.27   ?
+   1.26   stb_extra
+   1.25   ?
+   1.24   stb_copyfile
+   1.23   stb_readdir
+   1.22   ?
+   1.21   ?
+   1.20   ?
+   1.19   ?
+   1.18   ?
+   1.17   ?
+   1.16   ?
+   1.15   stb_fixpath, stb_splitpath, stb_strchr2
+   1.14   stb_arr
+   1.13   ?stb, stb_log, stb_fatal
+   1.12   ?stb_hash2
+   1.11   miniML
+   1.10   stb_crc32, stb_adler32
+   1.09   stb_sdict
+   1.08   stb_bitreverse, stb_ispow2, stb_big32
+          stb_fopen, stb_fput_varlen, stb_fput_ranged
+          stb_fcmp, stb_feq
+   1.07   (stb_encompress)
+   1.06   stb_compress
+   1.05   stb_tokens, (stb_hheap)
+   1.04   stb_rand
+   1.03   ?(s-strings)
+   1.02   ?stb_filelen, stb_tokens
+   1.01   stb_tolower
+   1.00   stb_hash, stb_intcmp
+          stb_file, stb_stringfile, stb_fgets
+          stb_prefix, stb_strlower, stb_strtok
+          stb_image
+          (stb_array), (stb_arena)
+
+Parenthesized items have since been removed.
+
+LICENSE
+
+ See end of file for license information.
+
+CREDITS
+
+ Written by Sean Barrett.
+
+ Fixes:
+  Philipp Wiesemann
+  Robert Nix
+  r-lyeh
+  blackpawn
+  github:Mojofreem
+  Ryan Whitworth
+  Vincent Isambart
+  Mike Sartain
+  Eugene Opalev
+  Tim Sjostrand
+  github:infatum
+  Dave Butler (Croepha)
+*/
+
+#ifndef STB__INCLUDE_STB_H
+#define STB__INCLUDE_STB_H
+
+#define STB_VERSION  1
+
+/* In addition to trimming out all the stuff FAudio does not use, we are also
+ * binding various stdlib functions stb.h uses to FAudio's stdlib.
+ * -flibit
+ */
+#ifndef FAUDIO_WIN32_PLATFORM
+#ifdef memcpy /* Thanks Apple! */
+#undef memcpy
+#endif
+#define memcpy FAudio_memcpy
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+//                         Miscellany
+//
+
+STB_EXTERN void stb_swap(void *p, void *q, size_t sz);
+
+#ifdef STB_DEFINE
+typedef struct { char d[4]; } stb__4;
+typedef struct { char d[8]; } stb__8;
+
+// optimize the small cases, though you shouldn't be calling this for those!
+void stb_swap(void *p, void *q, size_t sz)
+{
+   char buffer[256];
+   if (p == q) return;
+   if (sz == 4) {
+      stb__4 temp    = * ( stb__4 *) p;
+      * (stb__4 *) p = * ( stb__4 *) q;
+      * (stb__4 *) q = temp;
+      return;
+   } else if (sz == 8) {
+      stb__8 temp    = * ( stb__8 *) p;
+      * (stb__8 *) p = * ( stb__8 *) q;
+      * (stb__8 *) q = temp;
+      return;
+   }
+
+   while (sz > sizeof(buffer)) {
+      stb_swap(p, q, sizeof(buffer));
+      p = (char *) p + sizeof(buffer);
+      q = (char *) q + sizeof(buffer);
+      sz -= sizeof(buffer);
+   }
+
+   memcpy(buffer, p     , sz);
+   memcpy(p     , q     , sz);
+   memcpy(q     , buffer, sz);
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+//               Random Numbers via Meresenne Twister or LCG
+//
+
+STB_EXTERN unsigned int  stb_srandLCG(unsigned int seed);
+STB_EXTERN unsigned int  stb_randLCG(void);
+
+STB_EXTERN void          stb_srand(unsigned int seed);
+STB_EXTERN unsigned int  stb_rand(void);
+STB_EXTERN double        stb_frand(void);
+
+#define stb_rand_define(x,y)                                         \
+                                                                     \
+   unsigned int  x(void)                                             \
+   {                                                                 \
+      static unsigned int  stb__rand = y;                            \
+      stb__rand = stb__rand * 2147001325 + 715136305; /* BCPL */     \
+      return 0x31415926 ^ ((stb__rand >> 16) + (stb__rand << 16));   \
+   }
+
+#ifdef STB_DEFINE
+static unsigned int  stb__rand_seed=0;
+
+unsigned int  stb_srandLCG(unsigned int seed)
+{
+   unsigned int  previous = stb__rand_seed;
+   stb__rand_seed = seed;
+   return previous;
+}
+
+unsigned int  stb_randLCG(void)
+{
+   stb__rand_seed = stb__rand_seed * 2147001325 + 715136305; // BCPL generator
+   // shuffle non-random bits to the middle, and xor to decorrelate with seed
+   return 0x31415926 ^ ((stb__rand_seed >> 16) + (stb__rand_seed << 16));
+}
+
+// public domain Mersenne Twister by Michael Brundage
+#define STB__MT_LEN       624
+
+int stb__mt_index = STB__MT_LEN*sizeof(int)+1;
+unsigned int stb__mt_buffer[STB__MT_LEN];
+
+void stb_srand(unsigned int seed)
+{
+   int i;
+   unsigned int old = stb_srandLCG(seed);
+   for (i = 0; i < STB__MT_LEN; i++)
+      stb__mt_buffer[i] = stb_randLCG();
+   stb_srandLCG(old);
+   stb__mt_index = STB__MT_LEN*sizeof(unsigned int);
+}
+
+#define STB__MT_IA           397
+#define STB__MT_IB           (STB__MT_LEN - STB__MT_IA)
+#define STB__UPPER_MASK      0x80000000
+#define STB__LOWER_MASK      0x7FFFFFFF
+#define STB__MATRIX_A        0x9908B0DF
+#define STB__TWIST(b,i,j)    ((b)[i] & STB__UPPER_MASK) | ((b)[j] & STB__LOWER_MASK)
+#define STB__MAGIC(s)        (((s)&1)*STB__MATRIX_A)
+
+unsigned int stb_rand()
+{
+   unsigned int  * b = stb__mt_buffer;
+   int idx = stb__mt_index;
+   unsigned int  s,r;
+   int i;
+	
+   if (idx >= STB__MT_LEN*sizeof(unsigned int)) {
+      if (idx > STB__MT_LEN*sizeof(unsigned int))
+         stb_srand(0);
+      idx = 0;
+      i = 0;
+      for (; i < STB__MT_IB; i++) {
+         s = STB__TWIST(b, i, i+1);
+         b[i] = b[i + STB__MT_IA] ^ (s >> 1) ^ STB__MAGIC(s);
+      }
+      for (; i < STB__MT_LEN-1; i++) {
+         s = STB__TWIST(b, i, i+1);
+         b[i] = b[i - STB__MT_IB] ^ (s >> 1) ^ STB__MAGIC(s);
+      }
+      
+      s = STB__TWIST(b, STB__MT_LEN-1, 0);
+      b[STB__MT_LEN-1] = b[STB__MT_IA-1] ^ (s >> 1) ^ STB__MAGIC(s);
+   }
+   stb__mt_index = idx + sizeof(unsigned int);
+   
+   r = *(unsigned int *)((unsigned char *)b + idx);
+   
+   r ^= (r >> 11);
+   r ^= (r << 7) & 0x9D2C5680;
+   r ^= (r << 15) & 0xEFC60000;
+   r ^= (r >> 18);
+   
+   return r;
+}
+
+double stb_frand(void)
+{
+   return stb_rand() / ((double) (1 << 16) * (1 << 16));
+}
+
+#endif
+
+#undef STB_EXTERN
+#endif // STB_INCLUDE_STB_H
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of 
+this software and associated documentation files (the "Software"), to deal in 
+the Software without restriction, including without limitation the rights to 
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+of the Software, and to permit persons to whom the Software is furnished to do 
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all 
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 
+software, either in source code form or as a compiled binary, for any purpose, 
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this 
+software dedicate any and all copyright interest in the software to the public 
+domain. We make this dedication for the benefit of the public at large and to 
+the detriment of our heirs and successors. We intend this dedication to be an 
+overt act of relinquishment in perpetuity of all present and future rights to 
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/
diff --git a/libs/faudio/src/stb_vorbis.h b/libs/faudio/src/stb_vorbis.h
new file mode 100644
index 00000000000..3a6e009ba5a
--- /dev/null
+++ b/libs/faudio/src/stb_vorbis.h
@@ -0,0 +1,5572 @@
+// Ogg Vorbis audio decoder - v1.20 - public domain
+// http://nothings.org/stb_vorbis/
+//
+// Original version written by Sean Barrett in 2007.
+//
+// Originally sponsored by RAD Game Tools. Seeking implementation
+// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker,
+// Elias Software, Aras Pranckevicius, and Sean Barrett.
+//
+// LICENSE
+//
+//   See end of file for license information.
+//
+// Limitations:
+//
+//   - floor 0 not supported (used in old ogg vorbis files pre-2004)
+//   - lossless sample-truncation at beginning ignored
+//   - cannot concatenate multiple vorbis streams
+//   - sample positions are 32-bit, limiting seekable 192Khz
+//       files to around 6 hours (Ogg supports 64-bit)
+//
+// Feature contributors:
+//    Dougall Johnson (sample-exact seeking)
+//
+// Bugfix/warning contributors:
+//    Terje Mathisen     Niklas Frykholm     Andy Hill
+//    Casey Muratori     John Bolton         Gargaj
+//    Laurent Gomila     Marc LeBlanc        Ronny Chevalier
+//    Bernhard Wodo      Evan Balster        github:alxprd
+//    Tom Beaumont       Ingo Leitgeb        Nicolas Guillemot
+//    Phillip Bennefall  Rohit               Thiago Goulart
+//    github:manxorist   saga musix          github:infatum
+//    Timur Gagiev       Maxwell Koo         Peter Waller
+//    github:audinowho   Dougall Johnson     David Reid
+//    github:Clownacy    Pedro J. Estebanez  Remi Verschelde
+//
+// Partial history:
+//    1.20    - 2020-07-11 - several small fixes
+//    1.19    - 2020-02-05 - warnings
+//    1.18    - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc.
+//    1.17    - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure)
+//    1.16    - 2019-03-04 - fix warnings
+//    1.15    - 2019-02-07 - explicit failure if Ogg Skeleton data is found
+//    1.14    - 2018-02-11 - delete bogus dealloca usage
+//    1.13    - 2018-01-29 - fix truncation of last frame (hopefully)
+//    1.12    - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files
+//    1.11    - 2017-07-23 - fix MinGW compilation
+//    1.10    - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory
+//    1.09    - 2016-04-04 - back out 'truncation of last frame' fix from previous version
+//    1.08    - 2016-04-02 - warnings; setup memory leaks; truncation of last frame
+//    1.07    - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const
+//    1.06    - 2015-08-31 - full, correct support for seeking API (Dougall Johnson)
+//                           some crash fixes when out of memory or with corrupt files
+//                           fix some inappropriately signed shifts
+//    1.05    - 2015-04-19 - don't define __forceinline if it's redundant
+//    1.04    - 2014-08-27 - fix missing const-correct case in API
+//    1.03    - 2014-08-07 - warning fixes
+//    1.02    - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows
+//    1.01    - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct)
+//    1.0     - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel;
+//                           (API change) report sample rate for decode-full-file funcs
+//
+// See end of file for full version history.
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+//  HEADER BEGINS HERE
+//
+
+#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H
+#define STB_VORBIS_INCLUDE_STB_VORBIS_H
+
+#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO)
+#define STB_VORBIS_NO_STDIO 1
+#endif
+
+#if 0 /* FAudio change! */
+#ifndef STB_VORBIS_NO_STDIO
+#include <stdio.h>
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///////////   THREAD SAFETY
+
+// Individual stb_vorbis* handles are not thread-safe; you cannot decode from
+// them from multiple threads at the same time. However, you can have multiple
+// stb_vorbis* handles and decode from them independently in multiple thrads.
+
+
+///////////   MEMORY ALLOCATION
+
+// normally stb_vorbis uses malloc() to allocate memory at startup,
+// and alloca() to allocate temporary memory during a frame on the
+// stack. (Memory consumption will depend on the amount of setup
+// data in the file and how you set the compile flags for speed
+// vs. size. In my test files the maximal-size usage is ~150KB.)
+//
+// You can modify the wrapper functions in the source (setup_malloc,
+// setup_temp_malloc, temp_malloc) to change this behavior, or you
+// can use a simpler allocation model: you pass in a buffer from
+// which stb_vorbis will allocate _all_ its memory (including the
+// temp memory). "open" may fail with a VORBIS_outofmem if you
+// do not pass in enough data; there is no way to determine how
+// much you do need except to succeed (at which point you can
+// query get_info to find the exact amount required. yes I know
+// this is lame).
+//
+// If you pass in a non-NULL buffer of the type below, allocation
+// will occur from it as described above. Otherwise just pass NULL
+// to use malloc()/alloca()
+
+typedef struct
+{
+   char *alloc_buffer;
+   int   alloc_buffer_length_in_bytes;
+} stb_vorbis_alloc;
+
+
+///////////   FUNCTIONS USEABLE WITH ALL INPUT MODES
+
+typedef struct stb_vorbis stb_vorbis;
+
+typedef struct
+{
+   unsigned int sample_rate;
+   int channels;
+
+   unsigned int setup_memory_required;
+   unsigned int setup_temp_memory_required;
+   unsigned int temp_memory_required;
+
+   int max_frame_size;
+} stb_vorbis_info;
+
+typedef struct
+{
+   char *vendor;
+
+   int comment_list_length;
+   char **comment_list;
+} stb_vorbis_comment;
+
+// get general information about the file
+FAUDIOAPI stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f);
+
+// get ogg comments
+FAUDIOAPI stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f);
+
+// get the last error detected (clears it, too)
+FAUDIOAPI int stb_vorbis_get_error(stb_vorbis *f);
+
+// close an ogg vorbis file and free all memory in use
+FAUDIOAPI void stb_vorbis_close(stb_vorbis *f);
+
+// this function returns the offset (in samples) from the beginning of the
+// file that will be returned by the next decode, if it is known, or -1
+// otherwise. after a flush_pushdata() call, this may take a while before
+// it becomes valid again.
+// NOT WORKING YET after a seek with PULLDATA API
+FAUDIOAPI int stb_vorbis_get_sample_offset(stb_vorbis *f);
+
+// returns the current seek point within the file, or offset from the beginning
+// of the memory buffer. In pushdata mode it returns 0.
+FAUDIOAPI unsigned int stb_vorbis_get_file_offset(stb_vorbis *f);
+
+///////////   PUSHDATA API
+
+#ifndef STB_VORBIS_NO_PUSHDATA_API
+
+// this API allows you to get blocks of data from any source and hand
+// them to stb_vorbis. you have to buffer them; stb_vorbis will tell
+// you how much it used, and you have to give it the rest next time;
+// and stb_vorbis may not have enough data to work with and you will
+// need to give it the same data again PLUS more. Note that the Vorbis
+// specification does not bound the size of an individual frame.
+
+extern stb_vorbis *stb_vorbis_open_pushdata(
+         const unsigned char * datablock, int datablock_length_in_bytes,
+         int *datablock_memory_consumed_in_bytes,
+         int *error,
+         const stb_vorbis_alloc *alloc_buffer);
+// create a vorbis decoder by passing in the initial data block containing
+//    the ogg&vorbis headers (you don't need to do parse them, just provide
+//    the first N bytes of the file--you're told if it's not enough, see below)
+// on success, returns an stb_vorbis *, does not set error, returns the amount of
+//    data parsed/consumed on this call in *datablock_memory_consumed_in_bytes;
+// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed
+// if returns NULL and *error is VORBIS_need_more_data, then the input block was
+//       incomplete and you need to pass in a larger block from the start of the file
+
+extern int stb_vorbis_decode_frame_pushdata(
+         stb_vorbis *f,
+         const unsigned char *datablock, int datablock_length_in_bytes,
+         int *channels,             // place to write number of float * buffers
+         float ***output,           // place to write float ** array of float * buffers
+         int *samples               // place to write number of output samples
+     );
+// decode a frame of audio sample data if possible from the passed-in data block
+//
+// return value: number of bytes we used from datablock
+//
+// possible cases:
+//     0 bytes used, 0 samples output (need more data)
+//     N bytes used, 0 samples output (resynching the stream, keep going)
+//     N bytes used, M samples output (one frame of data)
+// note that after opening a file, you will ALWAYS get one N-bytes,0-sample
+// frame, because Vorbis always "discards" the first frame.
+//
+// Note that on resynch, stb_vorbis will rarely consume all of the buffer,
+// instead only datablock_length_in_bytes-3 or less. This is because it wants
+// to avoid missing parts of a page header if they cross a datablock boundary,
+// without writing state-machiney code to record a partial detection.
+//
+// The number of channels returned are stored in *channels (which can be
+// NULL--it is always the same as the number of channels reported by
+// get_info). *output will contain an array of float* buffers, one per
+// channel. In other words, (*output)[0][0] contains the first sample from
+// the first channel, and (*output)[1][0] contains the first sample from
+// the second channel.
+
+extern void stb_vorbis_flush_pushdata(stb_vorbis *f);
+// inform stb_vorbis that your next datablock will not be contiguous with
+// previous ones (e.g. you've seeked in the data); future attempts to decode
+// frames will cause stb_vorbis to resynchronize (as noted above), and
+// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it
+// will begin decoding the _next_ frame.
+//
+// if you want to seek using pushdata, you need to seek in your file, then
+// call stb_vorbis_flush_pushdata(), then start calling decoding, then once
+// decoding is returning you data, call stb_vorbis_get_sample_offset, and
+// if you don't like the result, seek your file again and repeat.
+#endif
+
+
+//////////   PULLING INPUT API
+
+#ifndef STB_VORBIS_NO_PULLDATA_API
+// This API assumes stb_vorbis is allowed to pull data from a source--
+// either a block of memory containing the _entire_ vorbis stream, or a
+// FILE * that you or it create, or possibly some other reading mechanism
+// if you go modify the source to replace the FILE * case with some kind
+// of callback to your code. (But if you don't support seeking, you may
+// just want to go ahead and use pushdata.)
+
+#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION)
+extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output);
+#endif
+#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION)
+extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output);
+#endif
+// decode an entire file and output the data interleaved into a malloc()ed
+// buffer stored in *output. The return value is the number of samples
+// decoded, or -1 if the file could not be opened or was not an ogg vorbis file.
+// When you're done with it, just free() the pointer returned in *output.
+
+FAUDIOAPI stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len,
+                                  int *error, const stb_vorbis_alloc *alloc_buffer);
+// create an ogg vorbis decoder from an ogg vorbis stream in memory (note
+// this must be the entire stream!). on failure, returns NULL and sets *error
+
+#ifndef STB_VORBIS_NO_STDIO
+FAUDIOAPI stb_vorbis * stb_vorbis_open_filename(const char *filename,
+                                  int *error, const stb_vorbis_alloc *alloc_buffer);
+// create an ogg vorbis decoder from a filename via fopen(). on failure,
+// returns NULL and sets *error (possibly to VORBIS_file_open_failure).
+
+FAUDIOAPI stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close,
+                                  int *error, const stb_vorbis_alloc *alloc_buffer);
+// create an ogg vorbis decoder from an open FILE *, looking for a stream at
+// the _current_ seek point (ftell). on failure, returns NULL and sets *error.
+// note that stb_vorbis must "own" this stream; if you seek it in between
+// calls to stb_vorbis, it will become confused. Moreover, if you attempt to
+// perform stb_vorbis_seek_*() operations on this file, it will assume it
+// owns the _entire_ rest of the file after the start point. Use the next
+// function, stb_vorbis_open_file_section(), to limit it.
+
+FAUDIOAPI stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close,
+                int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len);
+// create an ogg vorbis decoder from an open FILE *, looking for a stream at
+// the _current_ seek point (ftell); the stream will be of length 'len' bytes.
+// on failure, returns NULL and sets *error. note that stb_vorbis must "own"
+// this stream; if you seek it in between calls to stb_vorbis, it will become
+// confused.
+#endif
+
+FAUDIOAPI int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number);
+FAUDIOAPI int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number);
+// these functions seek in the Vorbis file to (approximately) 'sample_number'.
+// after calling seek_frame(), the next call to get_frame_*() will include
+// the specified sample. after calling stb_vorbis_seek(), the next call to
+// stb_vorbis_get_samples_* will start with the specified sample. If you
+// do not need to seek to EXACTLY the target sample when using get_samples_*,
+// you can also use seek_frame().
+
+FAUDIOAPI int stb_vorbis_seek_start(stb_vorbis *f);
+// this function is equivalent to stb_vorbis_seek(f,0)
+
+FAUDIOAPI unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f);
+FAUDIOAPI float        stb_vorbis_stream_length_in_seconds(stb_vorbis *f);
+// these functions return the total length of the vorbis stream
+
+FAUDIOAPI int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output);
+// decode the next frame and return the number of samples. the number of
+// channels returned are stored in *channels (which can be NULL--it is always
+// the same as the number of channels reported by get_info). *output will
+// contain an array of float* buffers, one per channel. These outputs will
+// be overwritten on the next call to stb_vorbis_get_frame_*.
+//
+// You generally should not intermix calls to stb_vorbis_get_frame_*()
+// and stb_vorbis_get_samples_*(), since the latter calls the former.
+
+#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
+extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts);
+extern int stb_vorbis_get_frame_short            (stb_vorbis *f, int num_c, short **buffer, int num_samples);
+#endif
+// decode the next frame and return the number of *samples* per channel.
+// Note that for interleaved data, you pass in the number of shorts (the
+// size of your array), but the return value is the number of samples per
+// channel, not the total number of samples.
+//
+// The data is coerced to the number of channels you request according to the
+// channel coercion rules (see below). You must pass in the size of your
+// buffer(s) so that stb_vorbis will not overwrite the end of the buffer.
+// The maximum buffer size needed can be gotten from get_info(); however,
+// the Vorbis I specification implies an absolute maximum of 4096 samples
+// per channel.
+
+// Channel coercion rules:
+//    Let M be the number of channels requested, and N the number of channels present,
+//    and Cn be the nth channel; let stereo L be the sum of all L and center channels,
+//    and stereo R be the sum of all R and center channels (channel assignment from the
+//    vorbis spec).
+//        M    N       output
+//        1    k      sum(Ck) for all k
+//        2    *      stereo L, stereo R
+//        k    l      k > l, the first l channels, then 0s
+//        k    l      k <= l, the first k channels
+//    Note that this is not _good_ surround etc. mixing at all! It's just so
+//    you get something useful.
+
+FAUDIOAPI int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats);
+FAUDIOAPI int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples);
+// gets num_samples samples, not necessarily on a frame boundary--this requires
+// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES.
+// Returns the number of samples stored per channel; it may be less than requested
+// at the end of the file. If there are no more samples in the file, returns 0.
+
+#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
+extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts);
+extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples);
+#endif
+// gets num_samples samples, not necessarily on a frame boundary--this requires
+// buffering so you have to supply the buffers. Applies the coercion rules above
+// to produce 'channels' channels. Returns the number of samples stored per channel;
+// it may be less than requested at the end of the file. If there are no more
+// samples in the file, returns 0.
+
+#endif
+
+////////   ERROR CODES
+
+enum STBVorbisError
+{
+   VORBIS__no_error,
+
+   VORBIS_need_more_data=1,             // not a real error
+
+   VORBIS_invalid_api_mixing,           // can't mix API modes
+   VORBIS_outofmem,                     // not enough memory
+   VORBIS_feature_not_supported,        // uses floor 0
+   VORBIS_too_many_channels,            // STB_VORBIS_MAX_CHANNELS is too small
+   VORBIS_file_open_failure,            // fopen() failed
+   VORBIS_seek_without_length,          // can't seek in unknown-length file
+
+   VORBIS_unexpected_eof=10,            // file is truncated?
+   VORBIS_seek_invalid,                 // seek past EOF
+
+   // decoding errors (corrupt/invalid stream) -- you probably
+   // don't care about the exact details of these
+
+   // vorbis errors:
+   VORBIS_invalid_setup=20,
+   VORBIS_invalid_stream,
+
+   // ogg errors:
+   VORBIS_missing_capture_pattern=30,
+   VORBIS_invalid_stream_structure_version,
+   VORBIS_continued_packet_flag_invalid,
+   VORBIS_incorrect_stream_serial_number,
+   VORBIS_invalid_first_page,
+   VORBIS_bad_packet_type,
+   VORBIS_cant_find_last_page,
+   VORBIS_seek_failed,
+   VORBIS_ogg_skeleton_not_supported
+};
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H
+//
+//  HEADER ENDS HERE
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef STB_VORBIS_HEADER_ONLY
+
+// global configuration settings (e.g. set these in the project/makefile),
+// or just set them in this file at the top (although ideally the first few
+// should be visible when the header file is compiled too, although it's not
+// crucial)
+
+// STB_VORBIS_NO_PUSHDATA_API
+//     does not compile the code for the various stb_vorbis_*_pushdata()
+//     functions
+// #define STB_VORBIS_NO_PUSHDATA_API
+
+// STB_VORBIS_NO_PULLDATA_API
+//     does not compile the code for the non-pushdata APIs
+// #define STB_VORBIS_NO_PULLDATA_API
+
+// STB_VORBIS_NO_STDIO
+//     does not compile the code for the APIs that use FILE *s internally
+//     or externally (implied by STB_VORBIS_NO_PULLDATA_API)
+// #define STB_VORBIS_NO_STDIO
+
+// STB_VORBIS_NO_INTEGER_CONVERSION
+//     does not compile the code for converting audio sample data from
+//     float to integer (implied by STB_VORBIS_NO_PULLDATA_API)
+// #define STB_VORBIS_NO_INTEGER_CONVERSION
+
+// STB_VORBIS_NO_FAST_SCALED_FLOAT
+//      does not use a fast float-to-int trick to accelerate float-to-int on
+//      most platforms which requires endianness be defined correctly.
+//#define STB_VORBIS_NO_FAST_SCALED_FLOAT
+
+
+// STB_VORBIS_MAX_CHANNELS [number]
+//     globally define this to the maximum number of channels you need.
+//     The spec does not put a restriction on channels except that
+//     the count is stored in a byte, so 255 is the hard limit.
+//     Reducing this saves about 16 bytes per value, so using 16 saves
+//     (255-16)*16 or around 4KB. Plus anything other memory usage
+//     I forgot to account for. Can probably go as low as 8 (7.1 audio),
+//     6 (5.1 audio), or 2 (stereo only).
+#ifndef STB_VORBIS_MAX_CHANNELS
+#define STB_VORBIS_MAX_CHANNELS    16  // enough for anyone?
+#endif
+
+// STB_VORBIS_PUSHDATA_CRC_COUNT [number]
+//     after a flush_pushdata(), stb_vorbis begins scanning for the
+//     next valid page, without backtracking. when it finds something
+//     that looks like a page, it streams through it and verifies its
+//     CRC32. Should that validation fail, it keeps scanning. But it's
+//     possible that _while_ streaming through to check the CRC32 of
+//     one candidate page, it sees another candidate page. This #define
+//     determines how many "overlapping" candidate pages it can search
+//     at once. Note that "real" pages are typically ~4KB to ~8KB, whereas
+//     garbage pages could be as big as 64KB, but probably average ~16KB.
+//     So don't hose ourselves by scanning an apparent 64KB page and
+//     missing a ton of real ones in the interim; so minimum of 2
+#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT
+#define STB_VORBIS_PUSHDATA_CRC_COUNT  4
+#endif
+
+// STB_VORBIS_FAST_HUFFMAN_LENGTH [number]
+//     sets the log size of the huffman-acceleration table.  Maximum
+//     supported value is 24. with larger numbers, more decodings are O(1),
+//     but the table size is larger so worse cache missing, so you'll have
+//     to probe (and try multiple ogg vorbis files) to find the sweet spot.
+#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH
+#define STB_VORBIS_FAST_HUFFMAN_LENGTH   10
+#endif
+
+// STB_VORBIS_FAST_BINARY_LENGTH [number]
+//     sets the log size of the binary-search acceleration table. this
+//     is used in similar fashion to the fast-huffman size to set initial
+//     parameters for the binary search
+
+// STB_VORBIS_FAST_HUFFMAN_INT
+//     The fast huffman tables are much more efficient if they can be
+//     stored as 16-bit results instead of 32-bit results. This restricts
+//     the codebooks to having only 65535 possible outcomes, though.
+//     (At least, accelerated by the huffman table.)
+#ifndef STB_VORBIS_FAST_HUFFMAN_INT
+#define STB_VORBIS_FAST_HUFFMAN_SHORT
+#endif
+
+// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH
+//     If the 'fast huffman' search doesn't succeed, then stb_vorbis falls
+//     back on binary searching for the correct one. This requires storing
+//     extra tables with the huffman codes in sorted order. Defining this
+//     symbol trades off space for speed by forcing a linear search in the
+//     non-fast case, except for "sparse" codebooks.
+// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH
+
+// STB_VORBIS_DIVIDES_IN_RESIDUE
+//     stb_vorbis precomputes the result of the scalar residue decoding
+//     that would otherwise require a divide per chunk. you can trade off
+//     space for time by defining this symbol.
+// #define STB_VORBIS_DIVIDES_IN_RESIDUE
+
+// STB_VORBIS_DIVIDES_IN_CODEBOOK
+//     vorbis VQ codebooks can be encoded two ways: with every case explicitly
+//     stored, or with all elements being chosen from a small range of values,
+//     and all values possible in all elements. By default, stb_vorbis expands
+//     this latter kind out to look like the former kind for ease of decoding,
+//     because otherwise an integer divide-per-vector-element is required to
+//     unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can
+//     trade off storage for speed.
+//#define STB_VORBIS_DIVIDES_IN_CODEBOOK
+
+#ifdef STB_VORBIS_CODEBOOK_SHORTS
+#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats"
+#endif
+
+// STB_VORBIS_DIVIDE_TABLE
+//     this replaces small integer divides in the floor decode loop with
+//     table lookups. made less than 1% difference, so disabled by default.
+
+// STB_VORBIS_NO_INLINE_DECODE
+//     disables the inlining of the scalar codebook fast-huffman decode.
+//     might save a little codespace; useful for debugging
+// #define STB_VORBIS_NO_INLINE_DECODE
+
+// STB_VORBIS_NO_DEFER_FLOOR
+//     Normally we only decode the floor without synthesizing the actual
+//     full curve. We can instead synthesize the curve immediately. This
+//     requires more memory and is very likely slower, so I don't think
+//     you'd ever want to do it except for debugging.
+// #define STB_VORBIS_NO_DEFER_FLOOR
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+#ifdef STB_VORBIS_NO_PULLDATA_API
+   #define STB_VORBIS_NO_INTEGER_CONVERSION
+   #define STB_VORBIS_NO_STDIO
+#endif
+
+#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO)
+   #define STB_VORBIS_NO_STDIO 1
+#endif
+
+#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
+#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT
+
+   // only need endianness for fast-float-to-int, which we don't
+   // use for pushdata
+
+   #ifndef STB_VORBIS_BIG_ENDIAN
+     #define STB_VORBIS_ENDIAN  0
+   #else
+     #define STB_VORBIS_ENDIAN  1
+   #endif
+
+#endif
+#endif
+
+
+#if 0 /* FAudio change! */
+#ifndef STB_VORBIS_NO_STDIO
+#include <stdio.h>
+#endif
+
+#ifndef STB_VORBIS_NO_CRT
+   #include <stdlib.h>
+   #include <string.h>
+   #include <assert.h>
+   #include <math.h>
+
+   // find definition of alloca if it's not in stdlib.h:
+   #if defined(_MSC_VER) || defined(__MINGW32__)
+      #include <malloc.h>
+   #endif
+   #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__)
+      #include <alloca.h>
+   #endif
+#else // STB_VORBIS_NO_CRT
+   #define NULL 0
+   #define malloc(s)   0
+   #define free(s)     ((void) 0)
+   #define realloc(s)  0
+#endif // STB_VORBIS_NO_CRT
+#endif
+
+#include <limits.h>
+
+#ifdef __MINGW32__
+   // eff you mingw:
+   //     "fixed":
+   //         http://sourceforge.net/p/mingw-w64/mailman/message/32882927/
+   //     "no that broke the build, reverted, who cares about C":
+   //         http://sourceforge.net/p/mingw-w64/mailman/message/32890381/
+   #ifdef __forceinline
+   #undef __forceinline
+   #endif
+   #define __forceinline
+   #ifndef alloca
+   #define alloca __builtin_alloca
+   #endif
+#elif !defined(_MSC_VER)
+   #if __GNUC__
+      #define __forceinline inline
+   #else
+      #define __forceinline
+   #endif
+#endif
+
+#if STB_VORBIS_MAX_CHANNELS > 256
+#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range"
+#endif
+
+#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24
+#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range"
+#endif
+
+
+#if 0
+#include <crtdbg.h>
+#define CHECK(f)   _CrtIsValidHeapPointer(f->channel_buffers[1])
+#else
+#define CHECK(f)   ((void) 0)
+#endif
+
+#define MAX_BLOCKSIZE_LOG  13   // from specification
+#define MAX_BLOCKSIZE      (1 << MAX_BLOCKSIZE_LOG)
+
+
+typedef unsigned char  uint8;
+typedef   signed char   int8;
+typedef unsigned short uint16;
+typedef   signed short  int16;
+typedef unsigned int   uint32;
+typedef   signed int    int32;
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+typedef float codetype;
+
+// @NOTE
+//
+// Some arrays below are tagged "//varies", which means it's actually
+// a variable-sized piece of data, but rather than malloc I assume it's
+// small enough it's better to just allocate it all together with the
+// main thing
+//
+// Most of the variables are specified with the smallest size I could pack
+// them into. It might give better performance to make them all full-sized
+// integers. It should be safe to freely rearrange the structures or change
+// the sizes larger--nothing relies on silently truncating etc., nor the
+// order of variables.
+
+#define FAST_HUFFMAN_TABLE_SIZE   (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH)
+#define FAST_HUFFMAN_TABLE_MASK   (FAST_HUFFMAN_TABLE_SIZE - 1)
+
+typedef struct
+{
+   int dimensions, entries;
+   uint8 *codeword_lengths;
+   float  minimum_value;
+   float  delta_value;
+   uint8  value_bits;
+   uint8  lookup_type;
+   uint8  sequence_p;
+   uint8  sparse;
+   uint32 lookup_values;
+   codetype *multiplicands;
+   uint32 *codewords;
+   #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT
+    int16  fast_huffman[FAST_HUFFMAN_TABLE_SIZE];
+   #else
+    int32  fast_huffman[FAST_HUFFMAN_TABLE_SIZE];
+   #endif
+   uint32 *sorted_codewords;
+   int    *sorted_values;
+   int     sorted_entries;
+} Codebook;
+
+typedef struct
+{
+   uint8 order;
+   uint16 rate;
+   uint16 bark_map_size;
+   uint8 amplitude_bits;
+   uint8 amplitude_offset;
+   uint8 number_of_books;
+   uint8 book_list[16]; // varies
+} Floor0;
+
+typedef struct
+{
+   uint8 partitions;
+   uint8 partition_class_list[32]; // varies
+   uint8 class_dimensions[16]; // varies
+   uint8 class_subclasses[16]; // varies
+   uint8 class_masterbooks[16]; // varies
+   int16 subclass_books[16][8]; // varies
+   uint16 Xlist[31*8+2]; // varies
+   uint8 sorted_order[31*8+2];
+   uint8 neighbors[31*8+2][2];
+   uint8 floor1_multiplier;
+   uint8 rangebits;
+   int values;
+} Floor1;
+
+typedef union
+{
+   Floor0 floor0;
+   Floor1 floor1;
+} Floor;
+
+typedef struct
+{
+   uint32 begin, end;
+   uint32 part_size;
+   uint8 classifications;
+   uint8 classbook;
+   uint8 **classdata;
+   int16 (*residue_books)[8];
+} Residue;
+
+typedef struct
+{
+   uint8 magnitude;
+   uint8 angle;
+   uint8 mux;
+} MappingChannel;
+
+typedef struct
+{
+   uint16 coupling_steps;
+   MappingChannel *chan;
+   uint8  submaps;
+   uint8  submap_floor[15]; // varies
+   uint8  submap_residue[15]; // varies
+} Mapping;
+
+typedef struct
+{
+   uint8 blockflag;
+   uint8 mapping;
+   uint16 windowtype;
+   uint16 transformtype;
+} Mode;
+
+typedef struct
+{
+   uint32  goal_crc;    // expected crc if match
+   int     bytes_left;  // bytes left in packet
+   uint32  crc_so_far;  // running crc
+   int     bytes_done;  // bytes processed in _current_ chunk
+   uint32  sample_loc;  // granule pos encoded in page
+} CRCscan;
+
+typedef struct
+{
+   uint32 page_start, page_end;
+   uint32 last_decoded_sample;
+} ProbedPage;
+
+struct stb_vorbis
+{
+  // user-accessible info
+   unsigned int sample_rate;
+   int channels;
+
+   unsigned int setup_memory_required;
+   unsigned int temp_memory_required;
+   unsigned int setup_temp_memory_required;
+
+   char *vendor;
+   int comment_list_length;
+   char **comment_list;
+
+  // input config
+#ifndef STB_VORBIS_NO_STDIO
+   FILE *f;
+   uint32 f_start;
+   int close_on_free;
+#endif
+
+   uint8 *stream;
+   uint8 *stream_start;
+   uint8 *stream_end;
+
+   uint32 stream_len;
+
+   uint8  push_mode;
+
+   // the page to seek to when seeking to start, may be zero
+   uint32 first_audio_page_offset;
+
+   // p_first is the page on which the first audio packet ends
+   // (but not necessarily the page on which it starts)
+   ProbedPage p_first, p_last;
+
+  // memory management
+   stb_vorbis_alloc alloc;
+   int setup_offset;
+   int temp_offset;
+
+  // run-time results
+   int eof;
+   enum STBVorbisError error;
+
+  // user-useful data
+
+  // header info
+   int blocksize[2];
+   int blocksize_0, blocksize_1;
+   int codebook_count;
+   Codebook *codebooks;
+   int floor_count;
+   uint16 floor_types[64]; // varies
+   Floor *floor_config;
+   int residue_count;
+   uint16 residue_types[64]; // varies
+   Residue *residue_config;
+   int mapping_count;
+   Mapping *mapping;
+   int mode_count;
+   Mode mode_config[64];  // varies
+
+   uint32 total_samples;
+
+  // decode buffer
+   float *channel_buffers[STB_VORBIS_MAX_CHANNELS];
+   float *outputs        [STB_VORBIS_MAX_CHANNELS];
+
+   float *previous_window[STB_VORBIS_MAX_CHANNELS];
+   int previous_length;
+
+   #ifndef STB_VORBIS_NO_DEFER_FLOOR
+   int16 *finalY[STB_VORBIS_MAX_CHANNELS];
+   #else
+   float *floor_buffers[STB_VORBIS_MAX_CHANNELS];
+   #endif
+
+   uint32 current_loc; // sample location of next frame to decode
+   int    current_loc_valid;
+
+  // per-blocksize precomputed data
+
+   // twiddle factors
+   float *A[2],*B[2],*C[2];
+   float *window[2];
+   uint16 *bit_reverse[2];
+
+  // current page/packet/segment streaming info
+   uint32 serial; // stream serial number for verification
+   int last_page;
+   int segment_count;
+   uint8 segments[255];
+   uint8 page_flag;
+   uint8 bytes_in_seg;
+   uint8 first_decode;
+   int next_seg;
+   int last_seg;  // flag that we're on the last segment
+   int last_seg_which; // what was the segment number of the last seg?
+   uint32 acc;
+   int valid_bits;
+   int packet_bytes;
+   int end_seg_with_known_loc;
+   uint32 known_loc_for_packet;
+   int discard_samples_deferred;
+   uint32 samples_output;
+
+  // push mode scanning
+   int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching
+#ifndef STB_VORBIS_NO_PUSHDATA_API
+   CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT];
+#endif
+
+  // sample-access
+   int channel_buffer_start;
+   int channel_buffer_end;
+};
+
+#if defined(STB_VORBIS_NO_PUSHDATA_API)
+   #define IS_PUSH_MODE(f)   FALSE
+#elif defined(STB_VORBIS_NO_PULLDATA_API)
+   #define IS_PUSH_MODE(f)   TRUE
+#else
+   #define IS_PUSH_MODE(f)   ((f)->push_mode)
+#endif
+
+typedef struct stb_vorbis vorb;
+
+static int error(vorb *f, enum STBVorbisError e)
+{
+   f->error = e;
+   if (!f->eof && e != VORBIS_need_more_data) {
+      f->error=e; // breakpoint for debugging
+   }
+   return 0;
+}
+
+
+// these functions are used for allocating temporary memory
+// while decoding. if you can afford the stack space, use
+// alloca(); otherwise, provide a temp buffer and it will
+// allocate out of those.
+
+#define array_size_required(count,size)  (count*(sizeof(void *)+(size)))
+
+#define temp_alloc(f,size)              (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : FAudio_alloca(size))
+#define temp_free(f,p)                  FAudio_dealloca(p)
+#define temp_alloc_save(f)              ((f)->temp_offset)
+#define temp_alloc_restore(f,p)         ((f)->temp_offset = (p))
+
+#define temp_block_array(f,count,size)  make_block_array(temp_alloc(f,array_size_required(count,size)), count, size)
+
+// given a sufficiently large block of memory, make an array of pointers to subblocks of it
+static void *make_block_array(void *mem, int count, int size)
+{
+   int i;
+   void ** p = (void **) mem;
+   char *q = (char *) (p + count);
+   for (i=0; i < count; ++i) {
+      p[i] = q;
+      q += size;
+   }
+   return p;
+}
+
+// FIXME: https://github.com/nothings/stb/pull/1005 -flibit
+static char zeromalloc = 0;
+
+static void *setup_malloc(vorb *f, int sz)
+{
+   sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs.
+   f->setup_memory_required += sz;
+   if (f->alloc.alloc_buffer) {
+      void *p = (char *) f->alloc.alloc_buffer + f->setup_offset;
+      if (f->setup_offset + sz > f->temp_offset) return NULL;
+      f->setup_offset += sz;
+      return p;
+   }
+   return sz ? malloc(sz) : &zeromalloc;
+}
+
+static void setup_free(vorb *f, void *p)
+{
+   if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack
+   if (p != &zeromalloc)
+      free(p);
+}
+
+static void *setup_temp_malloc(vorb *f, int sz)
+{
+   sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs.
+   if (f->alloc.alloc_buffer) {
+      if (f->temp_offset - sz < f->setup_offset) return NULL;
+      f->temp_offset -= sz;
+      return (char *) f->alloc.alloc_buffer + f->temp_offset;
+   }
+   return malloc(sz);
+}
+
+static void setup_temp_free(vorb *f, void *p, int sz)
+{
+   if (f->alloc.alloc_buffer) {
+      f->temp_offset += (sz+7)&~7;
+      return;
+   }
+   free(p);
+}
+
+#define CRC32_POLY    0x04c11db7   // from spec
+
+static uint32 crc_table[256];
+static void crc32_init(void)
+{
+   int i,j;
+   uint32 s;
+   for(i=0; i < 256; i++) {
+      for (s=(uint32) i << 24, j=0; j < 8; ++j)
+         s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0);
+      crc_table[i] = s;
+   }
+}
+
+static __forceinline uint32 crc32_update(uint32 crc, uint8 byte)
+{
+   return (crc << 8) ^ crc_table[byte ^ (crc >> 24)];
+}
+
+
+// used in setup, and for huffman that doesn't go fast path
+static unsigned int bit_reverse(unsigned int n)
+{
+  n = ((n & 0xAAAAAAAA) >>  1) | ((n & 0x55555555) << 1);
+  n = ((n & 0xCCCCCCCC) >>  2) | ((n & 0x33333333) << 2);
+  n = ((n & 0xF0F0F0F0) >>  4) | ((n & 0x0F0F0F0F) << 4);
+  n = ((n & 0xFF00FF00) >>  8) | ((n & 0x00FF00FF) << 8);
+  return (n >> 16) | (n << 16);
+}
+
+static float square(float x)
+{
+   return x*x;
+}
+
+// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3
+// as required by the specification. fast(?) implementation from stb.h
+// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup
+static int ilog(int32 n)
+{
+   static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 };
+
+   if (n < 0) return 0; // signed n returns 0
+
+   // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29)
+   if (n < (1 << 14))
+        if (n < (1 <<  4))            return  0 + log2_4[n      ];
+        else if (n < (1 <<  9))       return  5 + log2_4[n >>  5];
+             else                     return 10 + log2_4[n >> 10];
+   else if (n < (1 << 24))
+             if (n < (1 << 19))       return 15 + log2_4[n >> 15];
+             else                     return 20 + log2_4[n >> 20];
+        else if (n < (1 << 29))       return 25 + log2_4[n >> 25];
+             else                     return 30 + log2_4[n >> 30];
+}
+
+#ifndef M_PI
+  #define M_PI  3.14159265358979323846264f  // from CRC
+#endif
+
+// code length assigned to a value with no huffman encoding
+#define NO_CODE   255
+
+/////////////////////// LEAF SETUP FUNCTIONS //////////////////////////
+//
+// these functions are only called at setup, and only a few times
+// per file
+
+static float float32_unpack(uint32 x)
+{
+   // from the specification
+   uint32 mantissa = x & 0x1fffff;
+   uint32 sign = x & 0x80000000;
+   uint32 exp = (x & 0x7fe00000) >> 21;
+   double res = sign ? -(double)mantissa : (double)mantissa;
+   return (float) ldexp((float)res, exp-788);
+}
+
+
+// zlib & jpeg huffman tables assume that the output symbols
+// can either be arbitrarily arranged, or have monotonically
+// increasing frequencies--they rely on the lengths being sorted;
+// this makes for a very simple generation algorithm.
+// vorbis allows a huffman table with non-sorted lengths. This
+// requires a more sophisticated construction, since symbols in
+// order do not map to huffman codes "in order".
+static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values)
+{
+   if (!c->sparse) {
+      c->codewords      [symbol] = huff_code;
+   } else {
+      c->codewords       [count] = huff_code;
+      c->codeword_lengths[count] = len;
+      values             [count] = symbol;
+   }
+}
+
+static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values)
+{
+   int i,k,m=0;
+   uint32 available[32];
+
+   memset(available, 0, sizeof(available));
+   // find the first entry
+   for (k=0; k < n; ++k) if (len[k] < NO_CODE) break;
+   if (k == n) { assert(c->sorted_entries == 0); return TRUE; }
+   // add to the list
+   add_entry(c, 0, k, m++, len[k], values);
+   // add all available leaves
+   for (i=1; i <= len[k]; ++i)
+      available[i] = 1U << (32-i);
+   // note that the above code treats the first case specially,
+   // but it's really the same as the following code, so they
+   // could probably be combined (except the initial code is 0,
+   // and I use 0 in available[] to mean 'empty')
+   for (i=k+1; i < n; ++i) {
+      uint32 res;
+      int z = len[i], y;
+      if (z == NO_CODE) continue;
+      // find lowest available leaf (should always be earliest,
+      // which is what the specification calls for)
+      // note that this property, and the fact we can never have
+      // more than one free leaf at a given level, isn't totally
+      // trivial to prove, but it seems true and the assert never
+      // fires, so!
+      while (z > 0 && !available[z]) --z;
+      if (z == 0) { return FALSE; }
+      res = available[z];
+      assert(z >= 0 && z < 32);
+      available[z] = 0;
+      add_entry(c, bit_reverse(res), i, m++, len[i], values);
+      // propagate availability up the tree
+      if (z != len[i]) {
+         assert(len[i] >= 0 && len[i] < 32);
+         for (y=len[i]; y > z; --y) {
+            assert(available[y] == 0);
+            available[y] = res + (1 << (32-y));
+         }
+      }
+   }
+   return TRUE;
+}
+
+// accelerated huffman table allows fast O(1) match of all symbols
+// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH
+static void compute_accelerated_huffman(Codebook *c)
+{
+   int i, len;
+   for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i)
+      c->fast_huffman[i] = -1;
+
+   len = c->sparse ? c->sorted_entries : c->entries;
+   #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT
+   if (len > 32767) len = 32767; // largest possible value we can encode!
+   #endif
+   for (i=0; i < len; ++i) {
+      if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) {
+         uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i];
+         // set table entries for all bit combinations in the higher bits
+         while (z < FAST_HUFFMAN_TABLE_SIZE) {
+             c->fast_huffman[z] = i;
+             z += 1 << c->codeword_lengths[i];
+         }
+      }
+   }
+}
+
+#ifdef _MSC_VER
+#define STBV_CDECL __cdecl
+#else
+#define STBV_CDECL
+#endif
+
+static int STBV_CDECL uint32_compare(const void *p, const void *q)
+{
+   uint32 x = * (uint32 *) p;
+   uint32 y = * (uint32 *) q;
+   return x < y ? -1 : x > y;
+}
+
+static int include_in_sort(Codebook *c, uint8 len)
+{
+   if (c->sparse) { assert(len != NO_CODE); return TRUE; }
+   if (len == NO_CODE) return FALSE;
+   if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE;
+   return FALSE;
+}
+
+// if the fast table above doesn't work, we want to binary
+// search them... need to reverse the bits
+static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values)
+{
+   int i, len;
+   // build a list of all the entries
+   // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN.
+   // this is kind of a frivolous optimization--I don't see any performance improvement,
+   // but it's like 4 extra lines of code, so.
+   if (!c->sparse) {
+      int k = 0;
+      for (i=0; i < c->entries; ++i)
+         if (include_in_sort(c, lengths[i]))
+            c->sorted_codewords[k++] = bit_reverse(c->codewords[i]);
+      assert(k == c->sorted_entries);
+   } else {
+      for (i=0; i < c->sorted_entries; ++i)
+         c->sorted_codewords[i] = bit_reverse(c->codewords[i]);
+   }
+
+   qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare);
+   c->sorted_codewords[c->sorted_entries] = 0xffffffff;
+
+   len = c->sparse ? c->sorted_entries : c->entries;
+   // now we need to indicate how they correspond; we could either
+   //   #1: sort a different data structure that says who they correspond to
+   //   #2: for each sorted entry, search the original list to find who corresponds
+   //   #3: for each original entry, find the sorted entry
+   // #1 requires extra storage, #2 is slow, #3 can use binary search!
+   for (i=0; i < len; ++i) {
+      int huff_len = c->sparse ? lengths[values[i]] : lengths[i];
+      if (include_in_sort(c,huff_len)) {
+         uint32 code = bit_reverse(c->codewords[i]);
+         int x=0, n=c->sorted_entries;
+         while (n > 1) {
+            // invariant: sc[x] <= code < sc[x+n]
+            int m = x + (n >> 1);
+            if (c->sorted_codewords[m] <= code) {
+               x = m;
+               n -= (n>>1);
+            } else {
+               n >>= 1;
+            }
+         }
+         assert(c->sorted_codewords[x] == code);
+         if (c->sparse) {
+            c->sorted_values[x] = values[i];
+            c->codeword_lengths[x] = huff_len;
+         } else {
+            c->sorted_values[x] = i;
+         }
+      }
+   }
+}
+
+// only run while parsing the header (3 times)
+static int vorbis_validate(uint8 *data)
+{
+   static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' };
+   return memcmp(data, vorbis, 6) == 0;
+}
+
+// called from setup only, once per code book
+// (formula implied by specification)
+static int lookup1_values(int entries, int dim)
+{
+   int r = (int) floor(exp((float) log((float) entries) / dim));
+   if ((int) floor(pow((float) r+1, dim)) <= entries)   // (int) cast for MinGW warning;
+      ++r;                                              // floor() to avoid _ftol() when non-CRT
+   if (pow((float) r+1, dim) <= entries)
+      return -1;
+   if ((int) floor(pow((float) r, dim)) > entries)
+      return -1;
+   return r;
+}
+
+// called twice per file
+static void compute_twiddle_factors(int n, float *A, float *B, float *C)
+{
+   int n4 = n >> 2, n8 = n >> 3;
+   int k,k2;
+
+   for (k=k2=0; k < n4; ++k,k2+=2) {
+      A[k2  ] = (float)  cos(4*k*M_PI/n);
+      A[k2+1] = (float) -sin(4*k*M_PI/n);
+      B[k2  ] = (float)  cos((k2+1)*M_PI/n/2) * 0.5f;
+      B[k2+1] = (float)  sin((k2+1)*M_PI/n/2) * 0.5f;
+   }
+   for (k=k2=0; k < n8; ++k,k2+=2) {
+      C[k2  ] = (float)  cos(2*(k2+1)*M_PI/n);
+      C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n);
+   }
+}
+
+static void compute_window(int n, float *window)
+{
+   int n2 = n >> 1, i;
+   for (i=0; i < n2; ++i)
+      window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI)));
+}
+
+static void compute_bitreverse(int n, uint16 *rev)
+{
+   int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions
+   int i, n8 = n >> 3;
+   for (i=0; i < n8; ++i)
+      rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2;
+}
+
+static int init_blocksize(vorb *f, int b, int n)
+{
+   int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3;
+   f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2);
+   f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2);
+   f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4);
+   if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem);
+   compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]);
+   f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2);
+   if (!f->window[b]) return error(f, VORBIS_outofmem);
+   compute_window(n, f->window[b]);
+   f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8);
+   if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem);
+   compute_bitreverse(n, f->bit_reverse[b]);
+   return TRUE;
+}
+
+static void neighbors(uint16 *x, int n, int *plow, int *phigh)
+{
+   int low = -1;
+   int high = 65536;
+   int i;
+   for (i=0; i < n; ++i) {
+      if (x[i] > low  && x[i] < x[n]) { *plow  = i; low = x[i]; }
+      if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; }
+   }
+}
+
+// this has been repurposed so y is now the original index instead of y
+typedef struct
+{
+   uint16 x,id;
+} stbv__floor_ordering;
+
+static int STBV_CDECL point_compare(const void *p, const void *q)
+{
+   stbv__floor_ordering *a = (stbv__floor_ordering *) p;
+   stbv__floor_ordering *b = (stbv__floor_ordering *) q;
+   return a->x < b->x ? -1 : a->x > b->x;
+}
+
+//
+/////////////////////// END LEAF SETUP FUNCTIONS //////////////////////////
+
+
+#if defined(STB_VORBIS_NO_STDIO)
+   #define USE_MEMORY(z)    TRUE
+#else
+   #define USE_MEMORY(z)    ((z)->stream)
+#endif
+
+static uint8 get8(vorb *z)
+{
+   if (USE_MEMORY(z)) {
+      if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; }
+      return *z->stream++;
+   }
+
+   #ifndef STB_VORBIS_NO_STDIO
+   {
+   /* FAudio change! */
+   int8 c;
+   if (fread(&c, 1, 1, z->f) != 1) { z->eof = TRUE; return 0; }
+   return c;
+   }
+   #endif
+}
+
+static uint32 get32(vorb *f)
+{
+   uint32 x;
+   x = get8(f);
+   x += get8(f) << 8;
+   x += get8(f) << 16;
+   x += (uint32) get8(f) << 24;
+   return x;
+}
+
+static int getn(vorb *z, uint8 *data, int n)
+{
+   if (USE_MEMORY(z)) {
+      if (z->stream+n > z->stream_end) { z->eof = 1; return 0; }
+      memcpy(data, z->stream, n);
+      z->stream += n;
+      return 1;
+   }
+
+   #ifndef STB_VORBIS_NO_STDIO
+   if (fread(data, n, 1, z->f) == 1)
+      return 1;
+   else {
+      z->eof = 1;
+      return 0;
+   }
+   #endif
+}
+
+static void skip(vorb *z, int n)
+{
+   if (USE_MEMORY(z)) {
+      z->stream += n;
+      if (z->stream >= z->stream_end) z->eof = 1;
+      return;
+   }
+   #ifndef STB_VORBIS_NO_STDIO
+   {
+      long x = ftell(z->f);
+      fseek(z->f, x+n, SEEK_SET);
+   }
+   #endif
+}
+
+static int set_file_offset(stb_vorbis *f, unsigned int loc)
+{
+   #ifndef STB_VORBIS_NO_PUSHDATA_API
+   if (f->push_mode) return 0;
+   #endif
+   f->eof = 0;
+   if (USE_MEMORY(f)) {
+      if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) {
+         f->stream = f->stream_end;
+         f->eof = 1;
+         return 0;
+      } else {
+         f->stream = f->stream_start + loc;
+         return 1;
+      }
+   }
+   #ifndef STB_VORBIS_NO_STDIO
+   if (loc + f->f_start < loc || loc >= 0x80000000) {
+      loc = 0x7fffffff;
+      f->eof = 1;
+   } else {
+      loc += f->f_start;
+   }
+   if (fseek(f->f, loc, SEEK_SET) != -1) /* FAudio change! */
+      return 1;
+   f->eof = 1;
+   fseek(f->f, f->f_start, SEEK_END);
+   return 0;
+   #endif
+}
+
+
+static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 };
+
+static int capture_pattern(vorb *f)
+{
+   if (0x4f != get8(f)) return FALSE;
+   if (0x67 != get8(f)) return FALSE;
+   if (0x67 != get8(f)) return FALSE;
+   if (0x53 != get8(f)) return FALSE;
+   return TRUE;
+}
+
+#define PAGEFLAG_continued_packet   1
+#define PAGEFLAG_first_page         2
+#define PAGEFLAG_last_page          4
+
+static int start_page_no_capturepattern(vorb *f)
+{
+   uint32 loc0,loc1,n;
+   if (f->first_decode && !IS_PUSH_MODE(f)) {
+      f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4;
+   }
+   // stream structure version
+   if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version);
+   // header flag
+   f->page_flag = get8(f);
+   // absolute granule position
+   loc0 = get32(f);
+   loc1 = get32(f);
+   // @TODO: validate loc0,loc1 as valid positions?
+   // stream serial number -- vorbis doesn't interleave, so discard
+   get32(f);
+   //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number);
+   // page sequence number
+   n = get32(f);
+   f->last_page = n;
+   // CRC32
+   get32(f);
+   // page_segments
+   f->segment_count = get8(f);
+   if (!getn(f, f->segments, f->segment_count))
+      return error(f, VORBIS_unexpected_eof);
+   // assume we _don't_ know any the sample position of any segments
+   f->end_seg_with_known_loc = -2;
+   if (loc0 != ~0U || loc1 != ~0U) {
+      int i;
+      // determine which packet is the last one that will complete
+      for (i=f->segment_count-1; i >= 0; --i)
+         if (f->segments[i] < 255)
+            break;
+      // 'i' is now the index of the _last_ segment of a packet that ends
+      if (i >= 0) {
+         f->end_seg_with_known_loc = i;
+         f->known_loc_for_packet   = loc0;
+      }
+   }
+   if (f->first_decode) {
+      int i,len;
+      len = 0;
+      for (i=0; i < f->segment_count; ++i)
+         len += f->segments[i];
+      len += 27 + f->segment_count;
+      f->p_first.page_end = f->p_first.page_start + len;
+      f->p_first.last_decoded_sample = loc0;
+   }
+   f->next_seg = 0;
+   return TRUE;
+}
+
+static int start_page(vorb *f)
+{
+   if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern);
+   return start_page_no_capturepattern(f);
+}
+
+static int start_packet(vorb *f)
+{
+   while (f->next_seg == -1) {
+      if (!start_page(f)) return FALSE;
+      if (f->page_flag & PAGEFLAG_continued_packet)
+         return error(f, VORBIS_continued_packet_flag_invalid);
+   }
+   f->last_seg = FALSE;
+   f->valid_bits = 0;
+   f->packet_bytes = 0;
+   f->bytes_in_seg = 0;
+   // f->next_seg is now valid
+   return TRUE;
+}
+
+static int maybe_start_packet(vorb *f)
+{
+   if (f->next_seg == -1) {
+      int x = get8(f);
+      if (f->eof) return FALSE; // EOF at page boundary is not an error!
+      if (0x4f != x      ) return error(f, VORBIS_missing_capture_pattern);
+      if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern);
+      if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern);
+      if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern);
+      if (!start_page_no_capturepattern(f)) return FALSE;
+      if (f->page_flag & PAGEFLAG_continued_packet) {
+         // set up enough state that we can read this packet if we want,
+         // e.g. during recovery
+         f->last_seg = FALSE;
+         f->bytes_in_seg = 0;
+         return error(f, VORBIS_continued_packet_flag_invalid);
+      }
+   }
+   return start_packet(f);
+}
+
+static int next_segment(vorb *f)
+{
+   int len;
+   if (f->last_seg) return 0;
+   if (f->next_seg == -1) {
+      f->last_seg_which = f->segment_count-1; // in case start_page fails
+      if (!start_page(f)) { f->last_seg = 1; return 0; }
+      if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid);
+   }
+   len = f->segments[f->next_seg++];
+   if (len < 255) {
+      f->last_seg = TRUE;
+      f->last_seg_which = f->next_seg-1;
+   }
+   if (f->next_seg >= f->segment_count)
+      f->next_seg = -1;
+   assert(f->bytes_in_seg == 0);
+   f->bytes_in_seg = len;
+   return len;
+}
+
+#define EOP    (-1)
+#define INVALID_BITS  (-1)
+
+static int get8_packet_raw(vorb *f)
+{
+   if (!f->bytes_in_seg) {  // CLANG!
+      if (f->last_seg) return EOP;
+      else if (!next_segment(f)) return EOP;
+   }
+   assert(f->bytes_in_seg > 0);
+   --f->bytes_in_seg;
+   ++f->packet_bytes;
+   return get8(f);
+}
+
+static int get8_packet(vorb *f)
+{
+   int x = get8_packet_raw(f);
+   f->valid_bits = 0;
+   return x;
+}
+
+static int get32_packet(vorb *f)
+{
+   uint32 x;
+   x = get8_packet(f);
+   x += get8_packet(f) << 8;
+   x += get8_packet(f) << 16;
+   x += (uint32) get8_packet(f) << 24;
+   return x;
+}
+
+static void flush_packet(vorb *f)
+{
+   while (get8_packet_raw(f) != EOP);
+}
+
+// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important
+// as the huffman decoder?
+static uint32 get_bits(vorb *f, int n)
+{
+   uint32 z;
+
+   if (f->valid_bits < 0) return 0;
+   if (f->valid_bits < n) {
+      if (n > 24) {
+         // the accumulator technique below would not work correctly in this case
+         z = get_bits(f, 24);
+         z += get_bits(f, n-24) << 24;
+         return z;
+      }
+      if (f->valid_bits == 0) f->acc = 0;
+      while (f->valid_bits < n) {
+         int z = get8_packet_raw(f);
+         if (z == EOP) {
+            f->valid_bits = INVALID_BITS;
+            return 0;
+         }
+         f->acc += z << f->valid_bits;
+         f->valid_bits += 8;
+      }
+   }
+
+   assert(f->valid_bits >= n);
+   z = f->acc & ((1 << n)-1);
+   f->acc >>= n;
+   f->valid_bits -= n;
+   return z;
+}
+
+// @OPTIMIZE: primary accumulator for huffman
+// expand the buffer to as many bits as possible without reading off end of packet
+// it might be nice to allow f->valid_bits and f->acc to be stored in registers,
+// e.g. cache them locally and decode locally
+static __forceinline void prep_huffman(vorb *f)
+{
+   if (f->valid_bits <= 24) {
+      if (f->valid_bits == 0) f->acc = 0;
+      do {
+         int z;
+         if (f->last_seg && !f->bytes_in_seg) return;
+         z = get8_packet_raw(f);
+         if (z == EOP) return;
+         f->acc += (unsigned) z << f->valid_bits;
+         f->valid_bits += 8;
+      } while (f->valid_bits <= 24);
+   }
+}
+
+enum
+{
+   VORBIS_packet_id = 1,
+   VORBIS_packet_comment = 3,
+   VORBIS_packet_setup = 5
+};
+
+static int codebook_decode_scalar_raw(vorb *f, Codebook *c)
+{
+   int i;
+   prep_huffman(f);
+
+   if (c->codewords == NULL && c->sorted_codewords == NULL)
+      return -1;
+
+   // cases to use binary search: sorted_codewords && !c->codewords
+   //                             sorted_codewords && c->entries > 8
+   if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) {
+      // binary search
+      uint32 code = bit_reverse(f->acc);
+      int x=0, n=c->sorted_entries, len;
+
+      while (n > 1) {
+         // invariant: sc[x] <= code < sc[x+n]
+         int m = x + (n >> 1);
+         if (c->sorted_codewords[m] <= code) {
+            x = m;
+            n -= (n>>1);
+         } else {
+            n >>= 1;
+         }
+      }
+      // x is now the sorted index
+      if (!c->sparse) x = c->sorted_values[x];
+      // x is now sorted index if sparse, or symbol otherwise
+      len = c->codeword_lengths[x];
+      if (f->valid_bits >= len) {
+         f->acc >>= len;
+         f->valid_bits -= len;
+         return x;
+      }
+
+      f->valid_bits = 0;
+      return -1;
+   }
+
+   // if small, linear search
+   assert(!c->sparse);
+   for (i=0; i < c->entries; ++i) {
+      if (c->codeword_lengths[i] == NO_CODE) continue;
+      if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) {
+         if (f->valid_bits >= c->codeword_lengths[i]) {
+            f->acc >>= c->codeword_lengths[i];
+            f->valid_bits -= c->codeword_lengths[i];
+            return i;
+         }
+         f->valid_bits = 0;
+         return -1;
+      }
+   }
+
+   error(f, VORBIS_invalid_stream);
+   f->valid_bits = 0;
+   return -1;
+}
+
+#ifndef STB_VORBIS_NO_INLINE_DECODE
+
+#define DECODE_RAW(var, f,c)                                  \
+   if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH)        \
+      prep_huffman(f);                                        \
+   var = f->acc & FAST_HUFFMAN_TABLE_MASK;                    \
+   var = c->fast_huffman[var];                                \
+   if (var >= 0) {                                            \
+      int n = c->codeword_lengths[var];                       \
+      f->acc >>= n;                                           \
+      f->valid_bits -= n;                                     \
+      if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \
+   } else {                                                   \
+      var = codebook_decode_scalar_raw(f,c);                  \
+   }
+
+#else
+
+static int codebook_decode_scalar(vorb *f, Codebook *c)
+{
+   int i;
+   if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH)
+      prep_huffman(f);
+   // fast huffman table lookup
+   i = f->acc & FAST_HUFFMAN_TABLE_MASK;
+   i = c->fast_huffman[i];
+   if (i >= 0) {
+      f->acc >>= c->codeword_lengths[i];
+      f->valid_bits -= c->codeword_lengths[i];
+      if (f->valid_bits < 0) { f->valid_bits = 0; return -1; }
+      return i;
+   }
+   return codebook_decode_scalar_raw(f,c);
+}
+
+#define DECODE_RAW(var,f,c)    var = codebook_decode_scalar(f,c);
+
+#endif
+
+#define DECODE(var,f,c)                                       \
+   DECODE_RAW(var,f,c)                                        \
+   if (c->sparse) var = c->sorted_values[var];
+
+#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
+  #define DECODE_VQ(var,f,c)   DECODE_RAW(var,f,c)
+#else
+  #define DECODE_VQ(var,f,c)   DECODE(var,f,c)
+#endif
+
+
+
+
+
+
+// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case
+// where we avoid one addition
+#define CODEBOOK_ELEMENT(c,off)          (c->multiplicands[off])
+#define CODEBOOK_ELEMENT_FAST(c,off)     (c->multiplicands[off])
+#define CODEBOOK_ELEMENT_BASE(c)         (0)
+
+static int codebook_decode_start(vorb *f, Codebook *c)
+{
+   int z = -1;
+
+   // type 0 is only legal in a scalar context
+   if (c->lookup_type == 0)
+      error(f, VORBIS_invalid_stream);
+   else {
+      DECODE_VQ(z,f,c);
+      if (c->sparse) assert(z < c->sorted_entries);
+      if (z < 0) {  // check for EOP
+         if (!f->bytes_in_seg)
+            if (f->last_seg)
+               return z;
+         error(f, VORBIS_invalid_stream);
+      }
+   }
+   return z;
+}
+
+static int codebook_decode(vorb *f, Codebook *c, float *output, int len)
+{
+   int i,z = codebook_decode_start(f,c);
+   if (z < 0) return FALSE;
+   if (len > c->dimensions) len = c->dimensions;
+
+#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
+   if (c->lookup_type == 1) {
+      float last = CODEBOOK_ELEMENT_BASE(c);
+      int div = 1;
+      for (i=0; i < len; ++i) {
+         int off = (z / div) % c->lookup_values;
+         float val = CODEBOOK_ELEMENT_FAST(c,off) + last;
+         output[i] += val;
+         if (c->sequence_p) last = val + c->minimum_value;
+         div *= c->lookup_values;
+      }
+      return TRUE;
+   }
+#endif
+
+   z *= c->dimensions;
+   if (c->sequence_p) {
+      float last = CODEBOOK_ELEMENT_BASE(c);
+      for (i=0; i < len; ++i) {
+         float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
+         output[i] += val;
+         last = val + c->minimum_value;
+      }
+   } else {
+      float last = CODEBOOK_ELEMENT_BASE(c);
+      for (i=0; i < len; ++i) {
+         output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last;
+      }
+   }
+
+   return TRUE;
+}
+
+static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step)
+{
+   int i,z = codebook_decode_start(f,c);
+   float last = CODEBOOK_ELEMENT_BASE(c);
+   if (z < 0) return FALSE;
+   if (len > c->dimensions) len = c->dimensions;
+
+#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
+   if (c->lookup_type == 1) {
+      int div = 1;
+      for (i=0; i < len; ++i) {
+         int off = (z / div) % c->lookup_values;
+         float val = CODEBOOK_ELEMENT_FAST(c,off) + last;
+         output[i*step] += val;
+         if (c->sequence_p) last = val;
+         div *= c->lookup_values;
+      }
+      return TRUE;
+   }
+#endif
+
+   z *= c->dimensions;
+   for (i=0; i < len; ++i) {
+      float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
+      output[i*step] += val;
+      if (c->sequence_p) last = val;
+   }
+
+   return TRUE;
+}
+
+static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode)
+{
+   int c_inter = *c_inter_p;
+   int p_inter = *p_inter_p;
+   int i,z, effective = c->dimensions;
+
+   // type 0 is only legal in a scalar context
+   if (c->lookup_type == 0)   return error(f, VORBIS_invalid_stream);
+
+   while (total_decode > 0) {
+      float last = CODEBOOK_ELEMENT_BASE(c);
+      DECODE_VQ(z,f,c);
+      #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
+      assert(!c->sparse || z < c->sorted_entries);
+      #endif
+      if (z < 0) {
+         if (!f->bytes_in_seg)
+            if (f->last_seg) return FALSE;
+         return error(f, VORBIS_invalid_stream);
+      }
+
+      // if this will take us off the end of the buffers, stop short!
+      // we check by computing the length of the virtual interleaved
+      // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter),
+      // and the length we'll be using (effective)
+      if (c_inter + p_inter*ch + effective > len * ch) {
+         effective = len*ch - (p_inter*ch - c_inter);
+      }
+
+   #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
+      if (c->lookup_type == 1) {
+         int div = 1;
+         for (i=0; i < effective; ++i) {
+            int off = (z / div) % c->lookup_values;
+            float val = CODEBOOK_ELEMENT_FAST(c,off) + last;
+            if (outputs[c_inter])
+               outputs[c_inter][p_inter] += val;
+            if (++c_inter == ch) { c_inter = 0; ++p_inter; }
+            if (c->sequence_p) last = val;
+            div *= c->lookup_values;
+         }
+      } else
+   #endif
+      {
+         z *= c->dimensions;
+         if (c->sequence_p) {
+            for (i=0; i < effective; ++i) {
+               float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
+               if (outputs[c_inter])
+                  outputs[c_inter][p_inter] += val;
+               if (++c_inter == ch) { c_inter = 0; ++p_inter; }
+               last = val;
+            }
+         } else {
+            for (i=0; i < effective; ++i) {
+               float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
+               if (outputs[c_inter])
+                  outputs[c_inter][p_inter] += val;
+               if (++c_inter == ch) { c_inter = 0; ++p_inter; }
+            }
+         }
+      }
+
+      total_decode -= effective;
+   }
+   *c_inter_p = c_inter;
+   *p_inter_p = p_inter;
+   return TRUE;
+}
+
+static int predict_point(int x, int x0, int x1, int y0, int y1)
+{
+   int dy = y1 - y0;
+   int adx = x1 - x0;
+   // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86?
+   int err = abs(dy) * (x - x0);
+   int off = err / adx;
+   return dy < 0 ? y0 - off : y0 + off;
+}
+
+// the following table is block-copied from the specification
+static float inverse_db_table[256] =
+{
+  1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f,
+  1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f,
+  1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f,
+  2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f,
+  2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f,
+  3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f,
+  4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f,
+  6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f,
+  7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f,
+  1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f,
+  1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f,
+  1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f,
+  2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f,
+  2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f,
+  3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f,
+  4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f,
+  5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f,
+  7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f,
+  9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f,
+  1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f,
+  1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f,
+  2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f,
+  2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f,
+  3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f,
+  4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f,
+  5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f,
+  7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f,
+  9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f,
+  0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f,
+  0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f,
+  0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f,
+  0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f,
+  0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f,
+  0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f,
+  0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f,
+  0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f,
+  0.00092223983f, 0.00098217216f, 0.0010459992f,  0.0011139742f,
+  0.0011863665f,  0.0012634633f,  0.0013455702f,  0.0014330129f,
+  0.0015261382f,  0.0016253153f,  0.0017309374f,  0.0018434235f,
+  0.0019632195f,  0.0020908006f,  0.0022266726f,  0.0023713743f,
+  0.0025254795f,  0.0026895994f,  0.0028643847f,  0.0030505286f,
+  0.0032487691f,  0.0034598925f,  0.0036847358f,  0.0039241906f,
+  0.0041792066f,  0.0044507950f,  0.0047400328f,  0.0050480668f,
+  0.0053761186f,  0.0057254891f,  0.0060975636f,  0.0064938176f,
+  0.0069158225f,  0.0073652516f,  0.0078438871f,  0.0083536271f,
+  0.0088964928f,  0.009474637f,   0.010090352f,   0.010746080f,
+  0.011444421f,   0.012188144f,   0.012980198f,   0.013823725f,
+  0.014722068f,   0.015678791f,   0.016697687f,   0.017782797f,
+  0.018938423f,   0.020169149f,   0.021479854f,   0.022875735f,
+  0.024362330f,   0.025945531f,   0.027631618f,   0.029427276f,
+  0.031339626f,   0.033376252f,   0.035545228f,   0.037855157f,
+  0.040315199f,   0.042935108f,   0.045725273f,   0.048696758f,
+  0.051861348f,   0.055231591f,   0.058820850f,   0.062643361f,
+  0.066714279f,   0.071049749f,   0.075666962f,   0.080584227f,
+  0.085821044f,   0.091398179f,   0.097337747f,   0.10366330f,
+  0.11039993f,    0.11757434f,    0.12521498f,    0.13335215f,
+  0.14201813f,    0.15124727f,    0.16107617f,    0.17154380f,
+  0.18269168f,    0.19456402f,    0.20720788f,    0.22067342f,
+  0.23501402f,    0.25028656f,    0.26655159f,    0.28387361f,
+  0.30232132f,    0.32196786f,    0.34289114f,    0.36517414f,
+  0.38890521f,    0.41417847f,    0.44109412f,    0.46975890f,
+  0.50028648f,    0.53279791f,    0.56742212f,    0.60429640f,
+  0.64356699f,    0.68538959f,    0.72993007f,    0.77736504f,
+  0.82788260f,    0.88168307f,    0.9389798f,     1.0f
+};
+
+
+// @OPTIMIZE: if you want to replace this bresenham line-drawing routine,
+// note that you must produce bit-identical output to decode correctly;
+// this specific sequence of operations is specified in the spec (it's
+// drawing integer-quantized frequency-space lines that the encoder
+// expects to be exactly the same)
+//     ... also, isn't the whole point of Bresenham's algorithm to NOT
+// have to divide in the setup? sigh.
+#ifndef STB_VORBIS_NO_DEFER_FLOOR
+#define LINE_OP(a,b)   a *= b
+#else
+#define LINE_OP(a,b)   a = b
+#endif
+
+#ifdef STB_VORBIS_DIVIDE_TABLE
+#define DIVTAB_NUMER   32
+#define DIVTAB_DENOM   64
+int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB
+#endif
+
+static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n)
+{
+   int dy = y1 - y0;
+   int adx = x1 - x0;
+   int ady = abs(dy);
+   int base;
+   int x=x0,y=y0;
+   int err = 0;
+   int sy;
+
+#ifdef STB_VORBIS_DIVIDE_TABLE
+   if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) {
+      if (dy < 0) {
+         base = -integer_divide_table[ady][adx];
+         sy = base-1;
+      } else {
+         base =  integer_divide_table[ady][adx];
+         sy = base+1;
+      }
+   } else {
+      base = dy / adx;
+      if (dy < 0)
+         sy = base - 1;
+      else
+         sy = base+1;
+   }
+#else
+   base = dy / adx;
+   if (dy < 0)
+      sy = base - 1;
+   else
+      sy = base+1;
+#endif
+   ady -= abs(base) * adx;
+   if (x1 > n) x1 = n;
+   if (x < x1) {
+      LINE_OP(output[x], inverse_db_table[y&255]);
+      for (++x; x < x1; ++x) {
+         err += ady;
+         if (err >= adx) {
+            err -= adx;
+            y += sy;
+         } else
+            y += base;
+         LINE_OP(output[x], inverse_db_table[y&255]);
+      }
+   }
+}
+
+static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype)
+{
+   int k;
+   if (rtype == 0) {
+      int step = n / book->dimensions;
+      for (k=0; k < step; ++k)
+         if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step))
+            return FALSE;
+   } else {
+      for (k=0; k < n; ) {
+         if (!codebook_decode(f, book, target+offset, n-k))
+            return FALSE;
+         k += book->dimensions;
+         offset += book->dimensions;
+      }
+   }
+   return TRUE;
+}
+
+// n is 1/2 of the blocksize --
+// specification: "Correct per-vector decode length is [n]/2"
+static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode)
+{
+   int i,j,pass;
+   Residue *r = f->residue_config + rn;
+   int rtype = f->residue_types[rn];
+   int c = r->classbook;
+   int classwords = f->codebooks[c].dimensions;
+   unsigned int actual_size = rtype == 2 ? n*2 : n;
+   unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size);
+   unsigned int limit_r_end   = (r->end   < actual_size ? r->end   : actual_size);
+   int n_read = limit_r_end - limit_r_begin;
+   int part_read = n_read / r->part_size;
+   int temp_alloc_point = temp_alloc_save(f);
+   #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+   uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata));
+   #else
+   int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications));
+   #endif
+
+   CHECK(f);
+
+   for (i=0; i < ch; ++i)
+      if (!do_not_decode[i])
+         memset(residue_buffers[i], 0, sizeof(float) * n);
+
+   if (rtype == 2 && ch != 1) {
+      for (j=0; j < ch; ++j)
+         if (!do_not_decode[j])
+            break;
+      if (j == ch)
+         goto done;
+
+      for (pass=0; pass < 8; ++pass) {
+         int pcount = 0, class_set = 0;
+         if (ch == 2) {
+            while (pcount < part_read) {
+               int z = r->begin + pcount*r->part_size;
+               int c_inter = (z & 1), p_inter = z>>1;
+               if (pass == 0) {
+                  Codebook *c = f->codebooks+r->classbook;
+                  int q;
+                  DECODE(q,f,c);
+                  if (q == EOP) goto done;
+                  #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+                  part_classdata[0][class_set] = r->classdata[q];
+                  #else
+                  for (i=classwords-1; i >= 0; --i) {
+                     classifications[0][i+pcount] = q % r->classifications;
+                     q /= r->classifications;
+                  }
+                  #endif
+               }
+               for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) {
+                  int z = r->begin + pcount*r->part_size;
+                  #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+                  int c = part_classdata[0][class_set][i];
+                  #else
+                  int c = classifications[0][pcount];
+                  #endif
+                  int b = r->residue_books[c][pass];
+                  if (b >= 0) {
+                     Codebook *book = f->codebooks + b;
+                     #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
+                     if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
+                        goto done;
+                     #else
+                     // saves 1%
+                     if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
+                        goto done;
+                     #endif
+                  } else {
+                     z += r->part_size;
+                     c_inter = z & 1;
+                     p_inter = z >> 1;
+                  }
+               }
+               #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+               ++class_set;
+               #endif
+            }
+         } else if (ch > 2) {
+            while (pcount < part_read) {
+               int z = r->begin + pcount*r->part_size;
+               int c_inter = z % ch, p_inter = z/ch;
+               if (pass == 0) {
+                  Codebook *c = f->codebooks+r->classbook;
+                  int q;
+                  DECODE(q,f,c);
+                  if (q == EOP) goto done;
+                  #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+                  part_classdata[0][class_set] = r->classdata[q];
+                  #else
+                  for (i=classwords-1; i >= 0; --i) {
+                     classifications[0][i+pcount] = q % r->classifications;
+                     q /= r->classifications;
+                  }
+                  #endif
+               }
+               for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) {
+                  int z = r->begin + pcount*r->part_size;
+                  #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+                  int c = part_classdata[0][class_set][i];
+                  #else
+                  int c = classifications[0][pcount];
+                  #endif
+                  int b = r->residue_books[c][pass];
+                  if (b >= 0) {
+                     Codebook *book = f->codebooks + b;
+                     if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
+                        goto done;
+                  } else {
+                     z += r->part_size;
+                     c_inter = z % ch;
+                     p_inter = z / ch;
+                  }
+               }
+               #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+               ++class_set;
+               #endif
+            }
+         }
+      }
+      goto done;
+   }
+   CHECK(f);
+
+   for (pass=0; pass < 8; ++pass) {
+      int pcount = 0, class_set=0;
+      while (pcount < part_read) {
+         if (pass == 0) {
+            for (j=0; j < ch; ++j) {
+               if (!do_not_decode[j]) {
+                  Codebook *c = f->codebooks+r->classbook;
+                  int temp;
+                  DECODE(temp,f,c);
+                  if (temp == EOP) goto done;
+                  #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+                  part_classdata[j][class_set] = r->classdata[temp];
+                  #else
+                  for (i=classwords-1; i >= 0; --i) {
+                     classifications[j][i+pcount] = temp % r->classifications;
+                     temp /= r->classifications;
+                  }
+                  #endif
+               }
+            }
+         }
+         for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) {
+            for (j=0; j < ch; ++j) {
+               if (!do_not_decode[j]) {
+                  #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+                  int c = part_classdata[j][class_set][i];
+                  #else
+                  int c = classifications[j][pcount];
+                  #endif
+                  int b = r->residue_books[c][pass];
+                  if (b >= 0) {
+                     float *target = residue_buffers[j];
+                     int offset = r->begin + pcount * r->part_size;
+                     int n = r->part_size;
+                     Codebook *book = f->codebooks + b;
+                     if (!residue_decode(f, book, target, offset, n, rtype))
+                        goto done;
+                  }
+               }
+            }
+         }
+         #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+         ++class_set;
+         #endif
+      }
+   }
+  done:
+   CHECK(f);
+   #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+   temp_free(f,part_classdata);
+   #else
+   temp_free(f,classifications);
+   #endif
+   temp_alloc_restore(f,temp_alloc_point);
+}
+
+
+#if 0
+// slow way for debugging
+void inverse_mdct_slow(float *buffer, int n)
+{
+   int i,j;
+   int n2 = n >> 1;
+   float *x = (float *) malloc(sizeof(*x) * n2);
+   memcpy(x, buffer, sizeof(*x) * n2);
+   for (i=0; i < n; ++i) {
+      float acc = 0;
+      for (j=0; j < n2; ++j)
+         // formula from paper:
+         //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1));
+         // formula from wikipedia
+         //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5));
+         // these are equivalent, except the formula from the paper inverts the multiplier!
+         // however, what actually works is NO MULTIPLIER!?!
+         //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5));
+         acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1));
+      buffer[i] = acc;
+   }
+   free(x);
+}
+#elif 0
+// same as above, but just barely able to run in real time on modern machines
+void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype)
+{
+   float mcos[16384];
+   int i,j;
+   int n2 = n >> 1, nmask = (n << 2) -1;
+   float *x = (float *) malloc(sizeof(*x) * n2);
+   memcpy(x, buffer, sizeof(*x) * n2);
+   for (i=0; i < 4*n; ++i)
+      mcos[i] = (float) cos(M_PI / 2 * i / n);
+
+   for (i=0; i < n; ++i) {
+      float acc = 0;
+      for (j=0; j < n2; ++j)
+         acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask];
+      buffer[i] = acc;
+   }
+   free(x);
+}
+#elif 0
+// transform to use a slow dct-iv; this is STILL basically trivial,
+// but only requires half as many ops
+void dct_iv_slow(float *buffer, int n)
+{
+   float mcos[16384];
+   float x[2048];
+   int i,j;
+   int n2 = n >> 1, nmask = (n << 3) - 1;
+   memcpy(x, buffer, sizeof(*x) * n);
+   for (i=0; i < 8*n; ++i)
+      mcos[i] = (float) cos(M_PI / 4 * i / n);
+   for (i=0; i < n; ++i) {
+      float acc = 0;
+      for (j=0; j < n; ++j)
+         acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask];
+      buffer[i] = acc;
+   }
+}
+
+void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype)
+{
+   int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4;
+   float temp[4096];
+
+   memcpy(temp, buffer, n2 * sizeof(float));
+   dct_iv_slow(temp, n2);  // returns -c'-d, a-b'
+
+   for (i=0; i < n4  ; ++i) buffer[i] = temp[i+n4];            // a-b'
+   for (   ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1];   // b-a', c+d'
+   for (   ; i < n   ; ++i) buffer[i] = -temp[i - n3_4];       // c'+d
+}
+#endif
+
+#ifndef LIBVORBIS_MDCT
+#define LIBVORBIS_MDCT 0
+#endif
+
+#if LIBVORBIS_MDCT
+// directly call the vorbis MDCT using an interface documented
+// by Jeff Roberts... useful for performance comparison
+typedef struct
+{
+  int n;
+  int log2n;
+
+  float *trig;
+  int   *bitrev;
+
+  float scale;
+} mdct_lookup;
+
+extern void mdct_init(mdct_lookup *lookup, int n);
+extern void mdct_clear(mdct_lookup *l);
+extern void mdct_backward(mdct_lookup *init, float *in, float *out);
+
+mdct_lookup M1,M2;
+
+void inverse_mdct(float *buffer, int n, vorb *f, int blocktype)
+{
+   mdct_lookup *M;
+   if (M1.n == n) M = &M1;
+   else if (M2.n == n) M = &M2;
+   else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; }
+   else {
+      if (M2.n) __asm int 3;
+      mdct_init(&M2, n);
+      M = &M2;
+   }
+
+   mdct_backward(M, buffer, buffer);
+}
+#endif
+
+
+// the following were split out into separate functions while optimizing;
+// they could be pushed back up but eh. __forceinline showed no change;
+// they're probably already being inlined.
+static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A)
+{
+   float *ee0 = e + i_off;
+   float *ee2 = ee0 + k_off;
+   int i;
+
+   assert((n & 3) == 0);
+   for (i=(n>>2); i > 0; --i) {
+      float k00_20, k01_21;
+      k00_20  = ee0[ 0] - ee2[ 0];
+      k01_21  = ee0[-1] - ee2[-1];
+      ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0];
+      ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1];
+      ee2[ 0] = k00_20 * A[0] - k01_21 * A[1];
+      ee2[-1] = k01_21 * A[0] + k00_20 * A[1];
+      A += 8;
+
+      k00_20  = ee0[-2] - ee2[-2];
+      k01_21  = ee0[-3] - ee2[-3];
+      ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2];
+      ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3];
+      ee2[-2] = k00_20 * A[0] - k01_21 * A[1];
+      ee2[-3] = k01_21 * A[0] + k00_20 * A[1];
+      A += 8;
+
+      k00_20  = ee0[-4] - ee2[-4];
+      k01_21  = ee0[-5] - ee2[-5];
+      ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4];
+      ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5];
+      ee2[-4] = k00_20 * A[0] - k01_21 * A[1];
+      ee2[-5] = k01_21 * A[0] + k00_20 * A[1];
+      A += 8;
+
+      k00_20  = ee0[-6] - ee2[-6];
+      k01_21  = ee0[-7] - ee2[-7];
+      ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6];
+      ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7];
+      ee2[-6] = k00_20 * A[0] - k01_21 * A[1];
+      ee2[-7] = k01_21 * A[0] + k00_20 * A[1];
+      A += 8;
+      ee0 -= 8;
+      ee2 -= 8;
+   }
+}
+
+static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1)
+{
+   int i;
+   float k00_20, k01_21;
+
+   float *e0 = e + d0;
+   float *e2 = e0 + k_off;
+
+   for (i=lim >> 2; i > 0; --i) {
+      k00_20 = e0[-0] - e2[-0];
+      k01_21 = e0[-1] - e2[-1];
+      e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0];
+      e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1];
+      e2[-0] = (k00_20)*A[0] - (k01_21) * A[1];
+      e2[-1] = (k01_21)*A[0] + (k00_20) * A[1];
+
+      A += k1;
+
+      k00_20 = e0[-2] - e2[-2];
+      k01_21 = e0[-3] - e2[-3];
+      e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2];
+      e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3];
+      e2[-2] = (k00_20)*A[0] - (k01_21) * A[1];
+      e2[-3] = (k01_21)*A[0] + (k00_20) * A[1];
+
+      A += k1;
+
+      k00_20 = e0[-4] - e2[-4];
+      k01_21 = e0[-5] - e2[-5];
+      e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4];
+      e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5];
+      e2[-4] = (k00_20)*A[0] - (k01_21) * A[1];
+      e2[-5] = (k01_21)*A[0] + (k00_20) * A[1];
+
+      A += k1;
+
+      k00_20 = e0[-6] - e2[-6];
+      k01_21 = e0[-7] - e2[-7];
+      e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6];
+      e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7];
+      e2[-6] = (k00_20)*A[0] - (k01_21) * A[1];
+      e2[-7] = (k01_21)*A[0] + (k00_20) * A[1];
+
+      e0 -= 8;
+      e2 -= 8;
+
+      A += k1;
+   }
+}
+
+static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0)
+{
+   int i;
+   float A0 = A[0];
+   float A1 = A[0+1];
+   float A2 = A[0+a_off];
+   float A3 = A[0+a_off+1];
+   float A4 = A[0+a_off*2+0];
+   float A5 = A[0+a_off*2+1];
+   float A6 = A[0+a_off*3+0];
+   float A7 = A[0+a_off*3+1];
+
+   float k00,k11;
+
+   float *ee0 = e  +i_off;
+   float *ee2 = ee0+k_off;
+
+   for (i=n; i > 0; --i) {
+      k00     = ee0[ 0] - ee2[ 0];
+      k11     = ee0[-1] - ee2[-1];
+      ee0[ 0] =  ee0[ 0] + ee2[ 0];
+      ee0[-1] =  ee0[-1] + ee2[-1];
+      ee2[ 0] = (k00) * A0 - (k11) * A1;
+      ee2[-1] = (k11) * A0 + (k00) * A1;
+
+      k00     = ee0[-2] - ee2[-2];
+      k11     = ee0[-3] - ee2[-3];
+      ee0[-2] =  ee0[-2] + ee2[-2];
+      ee0[-3] =  ee0[-3] + ee2[-3];
+      ee2[-2] = (k00) * A2 - (k11) * A3;
+      ee2[-3] = (k11) * A2 + (k00) * A3;
+
+      k00     = ee0[-4] - ee2[-4];
+      k11     = ee0[-5] - ee2[-5];
+      ee0[-4] =  ee0[-4] + ee2[-4];
+      ee0[-5] =  ee0[-5] + ee2[-5];
+      ee2[-4] = (k00) * A4 - (k11) * A5;
+      ee2[-5] = (k11) * A4 + (k00) * A5;
+
+      k00     = ee0[-6] - ee2[-6];
+      k11     = ee0[-7] - ee2[-7];
+      ee0[-6] =  ee0[-6] + ee2[-6];
+      ee0[-7] =  ee0[-7] + ee2[-7];
+      ee2[-6] = (k00) * A6 - (k11) * A7;
+      ee2[-7] = (k11) * A6 + (k00) * A7;
+
+      ee0 -= k0;
+      ee2 -= k0;
+   }
+}
+
+static __forceinline void iter_54(float *z)
+{
+   float k00,k11,k22,k33;
+   float y0,y1,y2,y3;
+
+   k00  = z[ 0] - z[-4];
+   y0   = z[ 0] + z[-4];
+   y2   = z[-2] + z[-6];
+   k22  = z[-2] - z[-6];
+
+   z[-0] = y0 + y2;      // z0 + z4 + z2 + z6
+   z[-2] = y0 - y2;      // z0 + z4 - z2 - z6
+
+   // done with y0,y2
+
+   k33  = z[-3] - z[-7];
+
+   z[-4] = k00 + k33;    // z0 - z4 + z3 - z7
+   z[-6] = k00 - k33;    // z0 - z4 - z3 + z7
+
+   // done with k33
+
+   k11  = z[-1] - z[-5];
+   y1   = z[-1] + z[-5];
+   y3   = z[-3] + z[-7];
+
+   z[-1] = y1 + y3;      // z1 + z5 + z3 + z7
+   z[-3] = y1 - y3;      // z1 + z5 - z3 - z7
+   z[-5] = k11 - k22;    // z1 - z5 + z2 - z6
+   z[-7] = k11 + k22;    // z1 - z5 - z2 + z6
+}
+
+static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n)
+{
+   int a_off = base_n >> 3;
+   float A2 = A[0+a_off];
+   float *z = e + i_off;
+   float *base = z - 16 * n;
+
+   while (z > base) {
+      float k00,k11;
+
+      k00   = z[-0] - z[-8];
+      k11   = z[-1] - z[-9];
+      z[-0] = z[-0] + z[-8];
+      z[-1] = z[-1] + z[-9];
+      z[-8] =  k00;
+      z[-9] =  k11 ;
+
+      k00    = z[ -2] - z[-10];
+      k11    = z[ -3] - z[-11];
+      z[ -2] = z[ -2] + z[-10];
+      z[ -3] = z[ -3] + z[-11];
+      z[-10] = (k00+k11) * A2;
+      z[-11] = (k11-k00) * A2;
+
+      k00    = z[-12] - z[ -4];  // reverse to avoid a unary negation
+      k11    = z[ -5] - z[-13];
+      z[ -4] = z[ -4] + z[-12];
+      z[ -5] = z[ -5] + z[-13];
+      z[-12] = k11;
+      z[-13] = k00;
+
+      k00    = z[-14] - z[ -6];  // reverse to avoid a unary negation
+      k11    = z[ -7] - z[-15];
+      z[ -6] = z[ -6] + z[-14];
+      z[ -7] = z[ -7] + z[-15];
+      z[-14] = (k00+k11) * A2;
+      z[-15] = (k00-k11) * A2;
+
+      iter_54(z);
+      iter_54(z-8);
+      z -= 16;
+   }
+}
+
+static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype)
+{
+   int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l;
+   int ld;
+   // @OPTIMIZE: reduce register pressure by using fewer variables?
+   int save_point = temp_alloc_save(f);
+   float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2));
+   float *u=NULL,*v=NULL;
+   // twiddle factors
+   float *A = f->A[blocktype];
+
+   // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio"
+   // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function.
+
+   // kernel from paper
+
+
+   // merged:
+   //   copy and reflect spectral data
+   //   step 0
+
+   // note that it turns out that the items added together during
+   // this step are, in fact, being added to themselves (as reflected
+   // by step 0). inexplicable inefficiency! this became obvious
+   // once I combined the passes.
+
+   // so there's a missing 'times 2' here (for adding X to itself).
+   // this propagates through linearly to the end, where the numbers
+   // are 1/2 too small, and need to be compensated for.
+
+   {
+      float *d,*e, *AA, *e_stop;
+      d = &buf2[n2-2];
+      AA = A;
+      e = &buffer[0];
+      e_stop = &buffer[n2];
+      while (e != e_stop) {
+         d[1] = (e[0] * AA[0] - e[2]*AA[1]);
+         d[0] = (e[0] * AA[1] + e[2]*AA[0]);
+         d -= 2;
+         AA += 2;
+         e += 4;
+      }
+
+      e = &buffer[n2-3];
+      while (d >= buf2) {
+         d[1] = (-e[2] * AA[0] - -e[0]*AA[1]);
+         d[0] = (-e[2] * AA[1] + -e[0]*AA[0]);
+         d -= 2;
+         AA += 2;
+         e -= 4;
+      }
+   }
+
+   // now we use symbolic names for these, so that we can
+   // possibly swap their meaning as we change which operations
+   // are in place
+
+   u = buffer;
+   v = buf2;
+
+   // step 2    (paper output is w, now u)
+   // this could be in place, but the data ends up in the wrong
+   // place... _somebody_'s got to swap it, so this is nominated
+   {
+      float *AA = &A[n2-8];
+      float *d0,*d1, *e0, *e1;
+
+      e0 = &v[n4];
+      e1 = &v[0];
+
+      d0 = &u[n4];
+      d1 = &u[0];
+
+      while (AA >= A) {
+         float v40_20, v41_21;
+
+         v41_21 = e0[1] - e1[1];
+         v40_20 = e0[0] - e1[0];
+         d0[1]  = e0[1] + e1[1];
+         d0[0]  = e0[0] + e1[0];
+         d1[1]  = v41_21*AA[4] - v40_20*AA[5];
+         d1[0]  = v40_20*AA[4] + v41_21*AA[5];
+
+         v41_21 = e0[3] - e1[3];
+         v40_20 = e0[2] - e1[2];
+         d0[3]  = e0[3] + e1[3];
+         d0[2]  = e0[2] + e1[2];
+         d1[3]  = v41_21*AA[0] - v40_20*AA[1];
+         d1[2]  = v40_20*AA[0] + v41_21*AA[1];
+
+         AA -= 8;
+
+         d0 += 4;
+         d1 += 4;
+         e0 += 4;
+         e1 += 4;
+      }
+   }
+
+   // step 3
+   ld = ilog(n) - 1; // ilog is off-by-one from normal definitions
+
+   // optimized step 3:
+
+   // the original step3 loop can be nested r inside s or s inside r;
+   // it's written originally as s inside r, but this is dumb when r
+   // iterates many times, and s few. So I have two copies of it and
+   // switch between them halfway.
+
+   // this is iteration 0 of step 3
+   imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A);
+   imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A);
+
+   // this is iteration 1 of step 3
+   imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16);
+   imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16);
+   imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16);
+   imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16);
+
+   l=2;
+   for (; l < (ld-3)>>1; ++l) {
+      int k0 = n >> (l+2), k0_2 = k0>>1;
+      int lim = 1 << (l+1);
+      int i;
+      for (i=0; i < lim; ++i)
+         imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3));
+   }
+
+   for (; l < ld-6; ++l) {
+      int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1;
+      int rlim = n >> (l+6), r;
+      int lim = 1 << (l+1);
+      int i_off;
+      float *A0 = A;
+      i_off = n2-1;
+      for (r=rlim; r > 0; --r) {
+         imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0);
+         A0 += k1*4;
+         i_off -= 8;
+      }
+   }
+
+   // iterations with count:
+   //   ld-6,-5,-4 all interleaved together
+   //       the big win comes from getting rid of needless flops
+   //         due to the constants on pass 5 & 4 being all 1 and 0;
+   //       combining them to be simultaneous to improve cache made little difference
+   imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n);
+
+   // output is u
+
+   // step 4, 5, and 6
+   // cannot be in-place because of step 5
+   {
+      uint16 *bitrev = f->bit_reverse[blocktype];
+      // weirdly, I'd have thought reading sequentially and writing
+      // erratically would have been better than vice-versa, but in
+      // fact that's not what my testing showed. (That is, with
+      // j = bitreverse(i), do you read i and write j, or read j and write i.)
+
+      float *d0 = &v[n4-4];
+      float *d1 = &v[n2-4];
+      while (d0 >= v) {
+         int k4;
+
+         k4 = bitrev[0];
+         d1[3] = u[k4+0];
+         d1[2] = u[k4+1];
+         d0[3] = u[k4+2];
+         d0[2] = u[k4+3];
+
+         k4 = bitrev[1];
+         d1[1] = u[k4+0];
+         d1[0] = u[k4+1];
+         d0[1] = u[k4+2];
+         d0[0] = u[k4+3];
+
+         d0 -= 4;
+         d1 -= 4;
+         bitrev += 2;
+      }
+   }
+   // (paper output is u, now v)
+
+
+   // data must be in buf2
+   assert(v == buf2);
+
+   // step 7   (paper output is v, now v)
+   // this is now in place
+   {
+      float *C = f->C[blocktype];
+      float *d, *e;
+
+      d = v;
+      e = v + n2 - 4;
+
+      while (d < e) {
+         float a02,a11,b0,b1,b2,b3;
+
+         a02 = d[0] - e[2];
+         a11 = d[1] + e[3];
+
+         b0 = C[1]*a02 + C[0]*a11;
+         b1 = C[1]*a11 - C[0]*a02;
+
+         b2 = d[0] + e[ 2];
+         b3 = d[1] - e[ 3];
+
+         d[0] = b2 + b0;
+         d[1] = b3 + b1;
+         e[2] = b2 - b0;
+         e[3] = b1 - b3;
+
+         a02 = d[2] - e[0];
+         a11 = d[3] + e[1];
+
+         b0 = C[3]*a02 + C[2]*a11;
+         b1 = C[3]*a11 - C[2]*a02;
+
+         b2 = d[2] + e[ 0];
+         b3 = d[3] - e[ 1];
+
+         d[2] = b2 + b0;
+         d[3] = b3 + b1;
+         e[0] = b2 - b0;
+         e[1] = b1 - b3;
+
+         C += 4;
+         d += 4;
+         e -= 4;
+      }
+   }
+
+   // data must be in buf2
+
+
+   // step 8+decode   (paper output is X, now buffer)
+   // this generates pairs of data a la 8 and pushes them directly through
+   // the decode kernel (pushing rather than pulling) to avoid having
+   // to make another pass later
+
+   // this cannot POSSIBLY be in place, so we refer to the buffers directly
+
+   {
+      float *d0,*d1,*d2,*d3;
+
+      float *B = f->B[blocktype] + n2 - 8;
+      float *e = buf2 + n2 - 8;
+      d0 = &buffer[0];
+      d1 = &buffer[n2-4];
+      d2 = &buffer[n2];
+      d3 = &buffer[n-4];
+      while (e >= v) {
+         float p0,p1,p2,p3;
+
+         p3 =  e[6]*B[7] - e[7]*B[6];
+         p2 = -e[6]*B[6] - e[7]*B[7];
+
+         d0[0] =   p3;
+         d1[3] = - p3;
+         d2[0] =   p2;
+         d3[3] =   p2;
+
+         p1 =  e[4]*B[5] - e[5]*B[4];
+         p0 = -e[4]*B[4] - e[5]*B[5];
+
+         d0[1] =   p1;
+         d1[2] = - p1;
+         d2[1] =   p0;
+         d3[2] =   p0;
+
+         p3 =  e[2]*B[3] - e[3]*B[2];
+         p2 = -e[2]*B[2] - e[3]*B[3];
+
+         d0[2] =   p3;
+         d1[1] = - p3;
+         d2[2] =   p2;
+         d3[1] =   p2;
+
+         p1 =  e[0]*B[1] - e[1]*B[0];
+         p0 = -e[0]*B[0] - e[1]*B[1];
+
+         d0[3] =   p1;
+         d1[0] = - p1;
+         d2[3] =   p0;
+         d3[0] =   p0;
+
+         B -= 8;
+         e -= 8;
+         d0 += 4;
+         d2 += 4;
+         d1 -= 4;
+         d3 -= 4;
+      }
+   }
+
+   temp_free(f,buf2);
+   temp_alloc_restore(f,save_point);
+}
+
+#if 0
+// this is the original version of the above code, if you want to optimize it from scratch
+void inverse_mdct_naive(float *buffer, int n)
+{
+   float s;
+   float A[1 << 12], B[1 << 12], C[1 << 11];
+   int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l;
+   int n3_4 = n - n4, ld;
+   // how can they claim this only uses N words?!
+   // oh, because they're only used sparsely, whoops
+   float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13];
+   // set up twiddle factors
+
+   for (k=k2=0; k < n4; ++k,k2+=2) {
+      A[k2  ] = (float)  cos(4*k*M_PI/n);
+      A[k2+1] = (float) -sin(4*k*M_PI/n);
+      B[k2  ] = (float)  cos((k2+1)*M_PI/n/2);
+      B[k2+1] = (float)  sin((k2+1)*M_PI/n/2);
+   }
+   for (k=k2=0; k < n8; ++k,k2+=2) {
+      C[k2  ] = (float)  cos(2*(k2+1)*M_PI/n);
+      C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n);
+   }
+
+   // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio"
+   // Note there are bugs in that pseudocode, presumably due to them attempting
+   // to rename the arrays nicely rather than representing the way their actual
+   // implementation bounces buffers back and forth. As a result, even in the
+   // "some formulars corrected" version, a direct implementation fails. These
+   // are noted below as "paper bug".
+
+   // copy and reflect spectral data
+   for (k=0; k < n2; ++k) u[k] = buffer[k];
+   for (   ; k < n ; ++k) u[k] = -buffer[n - k - 1];
+   // kernel from paper
+   // step 1
+   for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) {
+      v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2]   - (u[k4+2] - u[n-k4-3])*A[k2+1];
+      v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2];
+   }
+   // step 2
+   for (k=k4=0; k < n8; k+=1, k4+=4) {
+      w[n2+3+k4] = v[n2+3+k4] + v[k4+3];
+      w[n2+1+k4] = v[n2+1+k4] + v[k4+1];
+      w[k4+3]    = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4];
+      w[k4+1]    = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4];
+   }
+   // step 3
+   ld = ilog(n) - 1; // ilog is off-by-one from normal definitions
+   for (l=0; l < ld-3; ++l) {
+      int k0 = n >> (l+2), k1 = 1 << (l+3);
+      int rlim = n >> (l+4), r4, r;
+      int s2lim = 1 << (l+2), s2;
+      for (r=r4=0; r < rlim; r4+=4,++r) {
+         for (s2=0; s2 < s2lim; s2+=2) {
+            u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4];
+            u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4];
+            u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1]
+                                - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1];
+            u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1]
+                                + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1];
+         }
+      }
+      if (l+1 < ld-3) {
+         // paper bug: ping-ponging of u&w here is omitted
+         memcpy(w, u, sizeof(u));
+      }
+   }
+
+   // step 4
+   for (i=0; i < n8; ++i) {
+      int j = bit_reverse(i) >> (32-ld+3);
+      assert(j < n8);
+      if (i == j) {
+         // paper bug: original code probably swapped in place; if copying,
+         //            need to directly copy in this case
+         int i8 = i << 3;
+         v[i8+1] = u[i8+1];
+         v[i8+3] = u[i8+3];
+         v[i8+5] = u[i8+5];
+         v[i8+7] = u[i8+7];
+      } else if (i < j) {
+         int i8 = i << 3, j8 = j << 3;
+         v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1];
+         v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3];
+         v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5];
+         v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7];
+      }
+   }
+   // step 5
+   for (k=0; k < n2; ++k) {
+      w[k] = v[k*2+1];
+   }
+   // step 6
+   for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) {
+      u[n-1-k2] = w[k4];
+      u[n-2-k2] = w[k4+1];
+      u[n3_4 - 1 - k2] = w[k4+2];
+      u[n3_4 - 2 - k2] = w[k4+3];
+   }
+   // step 7
+   for (k=k2=0; k < n8; ++k, k2 += 2) {
+      v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2;
+      v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2;
+      v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2;
+      v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2;
+   }
+   // step 8
+   for (k=k2=0; k < n4; ++k,k2 += 2) {
+      X[k]      = v[k2+n2]*B[k2  ] + v[k2+1+n2]*B[k2+1];
+      X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2  ];
+   }
+
+   // decode kernel to output
+   // determined the following value experimentally
+   // (by first figuring out what made inverse_mdct_slow work); then matching that here
+   // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?)
+   s = 0.5; // theoretically would be n4
+
+   // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code,
+   //     so it needs to use the "old" B values to behave correctly, or else
+   //     set s to 1.0 ]]]
+   for (i=0; i < n4  ; ++i) buffer[i] = s * X[i+n4];
+   for (   ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1];
+   for (   ; i < n   ; ++i) buffer[i] = -s * X[i - n3_4];
+}
+#endif
+
+static float *get_window(vorb *f, int len)
+{
+   len <<= 1;
+   if (len == f->blocksize_0) return f->window[0];
+   if (len == f->blocksize_1) return f->window[1];
+   return NULL;
+}
+
+#ifndef STB_VORBIS_NO_DEFER_FLOOR
+typedef int16 YTYPE;
+#else
+typedef int YTYPE;
+#endif
+static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag)
+{
+   int n2 = n >> 1;
+   int s = map->chan[i].mux, floor;
+   floor = map->submap_floor[s];
+   if (f->floor_types[floor] == 0) {
+      return error(f, VORBIS_invalid_stream);
+   } else {
+      Floor1 *g = &f->floor_config[floor].floor1;
+      int j,q;
+      int lx = 0, ly = finalY[0] * g->floor1_multiplier;
+      for (q=1; q < g->values; ++q) {
+         j = g->sorted_order[q];
+         #ifndef STB_VORBIS_NO_DEFER_FLOOR
+         if (finalY[j] >= 0)
+         #else
+         if (step2_flag[j])
+         #endif
+         {
+            int hy = finalY[j] * g->floor1_multiplier;
+            int hx = g->Xlist[j];
+            if (lx != hx)
+               draw_line(target, lx,ly, hx,hy, n2);
+            CHECK(f);
+            lx = hx, ly = hy;
+         }
+      }
+      if (lx < n2) {
+         // optimization of: draw_line(target, lx,ly, n,ly, n2);
+         for (j=lx; j < n2; ++j)
+            LINE_OP(target[j], inverse_db_table[ly]);
+         CHECK(f);
+      }
+   }
+   return TRUE;
+}
+
+// The meaning of "left" and "right"
+//
+// For a given frame:
+//     we compute samples from 0..n
+//     window_center is n/2
+//     we'll window and mix the samples from left_start to left_end with data from the previous frame
+//     all of the samples from left_end to right_start can be output without mixing; however,
+//        this interval is 0-length except when transitioning between short and long frames
+//     all of the samples from right_start to right_end need to be mixed with the next frame,
+//        which we don't have, so those get saved in a buffer
+//     frame N's right_end-right_start, the number of samples to mix with the next frame,
+//        has to be the same as frame N+1's left_end-left_start (which they are by
+//        construction)
+
+static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode)
+{
+   Mode *m;
+   int i, n, prev, next, window_center;
+   f->channel_buffer_start = f->channel_buffer_end = 0;
+
+  retry:
+   if (f->eof) return FALSE;
+   if (!maybe_start_packet(f))
+      return FALSE;
+   // check packet type
+   if (get_bits(f,1) != 0) {
+      if (IS_PUSH_MODE(f))
+         return error(f,VORBIS_bad_packet_type);
+      while (EOP != get8_packet(f));
+      goto retry;
+   }
+
+   if (f->alloc.alloc_buffer)
+      assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
+
+   i = get_bits(f, ilog(f->mode_count-1));
+   if (i == EOP) return FALSE;
+   if (i >= f->mode_count) return FALSE;
+   *mode = i;
+   m = f->mode_config + i;
+   if (m->blockflag) {
+      n = f->blocksize_1;
+      prev = get_bits(f,1);
+      next = get_bits(f,1);
+   } else {
+      prev = next = 0;
+      n = f->blocksize_0;
+   }
+
+// WINDOWING
+
+   window_center = n >> 1;
+   if (m->blockflag && !prev) {
+      *p_left_start = (n - f->blocksize_0) >> 2;
+      *p_left_end   = (n + f->blocksize_0) >> 2;
+   } else {
+      *p_left_start = 0;
+      *p_left_end   = window_center;
+   }
+   if (m->blockflag && !next) {
+      *p_right_start = (n*3 - f->blocksize_0) >> 2;
+      *p_right_end   = (n*3 + f->blocksize_0) >> 2;
+   } else {
+      *p_right_start = window_center;
+      *p_right_end   = n;
+   }
+
+   return TRUE;
+}
+
+static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left)
+{
+   Mapping *map;
+   int i,j,k,n,n2;
+   int zero_channel[256];
+   int really_zero_channel[256];
+
+// WINDOWING
+
+   n = f->blocksize[m->blockflag];
+   map = &f->mapping[m->mapping];
+
+// FLOORS
+   n2 = n >> 1;
+
+   CHECK(f);
+
+   for (i=0; i < f->channels; ++i) {
+      int s = map->chan[i].mux, floor;
+      zero_channel[i] = FALSE;
+      floor = map->submap_floor[s];
+      if (f->floor_types[floor] == 0) {
+         return error(f, VORBIS_invalid_stream);
+      } else {
+         Floor1 *g = &f->floor_config[floor].floor1;
+         if (get_bits(f, 1)) {
+            short *finalY;
+            uint8 step2_flag[256];
+            static int range_list[4] = { 256, 128, 86, 64 };
+            int range = range_list[g->floor1_multiplier-1];
+            int offset = 2;
+            finalY = f->finalY[i];
+            finalY[0] = get_bits(f, ilog(range)-1);
+            finalY[1] = get_bits(f, ilog(range)-1);
+            for (j=0; j < g->partitions; ++j) {
+               int pclass = g->partition_class_list[j];
+               int cdim = g->class_dimensions[pclass];
+               int cbits = g->class_subclasses[pclass];
+               int csub = (1 << cbits)-1;
+               int cval = 0;
+               if (cbits) {
+                  Codebook *c = f->codebooks + g->class_masterbooks[pclass];
+                  DECODE(cval,f,c);
+               }
+               for (k=0; k < cdim; ++k) {
+                  int book = g->subclass_books[pclass][cval & csub];
+                  cval = cval >> cbits;
+                  if (book >= 0) {
+                     int temp;
+                     Codebook *c = f->codebooks + book;
+                     DECODE(temp,f,c);
+                     finalY[offset++] = temp;
+                  } else
+                     finalY[offset++] = 0;
+               }
+            }
+            if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec
+            step2_flag[0] = step2_flag[1] = 1;
+            for (j=2; j < g->values; ++j) {
+               int low, high, pred, highroom, lowroom, room, val;
+               low = g->neighbors[j][0];
+               high = g->neighbors[j][1];
+               //neighbors(g->Xlist, j, &low, &high);
+               pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]);
+               val = finalY[j];
+               highroom = range - pred;
+               lowroom = pred;
+               if (highroom < lowroom)
+                  room = highroom * 2;
+               else
+                  room = lowroom * 2;
+               if (val) {
+                  step2_flag[low] = step2_flag[high] = 1;
+                  step2_flag[j] = 1;
+                  if (val >= room)
+                     if (highroom > lowroom)
+                        finalY[j] = val - lowroom + pred;
+                     else
+                        finalY[j] = pred - val + highroom - 1;
+                  else
+                     if (val & 1)
+                        finalY[j] = pred - ((val+1)>>1);
+                     else
+                        finalY[j] = pred + (val>>1);
+               } else {
+                  step2_flag[j] = 0;
+                  finalY[j] = pred;
+               }
+            }
+
+#ifdef STB_VORBIS_NO_DEFER_FLOOR
+            do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag);
+#else
+            // defer final floor computation until _after_ residue
+            for (j=0; j < g->values; ++j) {
+               if (!step2_flag[j])
+                  finalY[j] = -1;
+            }
+#endif
+         } else {
+           error:
+            zero_channel[i] = TRUE;
+         }
+         // So we just defer everything else to later
+
+         // at this point we've decoded the floor into buffer
+      }
+   }
+   CHECK(f);
+   // at this point we've decoded all floors
+
+   if (f->alloc.alloc_buffer)
+      assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
+
+   // re-enable coupled channels if necessary
+   memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels);
+   for (i=0; i < map->coupling_steps; ++i)
+      if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) {
+         zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE;
+      }
+
+   CHECK(f);
+// RESIDUE DECODE
+   for (i=0; i < map->submaps; ++i) {
+      float *residue_buffers[STB_VORBIS_MAX_CHANNELS];
+      int r;
+      uint8 do_not_decode[256];
+      int ch = 0;
+      for (j=0; j < f->channels; ++j) {
+         if (map->chan[j].mux == i) {
+            if (zero_channel[j]) {
+               do_not_decode[ch] = TRUE;
+               residue_buffers[ch] = NULL;
+            } else {
+               do_not_decode[ch] = FALSE;
+               residue_buffers[ch] = f->channel_buffers[j];
+            }
+            ++ch;
+         }
+      }
+      r = map->submap_residue[i];
+      decode_residue(f, residue_buffers, ch, n2, r, do_not_decode);
+   }
+
+   if (f->alloc.alloc_buffer)
+      assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
+   CHECK(f);
+
+// INVERSE COUPLING
+   for (i = map->coupling_steps-1; i >= 0; --i) {
+      int n2 = n >> 1;
+      float *m = f->channel_buffers[map->chan[i].magnitude];
+      float *a = f->channel_buffers[map->chan[i].angle    ];
+      for (j=0; j < n2; ++j) {
+         float a2,m2;
+         if (m[j] > 0)
+            if (a[j] > 0)
+               m2 = m[j], a2 = m[j] - a[j];
+            else
+               a2 = m[j], m2 = m[j] + a[j];
+         else
+            if (a[j] > 0)
+               m2 = m[j], a2 = m[j] + a[j];
+            else
+               a2 = m[j], m2 = m[j] - a[j];
+         m[j] = m2;
+         a[j] = a2;
+      }
+   }
+   CHECK(f);
+
+   // finish decoding the floors
+#ifndef STB_VORBIS_NO_DEFER_FLOOR
+   for (i=0; i < f->channels; ++i) {
+      if (really_zero_channel[i]) {
+         memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2);
+      } else {
+         do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL);
+      }
+   }
+#else
+   for (i=0; i < f->channels; ++i) {
+      if (really_zero_channel[i]) {
+         memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2);
+      } else {
+         for (j=0; j < n2; ++j)
+            f->channel_buffers[i][j] *= f->floor_buffers[i][j];
+      }
+   }
+#endif
+
+// INVERSE MDCT
+   CHECK(f);
+   for (i=0; i < f->channels; ++i)
+      inverse_mdct(f->channel_buffers[i], n, f, m->blockflag);
+   CHECK(f);
+
+   // this shouldn't be necessary, unless we exited on an error
+   // and want to flush to get to the next packet
+   flush_packet(f);
+
+   if (f->first_decode) {
+      // assume we start so first non-discarded sample is sample 0
+      // this isn't to spec, but spec would require us to read ahead
+      // and decode the size of all current frames--could be done,
+      // but presumably it's not a commonly used feature
+      f->current_loc = -n2; // start of first frame is positioned for discard
+      // we might have to discard samples "from" the next frame too,
+      // if we're lapping a large block then a small at the start?
+      f->discard_samples_deferred = n - right_end;
+      f->current_loc_valid = TRUE;
+      f->first_decode = FALSE;
+   } else if (f->discard_samples_deferred) {
+      if (f->discard_samples_deferred >= right_start - left_start) {
+         f->discard_samples_deferred -= (right_start - left_start);
+         left_start = right_start;
+         *p_left = left_start;
+      } else {
+         left_start += f->discard_samples_deferred;
+         *p_left = left_start;
+         f->discard_samples_deferred = 0;
+      }
+   } else if (f->previous_length == 0 && f->current_loc_valid) {
+      // we're recovering from a seek... that means we're going to discard
+      // the samples from this packet even though we know our position from
+      // the last page header, so we need to update the position based on
+      // the discarded samples here
+      // but wait, the code below is going to add this in itself even
+      // on a discard, so we don't need to do it here...
+   }
+
+   // check if we have ogg information about the sample # for this packet
+   if (f->last_seg_which == f->end_seg_with_known_loc) {
+      // if we have a valid current loc, and this is final:
+      if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) {
+         uint32 current_end = f->known_loc_for_packet;
+         // then let's infer the size of the (probably) short final frame
+         if (current_end < f->current_loc + (right_end-left_start)) {
+            if (current_end < f->current_loc) {
+               // negative truncation, that's impossible!
+               *len = 0;
+            } else {
+               *len = current_end - f->current_loc;
+            }
+            *len += left_start; // this doesn't seem right, but has no ill effect on my test files
+            if (*len > right_end) *len = right_end; // this should never happen
+            f->current_loc += *len;
+            return TRUE;
+         }
+      }
+      // otherwise, just set our sample loc
+      // guess that the ogg granule pos refers to the _middle_ of the
+      // last frame?
+      // set f->current_loc to the position of left_start
+      f->current_loc = f->known_loc_for_packet - (n2-left_start);
+      f->current_loc_valid = TRUE;
+   }
+   if (f->current_loc_valid)
+      f->current_loc += (right_start - left_start);
+
+   if (f->alloc.alloc_buffer)
+      assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
+   *len = right_end;  // ignore samples after the window goes to 0
+   CHECK(f);
+
+   return TRUE;
+}
+
+static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right)
+{
+   int mode, left_end, right_end;
+   if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0;
+   return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left);
+}
+
+static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right)
+{
+   int prev,i,j;
+   // we use right&left (the start of the right- and left-window sin()-regions)
+   // to determine how much to return, rather than inferring from the rules
+   // (same result, clearer code); 'left' indicates where our sin() window
+   // starts, therefore where the previous window's right edge starts, and
+   // therefore where to start mixing from the previous buffer. 'right'
+   // indicates where our sin() ending-window starts, therefore that's where
+   // we start saving, and where our returned-data ends.
+
+   // mixin from previous window
+   if (f->previous_length) {
+      int i,j, n = f->previous_length;
+      float *w = get_window(f, n);
+      if (w == NULL) return 0;
+      for (i=0; i < f->channels; ++i) {
+         for (j=0; j < n; ++j)
+            f->channel_buffers[i][left+j] =
+               f->channel_buffers[i][left+j]*w[    j] +
+               f->previous_window[i][     j]*w[n-1-j];
+      }
+   }
+
+   prev = f->previous_length;
+
+   // last half of this data becomes previous window
+   f->previous_length = len - right;
+
+   // @OPTIMIZE: could avoid this copy by double-buffering the
+   // output (flipping previous_window with channel_buffers), but
+   // then previous_window would have to be 2x as large, and
+   // channel_buffers couldn't be temp mem (although they're NOT
+   // currently temp mem, they could be (unless we want to level
+   // performance by spreading out the computation))
+   for (i=0; i < f->channels; ++i)
+      for (j=0; right+j < len; ++j)
+         f->previous_window[i][j] = f->channel_buffers[i][right+j];
+
+   if (!prev)
+      // there was no previous packet, so this data isn't valid...
+      // this isn't entirely true, only the would-have-overlapped data
+      // isn't valid, but this seems to be what the spec requires
+      return 0;
+
+   // truncate a short frame
+   if (len < right) right = len;
+
+   f->samples_output += right-left;
+
+   return right - left;
+}
+
+static int vorbis_pump_first_frame(stb_vorbis *f)
+{
+   int len, right, left, res;
+   res = vorbis_decode_packet(f, &len, &left, &right);
+   if (res)
+      vorbis_finish_frame(f, len, left, right);
+   return res;
+}
+
+#ifndef STB_VORBIS_NO_PUSHDATA_API
+static int is_whole_packet_present(stb_vorbis *f)
+{
+   // make sure that we have the packet available before continuing...
+   // this requires a full ogg parse, but we know we can fetch from f->stream
+
+   // instead of coding this out explicitly, we could save the current read state,
+   // read the next packet with get8() until end-of-packet, check f->eof, then
+   // reset the state? but that would be slower, esp. since we'd have over 256 bytes
+   // of state to restore (primarily the page segment table)
+
+   int s = f->next_seg, first = TRUE;
+   uint8 *p = f->stream;
+
+   if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag
+      for (; s < f->segment_count; ++s) {
+         p += f->segments[s];
+         if (f->segments[s] < 255)               // stop at first short segment
+            break;
+      }
+      // either this continues, or it ends it...
+      if (s == f->segment_count)
+         s = -1; // set 'crosses page' flag
+      if (p > f->stream_end)                     return error(f, VORBIS_need_more_data);
+      first = FALSE;
+   }
+   for (; s == -1;) {
+      uint8 *q;
+      int n;
+
+      // check that we have the page header ready
+      if (p + 26 >= f->stream_end)               return error(f, VORBIS_need_more_data);
+      // validate the page
+      if (memcmp(p, ogg_page_header, 4))         return error(f, VORBIS_invalid_stream);
+      if (p[4] != 0)                             return error(f, VORBIS_invalid_stream);
+      if (first) { // the first segment must NOT have 'continued_packet', later ones MUST
+         if (f->previous_length)
+            if ((p[5] & PAGEFLAG_continued_packet))  return error(f, VORBIS_invalid_stream);
+         // if no previous length, we're resynching, so we can come in on a continued-packet,
+         // which we'll just drop
+      } else {
+         if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream);
+      }
+      n = p[26]; // segment counts
+      q = p+27;  // q points to segment table
+      p = q + n; // advance past header
+      // make sure we've read the segment table
+      if (p > f->stream_end)                     return error(f, VORBIS_need_more_data);
+      for (s=0; s < n; ++s) {
+         p += q[s];
+         if (q[s] < 255)
+            break;
+      }
+      if (s == n)
+         s = -1; // set 'crosses page' flag
+      if (p > f->stream_end)                     return error(f, VORBIS_need_more_data);
+      first = FALSE;
+   }
+   return TRUE;
+}
+#endif // !STB_VORBIS_NO_PUSHDATA_API
+
+static int start_decoder(vorb *f)
+{
+   uint8 header[6], x,y;
+   int len,i,j,k, max_submaps = 0;
+   int longest_floorlist=0;
+
+   // first page, first packet
+   f->first_decode = TRUE;
+
+   if (!start_page(f))                              return FALSE;
+   // validate page flag
+   if (!(f->page_flag & PAGEFLAG_first_page))       return error(f, VORBIS_invalid_first_page);
+   if (f->page_flag & PAGEFLAG_last_page)           return error(f, VORBIS_invalid_first_page);
+   if (f->page_flag & PAGEFLAG_continued_packet)    return error(f, VORBIS_invalid_first_page);
+   // check for expected packet length
+   if (f->segment_count != 1)                       return error(f, VORBIS_invalid_first_page);
+   if (f->segments[0] != 30) {
+      // check for the Ogg skeleton fishead identifying header to refine our error
+      if (f->segments[0] == 64 &&
+          getn(f, header, 6) &&
+          header[0] == 'f' &&
+          header[1] == 'i' &&
+          header[2] == 's' &&
+          header[3] == 'h' &&
+          header[4] == 'e' &&
+          header[5] == 'a' &&
+          get8(f)   == 'd' &&
+          get8(f)   == '\0')                        return error(f, VORBIS_ogg_skeleton_not_supported);
+      else
+                                                    return error(f, VORBIS_invalid_first_page);
+   }
+
+   // read packet
+   // check packet header
+   if (get8(f) != VORBIS_packet_id)                 return error(f, VORBIS_invalid_first_page);
+   if (!getn(f, header, 6))                         return error(f, VORBIS_unexpected_eof);
+   if (!vorbis_validate(header))                    return error(f, VORBIS_invalid_first_page);
+   // vorbis_version
+   if (get32(f) != 0)                               return error(f, VORBIS_invalid_first_page);
+   f->channels = get8(f); if (!f->channels)         return error(f, VORBIS_invalid_first_page);
+   if (f->channels > STB_VORBIS_MAX_CHANNELS)       return error(f, VORBIS_too_many_channels);
+   f->sample_rate = get32(f); if (!f->sample_rate)  return error(f, VORBIS_invalid_first_page);
+   get32(f); // bitrate_maximum
+   get32(f); // bitrate_nominal
+   get32(f); // bitrate_minimum
+   x = get8(f);
+   {
+      int log0,log1;
+      log0 = x & 15;
+      log1 = x >> 4;
+      f->blocksize_0 = 1 << log0;
+      f->blocksize_1 = 1 << log1;
+      if (log0 < 6 || log0 > 13)                       return error(f, VORBIS_invalid_setup);
+      if (log1 < 6 || log1 > 13)                       return error(f, VORBIS_invalid_setup);
+      if (log0 > log1)                                 return error(f, VORBIS_invalid_setup);
+   }
+
+   // framing_flag
+   x = get8(f);
+   if (!(x & 1))                                    return error(f, VORBIS_invalid_first_page);
+
+   // second packet!
+   if (!start_page(f))                              return FALSE;
+
+   if (!start_packet(f))                            return FALSE;
+
+   if (!next_segment(f))                            return FALSE;
+
+   if (get8_packet(f) != VORBIS_packet_comment)            return error(f, VORBIS_invalid_setup);
+   for (i=0; i < 6; ++i) header[i] = get8_packet(f);
+   if (!vorbis_validate(header))                    return error(f, VORBIS_invalid_setup);
+   //file vendor
+   len = get32_packet(f);
+   f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1));
+   if (f->vendor == NULL)                           return error(f, VORBIS_outofmem);
+   for(i=0; i < len; ++i) {
+      f->vendor[i] = get8_packet(f);
+   }
+   f->vendor[len] = (char)'\0';
+   //user comments
+   f->comment_list_length = get32_packet(f);
+   f->comment_list = (char**)setup_malloc(f, sizeof(char*) * (f->comment_list_length));
+   if (f->comment_list == NULL)                     return error(f, VORBIS_outofmem);
+
+   for(i=0; i < f->comment_list_length; ++i) {
+      len = get32_packet(f);
+      f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1));
+      if (f->comment_list[i] == NULL)               return error(f, VORBIS_outofmem);
+
+      for(j=0; j < len; ++j) {
+         f->comment_list[i][j] = get8_packet(f);
+      }
+      f->comment_list[i][len] = (char)'\0';
+   }
+
+   // framing_flag
+   x = get8_packet(f);
+   if (!(x & 1))                                    return error(f, VORBIS_invalid_setup);
+
+
+   skip(f, f->bytes_in_seg);
+   f->bytes_in_seg = 0;
+
+   do {
+      len = next_segment(f);
+      skip(f, len);
+      f->bytes_in_seg = 0;
+   } while (len);
+
+   // third packet!
+   if (!start_packet(f))                            return FALSE;
+
+   #ifndef STB_VORBIS_NO_PUSHDATA_API
+   if (IS_PUSH_MODE(f)) {
+      if (!is_whole_packet_present(f)) {
+         // convert error in ogg header to write type
+         if (f->error == VORBIS_invalid_stream)
+            f->error = VORBIS_invalid_setup;
+         return FALSE;
+      }
+   }
+   #endif
+
+   crc32_init(); // always init it, to avoid multithread race conditions
+
+   if (get8_packet(f) != VORBIS_packet_setup)       return error(f, VORBIS_invalid_setup);
+   for (i=0; i < 6; ++i) header[i] = get8_packet(f);
+   if (!vorbis_validate(header))                    return error(f, VORBIS_invalid_setup);
+
+   // codebooks
+
+   f->codebook_count = get_bits(f,8) + 1;
+   f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count);
+   if (f->codebooks == NULL)                        return error(f, VORBIS_outofmem);
+   memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count);
+   for (i=0; i < f->codebook_count; ++i) {
+      uint32 *values;
+      int ordered, sorted_count;
+      int total=0;
+      uint8 *lengths;
+      Codebook *c = f->codebooks+i;
+      CHECK(f);
+      x = get_bits(f, 8); if (x != 0x42)            return error(f, VORBIS_invalid_setup);
+      x = get_bits(f, 8); if (x != 0x43)            return error(f, VORBIS_invalid_setup);
+      x = get_bits(f, 8); if (x != 0x56)            return error(f, VORBIS_invalid_setup);
+      x = get_bits(f, 8);
+      c->dimensions = (get_bits(f, 8)<<8) + x;
+      x = get_bits(f, 8);
+      y = get_bits(f, 8);
+      c->entries = (get_bits(f, 8)<<16) + (y<<8) + x;
+      ordered = get_bits(f,1);
+      c->sparse = ordered ? 0 : get_bits(f,1);
+
+      if (c->dimensions == 0 && c->entries != 0)    return error(f, VORBIS_invalid_setup);
+
+      if (c->sparse)
+         lengths = (uint8 *) setup_temp_malloc(f, c->entries);
+      else
+         lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries);
+
+      if (!lengths) return error(f, VORBIS_outofmem);
+
+      if (ordered) {
+         int current_entry = 0;
+         int current_length = get_bits(f,5) + 1;
+         while (current_entry < c->entries) {
+            int limit = c->entries - current_entry;
+            int n = get_bits(f, ilog(limit));
+            if (current_length >= 32) return error(f, VORBIS_invalid_setup);
+            if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); }
+            memset(lengths + current_entry, current_length, n);
+            current_entry += n;
+            ++current_length;
+         }
+      } else {
+         for (j=0; j < c->entries; ++j) {
+            int present = c->sparse ? get_bits(f,1) : 1;
+            if (present) {
+               lengths[j] = get_bits(f, 5) + 1;
+               ++total;
+               if (lengths[j] == 32)
+                  return error(f, VORBIS_invalid_setup);
+            } else {
+               lengths[j] = NO_CODE;
+            }
+         }
+      }
+
+      if (c->sparse && total >= c->entries >> 2) {
+         // convert sparse items to non-sparse!
+         if (c->entries > (int) f->setup_temp_memory_required)
+            f->setup_temp_memory_required = c->entries;
+
+         c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries);
+         if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem);
+         memcpy(c->codeword_lengths, lengths, c->entries);
+         setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs!
+         lengths = c->codeword_lengths;
+         c->sparse = 0;
+      }
+
+      // compute the size of the sorted tables
+      if (c->sparse) {
+         sorted_count = total;
+      } else {
+         sorted_count = 0;
+         #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH
+         for (j=0; j < c->entries; ++j)
+            if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE)
+               ++sorted_count;
+         #endif
+      }
+
+      c->sorted_entries = sorted_count;
+      values = NULL;
+
+      CHECK(f);
+      if (!c->sparse) {
+         c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries);
+         if (!c->codewords)                  return error(f, VORBIS_outofmem);
+      } else {
+         unsigned int size;
+         if (c->sorted_entries) {
+            c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries);
+            if (!c->codeword_lengths)           return error(f, VORBIS_outofmem);
+            c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries);
+            if (!c->codewords)                  return error(f, VORBIS_outofmem);
+            values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries);
+            if (!values)                        return error(f, VORBIS_outofmem);
+         }
+         size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries;
+         if (size > f->setup_temp_memory_required)
+            f->setup_temp_memory_required = size;
+      }
+
+      if (!compute_codewords(c, lengths, c->entries, values)) {
+         if (c->sparse) setup_temp_free(f, values, 0);
+         return error(f, VORBIS_invalid_setup);
+      }
+
+      if (c->sorted_entries) {
+         // allocate an extra slot for sentinels
+         c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1));
+         if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem);
+         // allocate an extra slot at the front so that c->sorted_values[-1] is defined
+         // so that we can catch that case without an extra if
+         c->sorted_values    = ( int   *) setup_malloc(f, sizeof(*c->sorted_values   ) * (c->sorted_entries+1));
+         if (c->sorted_values == NULL) return error(f, VORBIS_outofmem);
+         ++c->sorted_values;
+         c->sorted_values[-1] = -1;
+         compute_sorted_huffman(c, lengths, values);
+      }
+
+      if (c->sparse) {
+         setup_temp_free(f, values, sizeof(*values)*c->sorted_entries);
+         setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries);
+         setup_temp_free(f, lengths, c->entries);
+         c->codewords = NULL;
+      }
+
+      compute_accelerated_huffman(c);
+
+      CHECK(f);
+      c->lookup_type = get_bits(f, 4);
+      if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup);
+      if (c->lookup_type > 0) {
+         uint16 *mults;
+         c->minimum_value = float32_unpack(get_bits(f, 32));
+         c->delta_value = float32_unpack(get_bits(f, 32));
+         c->value_bits = get_bits(f, 4)+1;
+         c->sequence_p = get_bits(f,1);
+         if (c->lookup_type == 1) {
+            int values = lookup1_values(c->entries, c->dimensions);
+            if (values < 0) return error(f, VORBIS_invalid_setup);
+            c->lookup_values = (uint32) values;
+         } else {
+            c->lookup_values = c->entries * c->dimensions;
+         }
+         if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup);
+         mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values);
+         if (mults == NULL) return error(f, VORBIS_outofmem);
+         for (j=0; j < (int) c->lookup_values; ++j) {
+            int q = get_bits(f, c->value_bits);
+            if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); }
+            mults[j] = q;
+         }
+
+#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
+         if (c->lookup_type == 1) {
+            int len, sparse = c->sparse;
+            float last=0;
+            // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop
+            if (sparse) {
+               if (c->sorted_entries == 0) goto skip;
+               c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions);
+            } else
+               c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries        * c->dimensions);
+            if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); }
+            len = sparse ? c->sorted_entries : c->entries;
+            for (j=0; j < len; ++j) {
+               unsigned int z = sparse ? c->sorted_values[j] : j;
+               unsigned int div=1;
+               for (k=0; k < c->dimensions; ++k) {
+                  int off = (z / div) % c->lookup_values;
+                  float val = mults[off];
+                  val = mults[off]*c->delta_value + c->minimum_value + last;
+                  c->multiplicands[j*c->dimensions + k] = val;
+                  if (c->sequence_p)
+                     last = val;
+                  if (k+1 < c->dimensions) {
+                     if (div > UINT_MAX / (unsigned int) c->lookup_values) {
+                        setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values);
+                        return error(f, VORBIS_invalid_setup);
+                     }
+                     div *= c->lookup_values;
+                  }
+               }
+            }
+            c->lookup_type = 2;
+         }
+         else
+#endif
+         {
+            float last=0;
+            CHECK(f);
+            c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values);
+            if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); }
+            for (j=0; j < (int) c->lookup_values; ++j) {
+               float val = mults[j] * c->delta_value + c->minimum_value + last;
+               c->multiplicands[j] = val;
+               if (c->sequence_p)
+                  last = val;
+            }
+         }
+#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
+        skip:;
+#endif
+         setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values);
+
+         CHECK(f);
+      }
+      CHECK(f);
+   }
+
+   // time domain transfers (notused)
+
+   x = get_bits(f, 6) + 1;
+   for (i=0; i < x; ++i) {
+      uint32 z = get_bits(f, 16);
+      if (z != 0) return error(f, VORBIS_invalid_setup);
+   }
+
+   // Floors
+   f->floor_count = get_bits(f, 6)+1;
+   f->floor_config = (Floor *)  setup_malloc(f, f->floor_count * sizeof(*f->floor_config));
+   if (f->floor_config == NULL) return error(f, VORBIS_outofmem);
+   for (i=0; i < f->floor_count; ++i) {
+      f->floor_types[i] = get_bits(f, 16);
+      if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup);
+      if (f->floor_types[i] == 0) {
+         Floor0 *g = &f->floor_config[i].floor0;
+         g->order = get_bits(f,8);
+         g->rate = get_bits(f,16);
+         g->bark_map_size = get_bits(f,16);
+         g->amplitude_bits = get_bits(f,6);
+         g->amplitude_offset = get_bits(f,8);
+         g->number_of_books = get_bits(f,4) + 1;
+         for (j=0; j < g->number_of_books; ++j)
+            g->book_list[j] = get_bits(f,8);
+         return error(f, VORBIS_feature_not_supported);
+      } else {
+         stbv__floor_ordering p[31*8+2];
+         Floor1 *g = &f->floor_config[i].floor1;
+         int max_class = -1;
+         g->partitions = get_bits(f, 5);
+         for (j=0; j < g->partitions; ++j) {
+            g->partition_class_list[j] = get_bits(f, 4);
+            if (g->partition_class_list[j] > max_class)
+               max_class = g->partition_class_list[j];
+         }
+         for (j=0; j <= max_class; ++j) {
+            g->class_dimensions[j] = get_bits(f, 3)+1;
+            g->class_subclasses[j] = get_bits(f, 2);
+            if (g->class_subclasses[j]) {
+               g->class_masterbooks[j] = get_bits(f, 8);
+               if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
+            }
+            for (k=0; k < 1 << g->class_subclasses[j]; ++k) {
+               g->subclass_books[j][k] = get_bits(f,8)-1;
+               if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
+            }
+         }
+         g->floor1_multiplier = get_bits(f,2)+1;
+         g->rangebits = get_bits(f,4);
+         g->Xlist[0] = 0;
+         g->Xlist[1] = 1 << g->rangebits;
+         g->values = 2;
+         for (j=0; j < g->partitions; ++j) {
+            int c = g->partition_class_list[j];
+            for (k=0; k < g->class_dimensions[c]; ++k) {
+               g->Xlist[g->values] = get_bits(f, g->rangebits);
+               ++g->values;
+            }
+         }
+         // precompute the sorting
+         for (j=0; j < g->values; ++j) {
+            p[j].x = g->Xlist[j];
+            p[j].id = j;
+         }
+         qsort(p, g->values, sizeof(p[0]), point_compare);
+         for (j=0; j < g->values-1; ++j)
+            if (p[j].x == p[j+1].x)
+               return error(f, VORBIS_invalid_setup);
+         for (j=0; j < g->values; ++j)
+            g->sorted_order[j] = (uint8) p[j].id;
+         // precompute the neighbors
+         for (j=2; j < g->values; ++j) {
+            int low = 0,hi = 0;
+            neighbors(g->Xlist, j, &low,&hi);
+            g->neighbors[j][0] = low;
+            g->neighbors[j][1] = hi;
+         }
+
+         if (g->values > longest_floorlist)
+            longest_floorlist = g->values;
+      }
+   }
+
+   // Residue
+   f->residue_count = get_bits(f, 6)+1;
+   f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0]));
+   if (f->residue_config == NULL) return error(f, VORBIS_outofmem);
+   memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0]));
+   for (i=0; i < f->residue_count; ++i) {
+      uint8 residue_cascade[64];
+      Residue *r = f->residue_config+i;
+      f->residue_types[i] = get_bits(f, 16);
+      if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup);
+      r->begin = get_bits(f, 24);
+      r->end = get_bits(f, 24);
+      if (r->end < r->begin) return error(f, VORBIS_invalid_setup);
+      r->part_size = get_bits(f,24)+1;
+      r->classifications = get_bits(f,6)+1;
+      r->classbook = get_bits(f,8);
+      if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup);
+      for (j=0; j < r->classifications; ++j) {
+         uint8 high_bits=0;
+         uint8 low_bits=get_bits(f,3);
+         if (get_bits(f,1))
+            high_bits = get_bits(f,5);
+         residue_cascade[j] = high_bits*8 + low_bits;
+      }
+      r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications);
+      if (r->residue_books == NULL) return error(f, VORBIS_outofmem);
+      for (j=0; j < r->classifications; ++j) {
+         for (k=0; k < 8; ++k) {
+            if (residue_cascade[j] & (1 << k)) {
+               r->residue_books[j][k] = get_bits(f, 8);
+               if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
+            } else {
+               r->residue_books[j][k] = -1;
+            }
+         }
+      }
+      // precompute the classifications[] array to avoid inner-loop mod/divide
+      // call it 'classdata' since we already have r->classifications
+      r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries);
+      if (!r->classdata) return error(f, VORBIS_outofmem);
+      memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries);
+      for (j=0; j < f->codebooks[r->classbook].entries; ++j) {
+         int classwords = f->codebooks[r->classbook].dimensions;
+         int temp = j;
+         r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords);
+         if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem);
+         for (k=classwords-1; k >= 0; --k) {
+            r->classdata[j][k] = temp % r->classifications;
+            temp /= r->classifications;
+         }
+      }
+   }
+
+   f->mapping_count = get_bits(f,6)+1;
+   f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping));
+   if (f->mapping == NULL) return error(f, VORBIS_outofmem);
+   memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping));
+   for (i=0; i < f->mapping_count; ++i) {
+      Mapping *m = f->mapping + i;
+      int mapping_type = get_bits(f,16);
+      if (mapping_type != 0) return error(f, VORBIS_invalid_setup);
+      m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan));
+      if (m->chan == NULL) return error(f, VORBIS_outofmem);
+      if (get_bits(f,1))
+         m->submaps = get_bits(f,4)+1;
+      else
+         m->submaps = 1;
+      if (m->submaps > max_submaps)
+         max_submaps = m->submaps;
+      if (get_bits(f,1)) {
+         m->coupling_steps = get_bits(f,8)+1;
+         if (m->coupling_steps > f->channels) return error(f, VORBIS_invalid_setup);
+         for (k=0; k < m->coupling_steps; ++k) {
+            m->chan[k].magnitude = get_bits(f, ilog(f->channels-1));
+            m->chan[k].angle = get_bits(f, ilog(f->channels-1));
+            if (m->chan[k].magnitude >= f->channels)        return error(f, VORBIS_invalid_setup);
+            if (m->chan[k].angle     >= f->channels)        return error(f, VORBIS_invalid_setup);
+            if (m->chan[k].magnitude == m->chan[k].angle)   return error(f, VORBIS_invalid_setup);
+         }
+      } else
+         m->coupling_steps = 0;
+
+      // reserved field
+      if (get_bits(f,2)) return error(f, VORBIS_invalid_setup);
+      if (m->submaps > 1) {
+         for (j=0; j < f->channels; ++j) {
+            m->chan[j].mux = get_bits(f, 4);
+            if (m->chan[j].mux >= m->submaps)                return error(f, VORBIS_invalid_setup);
+         }
+      } else
+         // @SPECIFICATION: this case is missing from the spec
+         for (j=0; j < f->channels; ++j)
+            m->chan[j].mux = 0;
+
+      for (j=0; j < m->submaps; ++j) {
+         get_bits(f,8); // discard
+         m->submap_floor[j] = get_bits(f,8);
+         m->submap_residue[j] = get_bits(f,8);
+         if (m->submap_floor[j] >= f->floor_count)      return error(f, VORBIS_invalid_setup);
+         if (m->submap_residue[j] >= f->residue_count)  return error(f, VORBIS_invalid_setup);
+      }
+   }
+
+   // Modes
+   f->mode_count = get_bits(f, 6)+1;
+   for (i=0; i < f->mode_count; ++i) {
+      Mode *m = f->mode_config+i;
+      m->blockflag = get_bits(f,1);
+      m->windowtype = get_bits(f,16);
+      m->transformtype = get_bits(f,16);
+      m->mapping = get_bits(f,8);
+      if (m->windowtype != 0)                 return error(f, VORBIS_invalid_setup);
+      if (m->transformtype != 0)              return error(f, VORBIS_invalid_setup);
+      if (m->mapping >= f->mapping_count)     return error(f, VORBIS_invalid_setup);
+   }
+
+   flush_packet(f);
+
+   f->previous_length = 0;
+
+   for (i=0; i < f->channels; ++i) {
+      f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1);
+      f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2);
+      f->finalY[i]          = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist);
+      if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem);
+      memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1);
+      #ifdef STB_VORBIS_NO_DEFER_FLOOR
+      f->floor_buffers[i]   = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2);
+      if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem);
+      #endif
+   }
+
+   if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE;
+   if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE;
+   f->blocksize[0] = f->blocksize_0;
+   f->blocksize[1] = f->blocksize_1;
+
+#ifdef STB_VORBIS_DIVIDE_TABLE
+   if (integer_divide_table[1][1]==0)
+      for (i=0; i < DIVTAB_NUMER; ++i)
+         for (j=1; j < DIVTAB_DENOM; ++j)
+            integer_divide_table[i][j] = i / j;
+#endif
+
+   // compute how much temporary memory is needed
+
+   // 1.
+   {
+      uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1);
+      uint32 classify_mem;
+      int i,max_part_read=0;
+      for (i=0; i < f->residue_count; ++i) {
+         Residue *r = f->residue_config + i;
+         unsigned int actual_size = f->blocksize_1 / 2;
+         unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size;
+         unsigned int limit_r_end   = r->end   < actual_size ? r->end   : actual_size;
+         int n_read = limit_r_end - limit_r_begin;
+         int part_read = n_read / r->part_size;
+         if (part_read > max_part_read)
+            max_part_read = part_read;
+      }
+      #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+      classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *));
+      #else
+      classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *));
+      #endif
+
+      // maximum reasonable partition size is f->blocksize_1
+
+      f->temp_memory_required = classify_mem;
+      if (imdct_mem > f->temp_memory_required)
+         f->temp_memory_required = imdct_mem;
+   }
+
+
+   if (f->alloc.alloc_buffer) {
+      assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes);
+      // check if there's enough temp memory so we don't error later
+      if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset)
+         return error(f, VORBIS_outofmem);
+   }
+
+   // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point to a page
+   // without PAGEFLAG_continued_packet, so this either points to the first page, or
+   // the page after the end of the headers. It might be cleaner to point to a page
+   // in the middle of the headers, when that's the page where the first audio packet
+   // starts, but we'd have to also correctly skip the end of any continued packet in
+   // stb_vorbis_seek_start.
+   if (f->next_seg == -1) {
+      f->first_audio_page_offset = stb_vorbis_get_file_offset(f);
+   } else {
+      f->first_audio_page_offset = 0;
+   }
+
+   return TRUE;
+}
+
+static void vorbis_deinit(stb_vorbis *p)
+{
+   int i,j;
+
+   setup_free(p, p->vendor);
+   for (i=0; i < p->comment_list_length; ++i) {
+      setup_free(p, p->comment_list[i]);
+   }
+   setup_free(p, p->comment_list);
+
+   if (p->residue_config) {
+      for (i=0; i < p->residue_count; ++i) {
+         Residue *r = p->residue_config+i;
+         if (r->classdata) {
+            for (j=0; j < p->codebooks[r->classbook].entries; ++j)
+               setup_free(p, r->classdata[j]);
+            setup_free(p, r->classdata);
+         }
+         setup_free(p, r->residue_books);
+      }
+   }
+
+   if (p->codebooks) {
+      CHECK(p);
+      for (i=0; i < p->codebook_count; ++i) {
+         Codebook *c = p->codebooks + i;
+         setup_free(p, c->codeword_lengths);
+         setup_free(p, c->multiplicands);
+         setup_free(p, c->codewords);
+         setup_free(p, c->sorted_codewords);
+         // c->sorted_values[-1] is the first entry in the array
+         setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL);
+      }
+      setup_free(p, p->codebooks);
+   }
+   setup_free(p, p->floor_config);
+   setup_free(p, p->residue_config);
+   if (p->mapping) {
+      for (i=0; i < p->mapping_count; ++i)
+         setup_free(p, p->mapping[i].chan);
+      setup_free(p, p->mapping);
+   }
+   CHECK(p);
+   for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) {
+      setup_free(p, p->channel_buffers[i]);
+      setup_free(p, p->previous_window[i]);
+      #ifdef STB_VORBIS_NO_DEFER_FLOOR
+      setup_free(p, p->floor_buffers[i]);
+      #endif
+      setup_free(p, p->finalY[i]);
+   }
+   for (i=0; i < 2; ++i) {
+      setup_free(p, p->A[i]);
+      setup_free(p, p->B[i]);
+      setup_free(p, p->C[i]);
+      setup_free(p, p->window[i]);
+      setup_free(p, p->bit_reverse[i]);
+   }
+   #ifndef STB_VORBIS_NO_STDIO
+   if (p->close_on_free) fclose(p->f);
+   #endif
+}
+
+void stb_vorbis_close(stb_vorbis *p)
+{
+   if (p == NULL) return;
+   vorbis_deinit(p);
+   setup_free(p,p);
+}
+
+static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z)
+{
+   memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start
+   if (z) {
+      p->alloc = *z;
+      p->alloc.alloc_buffer_length_in_bytes &= ~7;
+      p->temp_offset = p->alloc.alloc_buffer_length_in_bytes;
+   }
+   p->eof = 0;
+   p->error = VORBIS__no_error;
+   p->stream = NULL;
+   p->codebooks = NULL;
+   p->page_crc_tests = -1;
+   #ifndef STB_VORBIS_NO_STDIO
+   p->close_on_free = FALSE;
+   p->f = NULL;
+   #endif
+}
+
+int stb_vorbis_get_sample_offset(stb_vorbis *f)
+{
+   if (f->current_loc_valid)
+      return f->current_loc;
+   else
+      return -1;
+}
+
+stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f)
+{
+   stb_vorbis_info d;
+   d.channels = f->channels;
+   d.sample_rate = f->sample_rate;
+   d.setup_memory_required = f->setup_memory_required;
+   d.setup_temp_memory_required = f->setup_temp_memory_required;
+   d.temp_memory_required = f->temp_memory_required;
+   d.max_frame_size = f->blocksize_1 >> 1;
+   return d;
+}
+
+stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f)
+{
+   stb_vorbis_comment d;
+   d.vendor = f->vendor;
+   d.comment_list_length = f->comment_list_length;
+   d.comment_list = f->comment_list;
+   return d;
+}
+
+int stb_vorbis_get_error(stb_vorbis *f)
+{
+   int e = f->error;
+   f->error = VORBIS__no_error;
+   return e;
+}
+
+static stb_vorbis * vorbis_alloc(stb_vorbis *f)
+{
+   stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p));
+   return p;
+}
+
+#ifndef STB_VORBIS_NO_PUSHDATA_API
+
+void stb_vorbis_flush_pushdata(stb_vorbis *f)
+{
+   f->previous_length = 0;
+   f->page_crc_tests  = 0;
+   f->discard_samples_deferred = 0;
+   f->current_loc_valid = FALSE;
+   f->first_decode = FALSE;
+   f->samples_output = 0;
+   f->channel_buffer_start = 0;
+   f->channel_buffer_end = 0;
+}
+
+static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len)
+{
+   int i,n;
+   for (i=0; i < f->page_crc_tests; ++i)
+      f->scan[i].bytes_done = 0;
+
+   // if we have room for more scans, search for them first, because
+   // they may cause us to stop early if their header is incomplete
+   if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) {
+      if (data_len < 4) return 0;
+      data_len -= 3; // need to look for 4-byte sequence, so don't miss
+                     // one that straddles a boundary
+      for (i=0; i < data_len; ++i) {
+         if (data[i] == 0x4f) {
+            if (0==memcmp(data+i, ogg_page_header, 4)) {
+               int j,len;
+               uint32 crc;
+               // make sure we have the whole page header
+               if (i+26 >= data_len || i+27+data[i+26] >= data_len) {
+                  // only read up to this page start, so hopefully we'll
+                  // have the whole page header start next time
+                  data_len = i;
+                  break;
+               }
+               // ok, we have it all; compute the length of the page
+               len = 27 + data[i+26];
+               for (j=0; j < data[i+26]; ++j)
+                  len += data[i+27+j];
+               // scan everything up to the embedded crc (which we must 0)
+               crc = 0;
+               for (j=0; j < 22; ++j)
+                  crc = crc32_update(crc, data[i+j]);
+               // now process 4 0-bytes
+               for (   ; j < 26; ++j)
+                  crc = crc32_update(crc, 0);
+               // len is the total number of bytes we need to scan
+               n = f->page_crc_tests++;
+               f->scan[n].bytes_left = len-j;
+               f->scan[n].crc_so_far = crc;
+               f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24);
+               // if the last frame on a page is continued to the next, then
+               // we can't recover the sample_loc immediately
+               if (data[i+27+data[i+26]-1] == 255)
+                  f->scan[n].sample_loc = ~0;
+               else
+                  f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24);
+               f->scan[n].bytes_done = i+j;
+               if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT)
+                  break;
+               // keep going if we still have room for more
+            }
+         }
+      }
+   }
+
+   for (i=0; i < f->page_crc_tests;) {
+      uint32 crc;
+      int j;
+      int n = f->scan[i].bytes_done;
+      int m = f->scan[i].bytes_left;
+      if (m > data_len - n) m = data_len - n;
+      // m is the bytes to scan in the current chunk
+      crc = f->scan[i].crc_so_far;
+      for (j=0; j < m; ++j)
+         crc = crc32_update(crc, data[n+j]);
+      f->scan[i].bytes_left -= m;
+      f->scan[i].crc_so_far = crc;
+      if (f->scan[i].bytes_left == 0) {
+         // does it match?
+         if (f->scan[i].crc_so_far == f->scan[i].goal_crc) {
+            // Houston, we have page
+            data_len = n+m; // consumption amount is wherever that scan ended
+            f->page_crc_tests = -1; // drop out of page scan mode
+            f->previous_length = 0; // decode-but-don't-output one frame
+            f->next_seg = -1;       // start a new page
+            f->current_loc = f->scan[i].sample_loc; // set the current sample location
+                                    // to the amount we'd have decoded had we decoded this page
+            f->current_loc_valid = f->current_loc != ~0U;
+            return data_len;
+         }
+         // delete entry
+         f->scan[i] = f->scan[--f->page_crc_tests];
+      } else {
+         ++i;
+      }
+   }
+
+   return data_len;
+}
+
+// return value: number of bytes we used
+int stb_vorbis_decode_frame_pushdata(
+         stb_vorbis *f,                   // the file we're decoding
+         const uint8 *data, int data_len, // the memory available for decoding
+         int *channels,                   // place to write number of float * buffers
+         float ***output,                 // place to write float ** array of float * buffers
+         int *samples                     // place to write number of output samples
+     )
+{
+   int i;
+   int len,right,left;
+
+   if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
+
+   if (f->page_crc_tests >= 0) {
+      *samples = 0;
+      return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len);
+   }
+
+   f->stream     = (uint8 *) data;
+   f->stream_end = (uint8 *) data + data_len;
+   f->error      = VORBIS__no_error;
+
+   // check that we have the entire packet in memory
+   if (!is_whole_packet_present(f)) {
+      *samples = 0;
+      return 0;
+   }
+
+   if (!vorbis_decode_packet(f, &len, &left, &right)) {
+      // save the actual error we encountered
+      enum STBVorbisError error = f->error;
+      if (error == VORBIS_bad_packet_type) {
+         // flush and resynch
+         f->error = VORBIS__no_error;
+         while (get8_packet(f) != EOP)
+            if (f->eof) break;
+         *samples = 0;
+         return (int) (f->stream - data);
+      }
+      if (error == VORBIS_continued_packet_flag_invalid) {
+         if (f->previous_length == 0) {
+            // we may be resynching, in which case it's ok to hit one
+            // of these; just discard the packet
+            f->error = VORBIS__no_error;
+            while (get8_packet(f) != EOP)
+               if (f->eof) break;
+            *samples = 0;
+            return (int) (f->stream - data);
+         }
+      }
+      // if we get an error while parsing, what to do?
+      // well, it DEFINITELY won't work to continue from where we are!
+      stb_vorbis_flush_pushdata(f);
+      // restore the error that actually made us bail
+      f->error = error;
+      *samples = 0;
+      return 1;
+   }
+
+   // success!
+   len = vorbis_finish_frame(f, len, left, right);
+   for (i=0; i < f->channels; ++i)
+      f->outputs[i] = f->channel_buffers[i] + left;
+
+   if (channels) *channels = f->channels;
+   *samples = len;
+   *output = f->outputs;
+   return (int) (f->stream - data);
+}
+
+stb_vorbis *stb_vorbis_open_pushdata(
+         const unsigned char *data, int data_len, // the memory available for decoding
+         int *data_used,              // only defined if result is not NULL
+         int *error, const stb_vorbis_alloc *alloc)
+{
+   stb_vorbis *f, p;
+   vorbis_init(&p, alloc);
+   p.stream     = (uint8 *) data;
+   p.stream_end = (uint8 *) data + data_len;
+   p.push_mode  = TRUE;
+   if (!start_decoder(&p)) {
+      if (p.eof)
+         *error = VORBIS_need_more_data;
+      else
+         *error = p.error;
+      return NULL;
+   }
+   f = vorbis_alloc(&p);
+   if (f) {
+      *f = p;
+      *data_used = (int) (f->stream - data);
+      *error = 0;
+      return f;
+   } else {
+      vorbis_deinit(&p);
+      return NULL;
+   }
+}
+#endif // STB_VORBIS_NO_PUSHDATA_API
+
+unsigned int stb_vorbis_get_file_offset(stb_vorbis *f)
+{
+   #ifndef STB_VORBIS_NO_PUSHDATA_API
+   if (f->push_mode) return 0;
+   #endif
+   if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start);
+   #ifndef STB_VORBIS_NO_STDIO
+   return (unsigned int) (ftell(f->f) - f->f_start);
+   #endif
+}
+
+#ifndef STB_VORBIS_NO_PULLDATA_API
+//
+// DATA-PULLING API
+//
+
+static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last)
+{
+   for(;;) {
+      int n;
+      if (f->eof) return 0;
+      n = get8(f);
+      if (n == 0x4f) { // page header candidate
+         unsigned int retry_loc = stb_vorbis_get_file_offset(f);
+         int i;
+         // check if we're off the end of a file_section stream
+         if (retry_loc - 25 > f->stream_len)
+            return 0;
+         // check the rest of the header
+         for (i=1; i < 4; ++i)
+            if (get8(f) != ogg_page_header[i])
+               break;
+         if (f->eof) return 0;
+         if (i == 4) {
+            uint8 header[27];
+            uint32 i, crc, goal, len;
+            for (i=0; i < 4; ++i)
+               header[i] = ogg_page_header[i];
+            for (; i < 27; ++i)
+               header[i] = get8(f);
+            if (f->eof) return 0;
+            if (header[4] != 0) goto invalid;
+            goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24);
+            for (i=22; i < 26; ++i)
+               header[i] = 0;
+            crc = 0;
+            for (i=0; i < 27; ++i)
+               crc = crc32_update(crc, header[i]);
+            len = 0;
+            for (i=0; i < header[26]; ++i) {
+               int s = get8(f);
+               crc = crc32_update(crc, s);
+               len += s;
+            }
+            if (len && f->eof) return 0;
+            for (i=0; i < len; ++i)
+               crc = crc32_update(crc, get8(f));
+            // finished parsing probable page
+            if (crc == goal) {
+               // we could now check that it's either got the last
+               // page flag set, OR it's followed by the capture
+               // pattern, but I guess TECHNICALLY you could have
+               // a file with garbage between each ogg page and recover
+               // from it automatically? So even though that paranoia
+               // might decrease the chance of an invalid decode by
+               // another 2^32, not worth it since it would hose those
+               // invalid-but-useful files?
+               if (end)
+                  *end = stb_vorbis_get_file_offset(f);
+               if (last) {
+                  if (header[5] & 0x04)
+                     *last = 1;
+                  else
+                     *last = 0;
+               }
+               set_file_offset(f, retry_loc-1);
+               return 1;
+            }
+         }
+        invalid:
+         // not a valid page, so rewind and look for next one
+         set_file_offset(f, retry_loc);
+      }
+   }
+}
+
+
+#define SAMPLE_unknown  0xffffffff
+
+// seeking is implemented with a binary search, which narrows down the range to
+// 64K, before using a linear search (because finding the synchronization
+// pattern can be expensive, and the chance we'd find the end page again is
+// relatively high for small ranges)
+//
+// two initial interpolation-style probes are used at the start of the search
+// to try to bound either side of the binary search sensibly, while still
+// working in O(log n) time if they fail.
+
+static int get_seek_page_info(stb_vorbis *f, ProbedPage *z)
+{
+   uint8 header[27], lacing[255];
+   int i,len;
+
+   // record where the page starts
+   z->page_start = stb_vorbis_get_file_offset(f);
+
+   // parse the header
+   getn(f, header, 27);
+   if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S')
+      return 0;
+   getn(f, lacing, header[26]);
+
+   // determine the length of the payload
+   len = 0;
+   for (i=0; i < header[26]; ++i)
+      len += lacing[i];
+
+   // this implies where the page ends
+   z->page_end = z->page_start + 27 + header[26] + len;
+
+   // read the last-decoded sample out of the data
+   z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24);
+
+   // restore file state to where we were
+   set_file_offset(f, z->page_start);
+   return 1;
+}
+
+// rarely used function to seek back to the preceding page while finding the
+// start of a packet
+static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset)
+{
+   unsigned int previous_safe, end;
+
+   // now we want to seek back 64K from the limit
+   if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset)
+      previous_safe = limit_offset - 65536;
+   else
+      previous_safe = f->first_audio_page_offset;
+
+   set_file_offset(f, previous_safe);
+
+   while (vorbis_find_page(f, &end, NULL)) {
+      if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset)
+         return 1;
+      set_file_offset(f, end);
+   }
+
+   return 0;
+}
+
+// implements the search logic for finding a page and starting decoding. if
+// the function succeeds, current_loc_valid will be true and current_loc will
+// be less than or equal to the provided sample number (the closer the
+// better).
+static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number)
+{
+   ProbedPage left, right, mid;
+   int i, start_seg_with_known_loc, end_pos, page_start;
+   uint32 delta, stream_length, padding, last_sample_limit;
+   double offset = 0.0, bytes_per_sample = 0.0;
+   int probe = 0;
+
+   // find the last page and validate the target sample
+   stream_length = stb_vorbis_stream_length_in_samples(f);
+   if (stream_length == 0)            return error(f, VORBIS_seek_without_length);
+   if (sample_number > stream_length) return error(f, VORBIS_seek_invalid);
+
+   // this is the maximum difference between the window-center (which is the
+   // actual granule position value), and the right-start (which the spec
+   // indicates should be the granule position (give or take one)).
+   padding = ((f->blocksize_1 - f->blocksize_0) >> 2);
+   if (sample_number < padding)
+      last_sample_limit = 0;
+   else
+      last_sample_limit = sample_number - padding;
+
+   left = f->p_first;
+   while (left.last_decoded_sample == ~0U) {
+      // (untested) the first page does not have a 'last_decoded_sample'
+      set_file_offset(f, left.page_end);
+      if (!get_seek_page_info(f, &left)) goto error;
+   }
+
+   right = f->p_last;
+   assert(right.last_decoded_sample != ~0U);
+
+   // starting from the start is handled differently
+   if (last_sample_limit <= left.last_decoded_sample) {
+      if (stb_vorbis_seek_start(f)) {
+         if (f->current_loc > sample_number)
+            return error(f, VORBIS_seek_failed);
+         return 1;
+      }
+      return 0;
+   }
+
+   while (left.page_end != right.page_start) {
+      assert(left.page_end < right.page_start);
+      // search range in bytes
+      delta = right.page_start - left.page_end;
+      if (delta <= 65536) {
+         // there's only 64K left to search - handle it linearly
+         set_file_offset(f, left.page_end);
+      } else {
+         if (probe < 2) {
+            if (probe == 0) {
+               // first probe (interpolate)
+               double data_bytes = right.page_end - left.page_start;
+               bytes_per_sample = data_bytes / right.last_decoded_sample;
+               offset = left.page_start + bytes_per_sample * (last_sample_limit - left.last_decoded_sample);
+            } else {
+               // second probe (try to bound the other side)
+               double error = ((double) last_sample_limit - mid.last_decoded_sample) * bytes_per_sample;
+               if (error >= 0 && error <  8000) error =  8000;
+               if (error <  0 && error > -8000) error = -8000;
+               offset += error * 2;
+            }
+
+            // ensure the offset is valid
+            if (offset < left.page_end)
+               offset = left.page_end;
+            if (offset > right.page_start - 65536)
+               offset = right.page_start - 65536;
+
+            set_file_offset(f, (unsigned int) offset);
+         } else {
+            // binary search for large ranges (offset by 32K to ensure
+            // we don't hit the right page)
+            set_file_offset(f, left.page_end + (delta / 2) - 32768);
+         }
+
+         if (!vorbis_find_page(f, NULL, NULL)) goto error;
+      }
+
+      for (;;) {
+         if (!get_seek_page_info(f, &mid)) goto error;
+         if (mid.last_decoded_sample != ~0U) break;
+         // (untested) no frames end on this page
+         set_file_offset(f, mid.page_end);
+         assert(mid.page_start < right.page_start);
+      }
+
+      // if we've just found the last page again then we're in a tricky file,
+      // and we're close enough (if it wasn't an interpolation probe).
+      if (mid.page_start == right.page_start) {
+         if (probe >= 2 || delta <= 65536)
+            break;
+      } else {
+         if (last_sample_limit < mid.last_decoded_sample)
+            right = mid;
+         else
+            left = mid;
+      }
+
+      ++probe;
+   }
+
+   // seek back to start of the last packet
+   page_start = left.page_start;
+   set_file_offset(f, page_start);
+   if (!start_page(f)) return error(f, VORBIS_seek_failed);
+   end_pos = f->end_seg_with_known_loc;
+   assert(end_pos >= 0);
+
+   for (;;) {
+      for (i = end_pos; i > 0; --i)
+         if (f->segments[i-1] != 255)
+            break;
+
+      start_seg_with_known_loc = i;
+
+      if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet))
+         break;
+
+      // (untested) the final packet begins on an earlier page
+      if (!go_to_page_before(f, page_start))
+         goto error;
+
+      page_start = stb_vorbis_get_file_offset(f);
+      if (!start_page(f)) goto error;
+      end_pos = f->segment_count - 1;
+   }
+
+   // prepare to start decoding
+   f->current_loc_valid = FALSE;
+   f->last_seg = FALSE;
+   f->valid_bits = 0;
+   f->packet_bytes = 0;
+   f->bytes_in_seg = 0;
+   f->previous_length = 0;
+   f->next_seg = start_seg_with_known_loc;
+
+   for (i = 0; i < start_seg_with_known_loc; i++)
+      skip(f, f->segments[i]);
+
+   // start decoding (optimizable - this frame is generally discarded)
+   if (!vorbis_pump_first_frame(f))
+      return 0;
+   if (f->current_loc > sample_number)
+      return error(f, VORBIS_seek_failed);
+   return 1;
+
+error:
+   // try to restore the file to a valid state
+   stb_vorbis_seek_start(f);
+   return error(f, VORBIS_seek_failed);
+}
+
+// the same as vorbis_decode_initial, but without advancing
+static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode)
+{
+   int bits_read, bytes_read;
+
+   if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode))
+      return 0;
+
+   // either 1 or 2 bytes were read, figure out which so we can rewind
+   bits_read = 1 + ilog(f->mode_count-1);
+   if (f->mode_config[*mode].blockflag)
+      bits_read += 2;
+   bytes_read = (bits_read + 7) / 8;
+
+   f->bytes_in_seg += bytes_read;
+   f->packet_bytes -= bytes_read;
+   skip(f, -bytes_read);
+   if (f->next_seg == -1)
+      f->next_seg = f->segment_count - 1;
+   else
+      f->next_seg--;
+   f->valid_bits = 0;
+
+   return 1;
+}
+
+int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number)
+{
+   uint32 max_frame_samples;
+
+   if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
+
+   // fast page-level search
+   if (!seek_to_sample_coarse(f, sample_number))
+      return 0;
+
+   assert(f->current_loc_valid);
+   assert(f->current_loc <= sample_number);
+
+   // linear search for the relevant packet
+   max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2;
+   while (f->current_loc < sample_number) {
+      int left_start, left_end, right_start, right_end, mode, frame_samples;
+      if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode))
+         return error(f, VORBIS_seek_failed);
+      // calculate the number of samples returned by the next frame
+      frame_samples = right_start - left_start;
+      if (f->current_loc + frame_samples > sample_number) {
+         return 1; // the next frame will contain the sample
+      } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) {
+         // there's a chance the frame after this could contain the sample
+         vorbis_pump_first_frame(f);
+      } else {
+         // this frame is too early to be relevant
+         f->current_loc += frame_samples;
+         f->previous_length = 0;
+         maybe_start_packet(f);
+         flush_packet(f);
+      }
+   }
+   // the next frame should start with the sample
+   if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed);
+   return 1;
+}
+
+int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number)
+{
+   if (!stb_vorbis_seek_frame(f, sample_number))
+      return 0;
+
+   if (sample_number != f->current_loc) {
+      int n;
+      uint32 frame_start = f->current_loc;
+      stb_vorbis_get_frame_float(f, &n, NULL);
+      assert(sample_number > frame_start);
+      assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end);
+      f->channel_buffer_start += (sample_number - frame_start);
+   }
+
+   return 1;
+}
+
+int stb_vorbis_seek_start(stb_vorbis *f)
+{
+   if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); }
+   set_file_offset(f, f->first_audio_page_offset);
+   f->previous_length = 0;
+   f->first_decode = TRUE;
+   f->next_seg = -1;
+   return vorbis_pump_first_frame(f);
+}
+
+unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f)
+{
+   unsigned int restore_offset, previous_safe;
+   unsigned int end, last_page_loc;
+
+   if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
+   if (!f->total_samples) {
+      unsigned int last;
+      uint32 lo,hi;
+      char header[6];
+
+      // first, store the current decode position so we can restore it
+      restore_offset = stb_vorbis_get_file_offset(f);
+
+      // now we want to seek back 64K from the end (the last page must
+      // be at most a little less than 64K, but let's allow a little slop)
+      if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset)
+         previous_safe = f->stream_len - 65536;
+      else
+         previous_safe = f->first_audio_page_offset;
+
+      set_file_offset(f, previous_safe);
+      // previous_safe is now our candidate 'earliest known place that seeking
+      // to will lead to the final page'
+
+      if (!vorbis_find_page(f, &end, &last)) {
+         // if we can't find a page, we're hosed!
+         f->error = VORBIS_cant_find_last_page;
+         f->total_samples = 0xffffffff;
+         goto done;
+      }
+
+      // check if there are more pages
+      last_page_loc = stb_vorbis_get_file_offset(f);
+
+      // stop when the last_page flag is set, not when we reach eof;
+      // this allows us to stop short of a 'file_section' end without
+      // explicitly checking the length of the section
+      while (!last) {
+         set_file_offset(f, end);
+         if (!vorbis_find_page(f, &end, &last)) {
+            // the last page we found didn't have the 'last page' flag
+            // set. whoops!
+            break;
+         }
+         previous_safe = last_page_loc+1;
+         last_page_loc = stb_vorbis_get_file_offset(f);
+      }
+
+      set_file_offset(f, last_page_loc);
+
+      // parse the header
+      getn(f, (unsigned char *)header, 6);
+      // extract the absolute granule position
+      lo = get32(f);
+      hi = get32(f);
+      if (lo == 0xffffffff && hi == 0xffffffff) {
+         f->error = VORBIS_cant_find_last_page;
+         f->total_samples = SAMPLE_unknown;
+         goto done;
+      }
+      if (hi)
+         lo = 0xfffffffe; // saturate
+      f->total_samples = lo;
+
+      f->p_last.page_start = last_page_loc;
+      f->p_last.page_end   = end;
+      f->p_last.last_decoded_sample = lo;
+
+     done:
+      set_file_offset(f, restore_offset);
+   }
+   return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples;
+}
+
+float stb_vorbis_stream_length_in_seconds(stb_vorbis *f)
+{
+   return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate;
+}
+
+
+
+int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output)
+{
+   int len, right,left,i;
+   if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
+
+   if (!vorbis_decode_packet(f, &len, &left, &right)) {
+      f->channel_buffer_start = f->channel_buffer_end = 0;
+      return 0;
+   }
+
+   len = vorbis_finish_frame(f, len, left, right);
+   for (i=0; i < f->channels; ++i)
+      f->outputs[i] = f->channel_buffers[i] + left;
+
+   f->channel_buffer_start = left;
+   f->channel_buffer_end   = left+len;
+
+   if (channels) *channels = f->channels;
+   if (output)   *output = f->outputs;
+   return len;
+}
+
+#ifndef STB_VORBIS_NO_STDIO
+
+stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length)
+{
+   stb_vorbis *f, p;
+   vorbis_init(&p, alloc);
+   p.f = file;
+   p.f_start = (uint32) ftell(file);
+   p.stream_len   = length;
+   p.close_on_free = close_on_free;
+   if (start_decoder(&p)) {
+      f = vorbis_alloc(&p);
+      if (f) {
+         *f = p;
+         vorbis_pump_first_frame(f);
+         return f;
+      }
+   }
+   if (error) *error = p.error;
+   vorbis_deinit(&p);
+   return NULL;
+}
+
+stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc)
+{
+   unsigned int len, start;
+   start = (unsigned int) ftell(file);
+   fseek(file, 0, SEEK_END);
+   len = (unsigned int) (ftell(file) - start);
+   fseek(file, start, SEEK_SET);
+   return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len);
+}
+
+stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc)
+{
+   FILE *f;
+#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)
+   if (0 != fopen_s(&f, filename, "rb"))
+      f = NULL;
+#else
+   f = fopen(filename, "rb");
+#endif
+   if (f)
+      return stb_vorbis_open_file(f, TRUE, error, alloc);
+   if (error) *error = VORBIS_file_open_failure;
+   return NULL;
+}
+#endif // STB_VORBIS_NO_STDIO
+
+stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc)
+{
+   stb_vorbis *f, p;
+   if (data == NULL) return NULL;
+   vorbis_init(&p, alloc);
+   p.stream = (uint8 *) data;
+   p.stream_end = (uint8 *) data + len;
+   p.stream_start = (uint8 *) p.stream;
+   p.stream_len = len;
+   p.push_mode = FALSE;
+   if (start_decoder(&p)) {
+      f = vorbis_alloc(&p);
+      if (f) {
+         *f = p;
+         vorbis_pump_first_frame(f);
+         if (error) *error = VORBIS__no_error;
+         return f;
+      }
+   }
+   if (error) *error = p.error;
+   vorbis_deinit(&p);
+   return NULL;
+}
+
+#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
+#define PLAYBACK_MONO     1
+#define PLAYBACK_LEFT     2
+#define PLAYBACK_RIGHT    4
+
+#define L  (PLAYBACK_LEFT  | PLAYBACK_MONO)
+#define C  (PLAYBACK_LEFT  | PLAYBACK_RIGHT | PLAYBACK_MONO)
+#define R  (PLAYBACK_RIGHT | PLAYBACK_MONO)
+
+static int8 channel_position[7][6] =
+{
+   { 0 },
+   { C },
+   { L, R },
+   { L, C, R },
+   { L, R, L, R },
+   { L, C, R, L, R },
+   { L, C, R, L, R, C },
+};
+
+
+#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT
+   typedef union {
+      float f;
+      int i;
+   } float_conv;
+   typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4];
+   #define FASTDEF(x) float_conv x
+   // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round
+   #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT))
+   #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22))
+   #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s))
+   #define check_endianness()
+#else
+   #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s))))
+   #define check_endianness()
+   #define FASTDEF(x)
+#endif
+
+static void copy_samples(short *dest, float *src, int len)
+{
+   int i;
+   check_endianness();
+   for (i=0; i < len; ++i) {
+      FASTDEF(temp);
+      int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15);
+      if ((unsigned int) (v + 32768) > 65535)
+         v = v < 0 ? -32768 : 32767;
+      dest[i] = v;
+   }
+}
+
+static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len)
+{
+   #define BUFFER_SIZE  32
+   float buffer[BUFFER_SIZE];
+   int i,j,o,n = BUFFER_SIZE;
+   check_endianness();
+   for (o = 0; o < len; o += BUFFER_SIZE) {
+      memset(buffer, 0, sizeof(buffer));
+      if (o + n > len) n = len - o;
+      for (j=0; j < num_c; ++j) {
+         if (channel_position[num_c][j] & mask) {
+            for (i=0; i < n; ++i)
+               buffer[i] += data[j][d_offset+o+i];
+         }
+      }
+      for (i=0; i < n; ++i) {
+         FASTDEF(temp);
+         int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15);
+         if ((unsigned int) (v + 32768) > 65535)
+            v = v < 0 ? -32768 : 32767;
+         output[o+i] = v;
+      }
+   }
+}
+
+static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len)
+{
+   #define BUFFER_SIZE  32
+   float buffer[BUFFER_SIZE];
+   int i,j,o,n = BUFFER_SIZE >> 1;
+   // o is the offset in the source data
+   check_endianness();
+   for (o = 0; o < len; o += BUFFER_SIZE >> 1) {
+      // o2 is the offset in the output data
+      int o2 = o << 1;
+      memset(buffer, 0, sizeof(buffer));
+      if (o + n > len) n = len - o;
+      for (j=0; j < num_c; ++j) {
+         int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT);
+         if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) {
+            for (i=0; i < n; ++i) {
+               buffer[i*2+0] += data[j][d_offset+o+i];
+               buffer[i*2+1] += data[j][d_offset+o+i];
+            }
+         } else if (m == PLAYBACK_LEFT) {
+            for (i=0; i < n; ++i) {
+               buffer[i*2+0] += data[j][d_offset+o+i];
+            }
+         } else if (m == PLAYBACK_RIGHT) {
+            for (i=0; i < n; ++i) {
+               buffer[i*2+1] += data[j][d_offset+o+i];
+            }
+         }
+      }
+      for (i=0; i < (n<<1); ++i) {
+         FASTDEF(temp);
+         int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15);
+         if ((unsigned int) (v + 32768) > 65535)
+            v = v < 0 ? -32768 : 32767;
+         output[o2+i] = v;
+      }
+   }
+}
+
+static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples)
+{
+   int i;
+   if (buf_c != data_c && buf_c <= 2 && data_c <= 6) {
+      static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} };
+      for (i=0; i < buf_c; ++i)
+         compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples);
+   } else {
+      int limit = buf_c < data_c ? buf_c : data_c;
+      for (i=0; i < limit; ++i)
+         copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples);
+      for (   ; i < buf_c; ++i)
+         memset(buffer[i]+b_offset, 0, sizeof(short) * samples);
+   }
+}
+
+int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples)
+{
+   float **output = NULL;
+   int len = stb_vorbis_get_frame_float(f, NULL, &output);
+   if (len > num_samples) len = num_samples;
+   if (len)
+      convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len);
+   return len;
+}
+
+static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len)
+{
+   int i;
+   check_endianness();
+   if (buf_c != data_c && buf_c <= 2 && data_c <= 6) {
+      assert(buf_c == 2);
+      for (i=0; i < buf_c; ++i)
+         compute_stereo_samples(buffer, data_c, data, d_offset, len);
+   } else {
+      int limit = buf_c < data_c ? buf_c : data_c;
+      int j;
+      for (j=0; j < len; ++j) {
+         for (i=0; i < limit; ++i) {
+            FASTDEF(temp);
+            float f = data[i][d_offset+j];
+            int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15);
+            if ((unsigned int) (v + 32768) > 65535)
+               v = v < 0 ? -32768 : 32767;
+            *buffer++ = v;
+         }
+         for (   ; i < buf_c; ++i)
+            *buffer++ = 0;
+      }
+   }
+}
+
+int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts)
+{
+   float **output;
+   int len;
+   if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts);
+   len = stb_vorbis_get_frame_float(f, NULL, &output);
+   if (len) {
+      if (len*num_c > num_shorts) len = num_shorts / num_c;
+      convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len);
+   }
+   return len;
+}
+
+int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts)
+{
+   float **outputs;
+   int len = num_shorts / channels;
+   int n=0;
+   int z = f->channels;
+   if (z > channels) z = channels;
+   while (n < len) {
+      int k = f->channel_buffer_end - f->channel_buffer_start;
+      if (n+k >= len) k = len - n;
+      if (k)
+         convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k);
+      buffer += k*channels;
+      n += k;
+      f->channel_buffer_start += k;
+      if (n == len) break;
+      if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break;
+   }
+   return n;
+}
+
+int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len)
+{
+   float **outputs;
+   int n=0;
+   int z = f->channels;
+   if (z > channels) z = channels;
+   while (n < len) {
+      int k = f->channel_buffer_end - f->channel_buffer_start;
+      if (n+k >= len) k = len - n;
+      if (k)
+         convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k);
+      n += k;
+      f->channel_buffer_start += k;
+      if (n == len) break;
+      if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break;
+   }
+   return n;
+}
+
+#ifndef STB_VORBIS_NO_STDIO
+int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output)
+{
+   int data_len, offset, total, limit, error;
+   short *data;
+   stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL);
+   if (v == NULL) return -1;
+   limit = v->channels * 4096;
+   *channels = v->channels;
+   if (sample_rate)
+      *sample_rate = v->sample_rate;
+   offset = data_len = 0;
+   total = limit;
+   data = (short *) malloc(total * sizeof(*data));
+   if (data == NULL) {
+      stb_vorbis_close(v);
+      return -2;
+   }
+   for (;;) {
+      int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset);
+      if (n == 0) break;
+      data_len += n;
+      offset += n * v->channels;
+      if (offset + limit > total) {
+         short *data2;
+         total *= 2;
+         data2 = (short *) realloc(data, total * sizeof(*data));
+         if (data2 == NULL) {
+            free(data);
+            stb_vorbis_close(v);
+            return -2;
+         }
+         data = data2;
+      }
+   }
+   *output = data;
+   stb_vorbis_close(v);
+   return data_len;
+}
+#endif // NO_STDIO
+
+int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output)
+{
+   int data_len, offset, total, limit, error;
+   short *data;
+   stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL);
+   if (v == NULL) return -1;
+   limit = v->channels * 4096;
+   *channels = v->channels;
+   if (sample_rate)
+      *sample_rate = v->sample_rate;
+   offset = data_len = 0;
+   total = limit;
+   data = (short *) malloc(total * sizeof(*data));
+   if (data == NULL) {
+      stb_vorbis_close(v);
+      return -2;
+   }
+   for (;;) {
+      int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset);
+      if (n == 0) break;
+      data_len += n;
+      offset += n * v->channels;
+      if (offset + limit > total) {
+         short *data2;
+         total *= 2;
+         data2 = (short *) realloc(data, total * sizeof(*data));
+         if (data2 == NULL) {
+            free(data);
+            stb_vorbis_close(v);
+            return -2;
+         }
+         data = data2;
+      }
+   }
+   *output = data;
+   stb_vorbis_close(v);
+   return data_len;
+}
+#endif // STB_VORBIS_NO_INTEGER_CONVERSION
+
+int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats)
+{
+   float **outputs;
+   int len = num_floats / channels;
+   int n=0;
+   int z = f->channels;
+   if (z > channels) z = channels;
+   while (n < len) {
+      int i,j;
+      int k = f->channel_buffer_end - f->channel_buffer_start;
+      if (n+k >= len) k = len - n;
+      for (j=0; j < k; ++j) {
+         for (i=0; i < z; ++i)
+            *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j];
+         for (   ; i < channels; ++i)
+            *buffer++ = 0;
+      }
+      n += k;
+      f->channel_buffer_start += k;
+      if (n == len)
+         break;
+      if (!stb_vorbis_get_frame_float(f, NULL, &outputs))
+         break;
+   }
+   return n;
+}
+
+int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples)
+{
+   float **outputs;
+   int n=0;
+   int z = f->channels;
+   if (z > channels) z = channels;
+   while (n < num_samples) {
+      int i;
+      int k = f->channel_buffer_end - f->channel_buffer_start;
+      if (n+k >= num_samples) k = num_samples - n;
+      if (k) {
+         for (i=0; i < z; ++i)
+            memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k);
+         for (   ; i < channels; ++i)
+            memset(buffer[i]+n, 0, sizeof(float) * k);
+      }
+      n += k;
+      f->channel_buffer_start += k;
+      if (n == num_samples)
+         break;
+      if (!stb_vorbis_get_frame_float(f, NULL, &outputs))
+         break;
+   }
+   return n;
+}
+#endif // STB_VORBIS_NO_PULLDATA_API
+
+/* Version history
+    1.17    - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223
+                           found with Mayhem by ForAllSecure
+    1.16    - 2019-03-04 - fix warnings
+    1.15    - 2019-02-07 - explicit failure if Ogg Skeleton data is found
+    1.14    - 2018-02-11 - delete bogus dealloca usage
+    1.13    - 2018-01-29 - fix truncation of last frame (hopefully)
+    1.12    - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files
+    1.11    - 2017-07-23 - fix MinGW compilation
+    1.10    - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory
+    1.09    - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version
+    1.08    - 2016-04-02 - fixed multiple warnings; fix setup memory leaks;
+                           avoid discarding last frame of audio data
+    1.07    - 2015-01-16 - fixed some warnings, fix mingw, const-correct API
+                           some more crash fixes when out of memory or with corrupt files
+    1.06    - 2015-08-31 - full, correct support for seeking API (Dougall Johnson)
+                           some crash fixes when out of memory or with corrupt files
+    1.05    - 2015-04-19 - don't define __forceinline if it's redundant
+    1.04    - 2014-08-27 - fix missing const-correct case in API
+    1.03    - 2014-08-07 - Warning fixes
+    1.02    - 2014-07-09 - Declare qsort compare function _cdecl on windows
+    1.01    - 2014-06-18 - fix stb_vorbis_get_samples_float
+    1.0     - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel
+                           (API change) report sample rate for decode-full-file funcs
+    0.99996 - bracket #include <malloc.h> for macintosh compilation by Laurent Gomila
+    0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem
+    0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence
+    0.99993 - remove assert that fired on legal files with empty tables
+    0.99992 - rewind-to-start
+    0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo
+    0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++
+    0.9998 - add a full-decode function with a memory source
+    0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition
+    0.9996 - query length of vorbis stream in samples/seconds
+    0.9995 - bugfix to another optimization that only happened in certain files
+    0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors
+    0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation
+    0.9992 - performance improvement of IMDCT; now performs close to reference implementation
+    0.9991 - performance improvement of IMDCT
+    0.999 - (should have been 0.9990) performance improvement of IMDCT
+    0.998 - no-CRT support from Casey Muratori
+    0.997 - bugfixes for bugs found by Terje Mathisen
+    0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen
+    0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen
+    0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen
+    0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen
+    0.992 - fixes for MinGW warning
+    0.991 - turn fast-float-conversion on by default
+    0.990 - fix push-mode seek recovery if you seek into the headers
+    0.98b - fix to bad release of 0.98
+    0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode
+    0.97 - builds under c++ (typecasting, don't use 'class' keyword)
+    0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code
+    0.95 - clamping code for 16-bit functions
+    0.94 - not publically released
+    0.93 - fixed all-zero-floor case (was decoding garbage)
+    0.92 - fixed a memory leak
+    0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION
+    0.90 - first public release
+*/
+
+#endif // STB_VORBIS_HEADER_ONLY
+
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/
-- 
GitLab